line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2016-2020 -- leonerd@leonerd.org.uk |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Net::Prometheus; |
7
|
|
|
|
|
|
|
|
8
|
7
|
|
|
7
|
|
415642
|
use strict; |
|
7
|
|
|
|
|
68
|
|
|
7
|
|
|
|
|
205
|
|
9
|
7
|
|
|
7
|
|
37
|
use warnings; |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
258
|
|
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
our $VERSION = '0.10'; |
12
|
|
|
|
|
|
|
|
13
|
7
|
|
|
7
|
|
37
|
use Carp; |
|
7
|
|
|
|
|
13
|
|
|
7
|
|
|
|
|
455
|
|
14
|
|
|
|
|
|
|
|
15
|
7
|
|
|
7
|
|
42
|
use List::Util 1.29 qw( pairmap ); |
|
7
|
|
|
|
|
188
|
|
|
7
|
|
|
|
|
737
|
|
16
|
|
|
|
|
|
|
|
17
|
7
|
|
|
7
|
|
2875
|
use Net::Prometheus::Gauge; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
233
|
|
18
|
7
|
|
|
7
|
|
2958
|
use Net::Prometheus::Counter; |
|
7
|
|
|
|
|
20
|
|
|
7
|
|
|
|
|
223
|
|
19
|
7
|
|
|
7
|
|
2835
|
use Net::Prometheus::Summary; |
|
7
|
|
|
|
|
21
|
|
|
7
|
|
|
|
|
176
|
|
20
|
7
|
|
|
7
|
|
2915
|
use Net::Prometheus::Histogram; |
|
7
|
|
|
|
|
16
|
|
|
7
|
|
|
|
|
206
|
|
21
|
|
|
|
|
|
|
|
22
|
7
|
|
|
7
|
|
2829
|
use Net::Prometheus::Registry; |
|
7
|
|
|
|
|
19
|
|
|
7
|
|
|
|
|
206
|
|
23
|
|
|
|
|
|
|
|
24
|
7
|
|
|
7
|
|
2824
|
use Net::Prometheus::ProcessCollector; |
|
7
|
|
|
|
|
22
|
|
|
7
|
|
|
|
|
231
|
|
25
|
|
|
|
|
|
|
|
26
|
7
|
|
|
7
|
|
43
|
use Net::Prometheus::Types qw( MetricSamples ); |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
367
|
|
27
|
|
|
|
|
|
|
|
28
|
7
|
|
|
7
|
|
41
|
use constant HAVE_PERLCOLLECTOR => defined eval { require Net::Prometheus::PerlCollector }; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
13020
|
|
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 NAME |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
C - export monitoring metrics for F |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 SYNOPSIS |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
use Net::Prometheus; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
my $client = Net::Prometheus->new; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
my $counter = $client->new_counter( |
41
|
|
|
|
|
|
|
name => "requests", |
42
|
|
|
|
|
|
|
help => "Number of received requests", |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub handle_request |
46
|
|
|
|
|
|
|
{ |
47
|
|
|
|
|
|
|
$counter->inc; |
48
|
|
|
|
|
|
|
... |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
use Plack::Builder; |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
builder { |
54
|
|
|
|
|
|
|
mount "/metrics" => $client->psgi_app; |
55
|
|
|
|
|
|
|
... |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=head1 DESCRIPTION |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
This module provides the ability for a program to collect monitoring metrics |
61
|
|
|
|
|
|
|
and export them to the F monitoring server. |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
As C will expect to collect the metrics by making an HTTP request, |
64
|
|
|
|
|
|
|
facilities are provided to yield a L application that the containing |
65
|
|
|
|
|
|
|
program can embed in its own structure to provide the results, or the |
66
|
|
|
|
|
|
|
application can generate a plain-text result directly and serve them by its |
67
|
|
|
|
|
|
|
own means. |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 Metrics::Any |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
For more flexibility of metrics reporting, other modules may wish to use |
72
|
|
|
|
|
|
|
L as an abstraction interface instead of directly using this |
73
|
|
|
|
|
|
|
API. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
By using C instead, the module does not directly depend on |
76
|
|
|
|
|
|
|
C, and in addition program ultimately using the module gets |
77
|
|
|
|
|
|
|
the flexibility to use Prometheus (via L) |
78
|
|
|
|
|
|
|
or use another reporting system via a different adapter. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=cut |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=cut |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head2 new |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$prometheus = Net::Prometheus->new; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Returns a new C instance. |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
Takes the following named arguments: |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=over |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=item disable_process_collector => BOOL |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
If present and true, this instance will not load the default process collector |
99
|
|
|
|
|
|
|
from L. If absent or false, such a |
100
|
|
|
|
|
|
|
collector will be loaded by default. |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=item disable_perl_collector => BOOL |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If present and true, this instance will not load perl-specific collector from |
105
|
|
|
|
|
|
|
L. If absent or false this collector is loaded |
106
|
|
|
|
|
|
|
if the module is installed and useable. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
These two options are provided for testing purposes, or for specific use-cases |
109
|
|
|
|
|
|
|
where such features are not required. Usually it's best just to leave these |
110
|
|
|
|
|
|
|
enabled. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=back |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=cut |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub new |
117
|
|
|
|
|
|
|
{ |
118
|
7
|
|
|
7
|
1
|
1179
|
my $class = shift; |
119
|
7
|
|
|
|
|
31
|
my %args = @_; |
120
|
|
|
|
|
|
|
|
121
|
7
|
|
|
|
|
48
|
my $self = bless { |
122
|
|
|
|
|
|
|
registry => Net::Prometheus::Registry->new, |
123
|
|
|
|
|
|
|
}, $class; |
124
|
|
|
|
|
|
|
|
125
|
7
|
100
|
66
|
|
|
54
|
if( not $args{disable_process_collector} and |
126
|
|
|
|
|
|
|
my $process_collector = Net::Prometheus::ProcessCollector->new ) { |
127
|
2
|
|
|
|
|
13
|
$self->register( $process_collector ); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
7
|
50
|
100
|
|
|
35
|
if( not $args{disable_perl_collector} and HAVE_PERLCOLLECTOR ) { |
131
|
0
|
|
|
|
|
0
|
$self->register( Net::Prometheus::PerlCollector->new ); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
7
|
|
|
|
|
27
|
return $self; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head1 METHODS |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=cut |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head2 register |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
$collector = $prometheus->register( $collector ) |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
Registers a new L to be collected from by the C |
146
|
|
|
|
|
|
|
method. The collector instance itself is returned, for convenience. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=cut |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub register |
151
|
|
|
|
|
|
|
{ |
152
|
13
|
|
|
13
|
1
|
61
|
my $self = shift; |
153
|
13
|
|
|
|
|
26
|
my ( $collector ) = @_; |
154
|
|
|
|
|
|
|
|
155
|
13
|
|
|
|
|
76
|
return $self->{registry}->register( $collector ); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head2 unregister |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
$prometheus->unregister( $collector ) |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Removes a previously-registered collector. |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=cut |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
sub unregister |
167
|
|
|
|
|
|
|
{ |
168
|
2
|
|
|
2
|
1
|
1119
|
my $self = shift; |
169
|
2
|
|
|
|
|
4
|
my ( $collector ) = @_; |
170
|
|
|
|
|
|
|
|
171
|
2
|
|
|
|
|
7
|
return $self->{registry}->unregister( $collector ); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=head2 new_gauge |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
$gauge = $prometheus->new_gauge( %args ) |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Constructs a new L using the arguments given and |
179
|
|
|
|
|
|
|
registers it with the exporter. The newly-constructed gauge is returned. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub new_gauge |
184
|
|
|
|
|
|
|
{ |
185
|
5
|
|
|
5
|
1
|
18
|
my $self = shift; |
186
|
5
|
|
|
|
|
17
|
my %args = @_; |
187
|
|
|
|
|
|
|
|
188
|
5
|
|
|
|
|
32
|
return $self->register( Net::Prometheus::Gauge->new( %args ) ); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 new_counter |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
$counter = $prometheus->new_counter( %args ) |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
Constructs a new L using the arguments given and |
196
|
|
|
|
|
|
|
registers it with the exporter. The newly-constructed counter is returned. |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=cut |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
sub new_counter |
201
|
|
|
|
|
|
|
{ |
202
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
203
|
1
|
|
|
|
|
3
|
my %args = @_; |
204
|
|
|
|
|
|
|
|
205
|
1
|
|
|
|
|
11
|
return $self->register( Net::Prometheus::Counter->new( %args ) ); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=head2 new_summary |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
$summary = $prometheus->new_summary( %args ) |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Constructs a new L using the arguments given |
213
|
|
|
|
|
|
|
and registers it with the exporter. The newly-constructed summary is returned. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=cut |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub new_summary |
218
|
|
|
|
|
|
|
{ |
219
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
220
|
0
|
|
|
|
|
0
|
my %args = @_; |
221
|
|
|
|
|
|
|
|
222
|
0
|
|
|
|
|
0
|
return $self->register( Net::Prometheus::Summary->new( %args ) ); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=head2 new_histogram |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
$histogram = $prometheus->new_histogram( %args ) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Constructs a new L using the arguments given |
230
|
|
|
|
|
|
|
and registers it with the exporter. The newly-constructed histogram is |
231
|
|
|
|
|
|
|
returned. |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=cut |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub new_histogram |
236
|
|
|
|
|
|
|
{ |
237
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
238
|
0
|
|
|
|
|
0
|
my %args = @_; |
239
|
|
|
|
|
|
|
|
240
|
0
|
|
|
|
|
0
|
return $self->register( Net::Prometheus::Histogram->new( %args ) ); |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 new_metricgroup |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
$group = $prometheus->new_metricgroup( %args ) |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Returns a new Metric Group instance as a convenience for registering multiple |
248
|
|
|
|
|
|
|
metrics using the same C and C arguments. Takes the |
249
|
|
|
|
|
|
|
following named arguments: |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=over |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=item namespace => STR |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=item subsystem => STR |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
String values to pass by default into new metrics the group will construct. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=back |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Once constructed, the group acts as a proxy to the other C methods, |
262
|
|
|
|
|
|
|
passing in these values as overrides. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
$gauge = $group->new_gauge( ... ) |
265
|
|
|
|
|
|
|
$counter = $group->new_counter( ... ) |
266
|
|
|
|
|
|
|
$summary = $group->new_summary( ... ) |
267
|
|
|
|
|
|
|
$histogram = $group->new_histogram( ... ) |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
=cut |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
sub new_metricgroup |
272
|
|
|
|
|
|
|
{ |
273
|
2
|
|
|
2
|
1
|
583
|
my $self = shift; |
274
|
2
|
|
|
|
|
7
|
my ( %args ) = @_; |
275
|
|
|
|
|
|
|
|
276
|
2
|
|
|
|
|
12
|
return Net::Prometheus::_MetricGroup->new( |
277
|
|
|
|
|
|
|
$self, %args |
278
|
|
|
|
|
|
|
); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
=head2 collect |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
@metricsamples = $prometheus->collect( $opts ) |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
Returns a list of L obtained from all |
286
|
|
|
|
|
|
|
of the currently-registered collectors. |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=cut |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub collect |
291
|
|
|
|
|
|
|
{ |
292
|
15
|
|
|
15
|
1
|
33
|
my $self = shift; |
293
|
15
|
|
|
|
|
31
|
my ( $opts ) = @_; |
294
|
|
|
|
|
|
|
|
295
|
15
|
|
100
|
|
|
76
|
$opts //= {}; |
296
|
|
|
|
|
|
|
|
297
|
15
|
|
|
|
|
27
|
my %samples_by_name; |
298
|
15
|
|
|
|
|
56
|
foreach my $collector ( $self->{registry}->collectors, Net::Prometheus::Registry->collectors ) { |
299
|
16
|
|
|
|
|
96
|
push @{ $samples_by_name{ $_->fullname } }, $_ for $collector->collect( $opts ); |
|
29
|
|
|
|
|
327
|
|
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
return map { |
303
|
15
|
|
|
|
|
221
|
my @results = @{ $samples_by_name{ $_ } }; |
|
28
|
|
|
|
|
42
|
|
|
28
|
|
|
|
|
70
|
|
304
|
28
|
|
|
|
|
42
|
my $first = $results[0]; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
@results > 1 ? |
307
|
|
|
|
|
|
|
MetricSamples( $first->fullname, $first->type, $first->help, |
308
|
28
|
100
|
|
|
|
98
|
[ map { @{ $_->samples } } @results ] |
|
2
|
|
|
|
|
20
|
|
|
2
|
|
|
|
|
6
|
|
309
|
|
|
|
|
|
|
) : |
310
|
|
|
|
|
|
|
$first; |
311
|
|
|
|
|
|
|
} sort keys %samples_by_name; |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=head2 render |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
$str = $prometheus->render |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
Returns a string in the Prometheus text exposition format containing the |
319
|
|
|
|
|
|
|
current values of all the registered metrics. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
$str = $prometheus->render( { options => "for collectors" } ) |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
An optional HASH reference may be provided; if so it will be passed into the |
324
|
|
|
|
|
|
|
C method of every registered collector. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=cut |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub _render_label_value |
329
|
|
|
|
|
|
|
{ |
330
|
8
|
|
|
8
|
|
14
|
my ( $v ) = @_; |
331
|
|
|
|
|
|
|
|
332
|
8
|
|
|
|
|
32
|
$v =~ s/(["\\])/\\$1/g; |
333
|
8
|
|
|
|
|
17
|
$v =~ s/\n/\\n/g; |
334
|
|
|
|
|
|
|
|
335
|
8
|
|
|
|
|
54
|
return qq("$v"); |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub _render_labels |
339
|
|
|
|
|
|
|
{ |
340
|
27
|
|
|
27
|
|
220
|
my ( $labels ) = @_; |
341
|
|
|
|
|
|
|
|
342
|
27
|
100
|
|
|
|
82
|
return "" if !scalar @$labels; |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
return "{" . |
345
|
7
|
|
|
8
|
|
51
|
join( ",", pairmap { $a . "=" . _render_label_value( $b ) } @$labels ) . |
|
8
|
|
|
|
|
20
|
|
346
|
|
|
|
|
|
|
"}"; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
sub render |
350
|
|
|
|
|
|
|
{ |
351
|
14
|
|
|
14
|
1
|
619
|
my $self = shift; |
352
|
14
|
|
|
|
|
29
|
my ( $opts ) = @_; |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
return join "", map { |
355
|
14
|
|
|
|
|
46
|
my $metricsamples = $_; |
|
27
|
|
|
|
|
190
|
|
356
|
|
|
|
|
|
|
|
357
|
27
|
|
|
|
|
62
|
my $fullname = $metricsamples->fullname; |
358
|
|
|
|
|
|
|
|
359
|
27
|
|
|
|
|
147
|
my $help = $metricsamples->help; |
360
|
27
|
|
|
|
|
143
|
$help =~ s/\\/\\\\/g; |
361
|
27
|
|
|
|
|
46
|
$help =~ s/\n/\\n/g; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
"# HELP $fullname $help\n", |
364
|
|
|
|
|
|
|
"# TYPE $fullname " . $metricsamples->type . "\n", |
365
|
|
|
|
|
|
|
map { |
366
|
27
|
|
|
|
|
125
|
my $sample = $_; |
367
|
27
|
|
|
|
|
60
|
sprintf "%s%s %s\n", |
368
|
|
|
|
|
|
|
$sample->varname, |
369
|
|
|
|
|
|
|
_render_labels( $sample->labels ), |
370
|
|
|
|
|
|
|
$sample->value |
371
|
27
|
|
|
|
|
100
|
} @{ $metricsamples->samples } |
|
27
|
|
|
|
|
167
|
|
372
|
|
|
|
|
|
|
} $self->collect( $opts ); |
373
|
|
|
|
|
|
|
} |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=head2 handle |
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
$response = $prometheus->handle( $request ) |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Given an HTTP request in an L instance, renders the metrics in |
380
|
|
|
|
|
|
|
response to it and returns an L instance. |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
This application will respond to any C request, and reject requests for |
383
|
|
|
|
|
|
|
any other method. If a query string is present on the URI it will be parsed |
384
|
|
|
|
|
|
|
for collector options to pass into the L method. |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
This method is useful for integrating metrics into an existing HTTP server |
387
|
|
|
|
|
|
|
application which uses these objects. For example: |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
my $prometheus = Net::Prometheus->new; |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
sub serve_request |
392
|
|
|
|
|
|
|
{ |
393
|
|
|
|
|
|
|
my ( $request ) = @_; |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
if( $request->uri->path eq "/metrics" ) { |
396
|
|
|
|
|
|
|
return $prometheus->handle( $request ); |
397
|
|
|
|
|
|
|
} |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
... |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
=cut |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Some handy pseudomethods to make working on HTTP::Response less painful |
405
|
|
|
|
|
|
|
my $set_header = sub { |
406
|
|
|
|
|
|
|
my $resp = shift; |
407
|
|
|
|
|
|
|
$resp->header( @_ ); |
408
|
|
|
|
|
|
|
$resp; |
409
|
|
|
|
|
|
|
}; |
410
|
|
|
|
|
|
|
my $set_content = sub { |
411
|
|
|
|
|
|
|
my $resp = shift; |
412
|
|
|
|
|
|
|
$resp->content( @_ ); |
413
|
|
|
|
|
|
|
$resp; |
414
|
|
|
|
|
|
|
}; |
415
|
|
|
|
|
|
|
my $fix_content_length = sub { |
416
|
|
|
|
|
|
|
my $resp = shift; |
417
|
|
|
|
|
|
|
$resp->content_length or $resp->content_length( length $resp->content ); |
418
|
|
|
|
|
|
|
$resp; |
419
|
|
|
|
|
|
|
}; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
sub handle |
422
|
|
|
|
|
|
|
{ |
423
|
1
|
|
|
1
|
1
|
8476
|
my $self = shift; |
424
|
1
|
|
|
|
|
3
|
my ( $request ) = @_; |
425
|
|
|
|
|
|
|
|
426
|
1
|
|
|
|
|
512
|
require HTTP::Response; |
427
|
|
|
|
|
|
|
|
428
|
1
|
50
|
|
|
|
6964
|
$request->method eq "GET" or return |
429
|
|
|
|
|
|
|
HTTP::Response->new( 405 ) |
430
|
|
|
|
|
|
|
->$set_header( Content_Type => "text/plain" ) |
431
|
|
|
|
|
|
|
->$set_content( "Method " . $request->method . " not supported" ) |
432
|
|
|
|
|
|
|
->$fix_content_length; |
433
|
|
|
|
|
|
|
|
434
|
1
|
|
|
|
|
18
|
my $opts; |
435
|
1
|
50
|
|
|
|
5
|
$opts = { $request->uri->query_form } if length $request->uri->query; |
436
|
|
|
|
|
|
|
|
437
|
1
|
|
|
|
|
84
|
return HTTP::Response->new( 200 ) |
438
|
|
|
|
|
|
|
->$set_header( Content_Type => "text/plain; version=0.0.4; charset=utf-8" ) |
439
|
|
|
|
|
|
|
->$set_content( $self->render( $opts ) ) |
440
|
|
|
|
|
|
|
->$fix_content_length; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=head2 psgi_app |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
$app = $prometheus->psgi_app |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
Returns a new L application as a C reference. This application |
448
|
|
|
|
|
|
|
will render the metrics in the Prometheus text exposition format, suitable for |
449
|
|
|
|
|
|
|
scraping by the Prometheus collector. |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
This application will respond to any C request, and reject requests for |
452
|
|
|
|
|
|
|
any other method. If a C is present in the environment it will |
453
|
|
|
|
|
|
|
be parsed for collector options to pass into the L method. |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
This method is useful for integrating metrics into an existing HTTP server |
456
|
|
|
|
|
|
|
application which is uses or is based on PSGI. For example: |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
use Plack::Builder; |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
my $prometheus = Net::Prometheus::->new; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
builder { |
463
|
|
|
|
|
|
|
mount "/metrics" => $prometheus->psgi_app; |
464
|
|
|
|
|
|
|
... |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=cut |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
sub psgi_app |
470
|
|
|
|
|
|
|
{ |
471
|
1
|
|
|
1
|
1
|
6
|
my $self = shift; |
472
|
|
|
|
|
|
|
|
473
|
1
|
|
|
|
|
542
|
require URI; |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
return sub { |
476
|
1
|
|
|
1
|
|
1124
|
my $env = shift; |
477
|
1
|
|
|
|
|
3
|
my $method = $env->{REQUEST_METHOD}; |
478
|
|
|
|
|
|
|
|
479
|
1
|
50
|
|
|
|
6
|
$method eq "GET" or return [ |
480
|
|
|
|
|
|
|
405, |
481
|
|
|
|
|
|
|
[ "Content-Type" => "text/plain" ], |
482
|
|
|
|
|
|
|
[ "Method $method not supported" ], |
483
|
|
|
|
|
|
|
]; |
484
|
|
|
|
|
|
|
|
485
|
1
|
|
|
|
|
2
|
my $opts; |
486
|
1
|
50
|
|
|
|
5
|
if( defined $env->{QUERY_STRING} ) { |
487
|
0
|
|
|
|
|
0
|
$opts = +{ URI->new( "?$env->{QUERY_STRING}", "http" )->query_form }; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
return [ |
491
|
1
|
|
|
|
|
5
|
200, |
492
|
|
|
|
|
|
|
[ "Content-Type" => "text/plain; version=0.0.4; charset=utf-8" ], |
493
|
|
|
|
|
|
|
[ $self->render( $opts ) ], |
494
|
|
|
|
|
|
|
]; |
495
|
1
|
|
|
|
|
4688
|
}; |
496
|
|
|
|
|
|
|
} |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=head2 export_to_IO_Async |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
$prometheus->export_to_IO_Async( $loop, %args ) |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
Performs the necessary steps to create an HTTP server for exporting metrics |
503
|
|
|
|
|
|
|
over HTTP via L. This will involve creating a new |
504
|
|
|
|
|
|
|
L instance added to the loop. |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
This new server will listen on its own port number for any incoming request, |
507
|
|
|
|
|
|
|
and will serve metrics regardless of path. |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
Note this should only be used in applications that don't otherwise have an |
510
|
|
|
|
|
|
|
HTTP server, such as self-contained monitoring exporters or exporting metrics |
511
|
|
|
|
|
|
|
as a side-effect of other activity. For existing HTTP server applications it |
512
|
|
|
|
|
|
|
is better to integrate with the existing request/response processing of the |
513
|
|
|
|
|
|
|
application, such as by using the L or L methods. |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
Takes the following named arguments: |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=over 4 |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=item port => INT |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
Port number on which to listen for incoming HTTP requests. |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=back |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
=cut |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub export_to_IO_Async |
528
|
|
|
|
|
|
|
{ |
529
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
530
|
0
|
|
|
|
|
0
|
my ( $loop, %args ) = @_; |
531
|
|
|
|
|
|
|
|
532
|
0
|
|
|
|
|
0
|
require IO::Async::Loop; |
533
|
0
|
|
|
|
|
0
|
require Net::Async::HTTP::Server; |
534
|
|
|
|
|
|
|
|
535
|
0
|
|
0
|
|
|
0
|
$loop //= IO::Async::Loop->new; |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
my $httpserver = Net::Async::HTTP::Server->new( |
538
|
|
|
|
|
|
|
on_request => sub { |
539
|
0
|
|
|
0
|
|
0
|
my $httpserver = shift; |
540
|
0
|
|
|
|
|
0
|
my ( $req ) = @_; |
541
|
|
|
|
|
|
|
|
542
|
0
|
|
|
|
|
0
|
$req->respond( $self->handle( $req->as_http_request ) ); |
543
|
|
|
|
|
|
|
}, |
544
|
0
|
|
|
|
|
0
|
); |
545
|
|
|
|
|
|
|
|
546
|
0
|
|
|
|
|
0
|
$loop->add( $httpserver ); |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# Yes this is a blocking call |
549
|
|
|
|
|
|
|
$httpserver->listen( |
550
|
|
|
|
|
|
|
socktype => "stream", |
551
|
|
|
|
|
|
|
service => $args{port}, |
552
|
0
|
|
|
|
|
0
|
)->get; |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
{ |
556
|
|
|
|
|
|
|
package |
557
|
|
|
|
|
|
|
Net::Prometheus::_MetricGroup; |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub new |
560
|
|
|
|
|
|
|
{ |
561
|
2
|
|
|
2
|
|
3
|
my $class = shift; |
562
|
2
|
|
|
|
|
6
|
my ( $prometheus, %args ) = @_; |
563
|
|
|
|
|
|
|
return bless { |
564
|
|
|
|
|
|
|
prometheus => $prometheus, |
565
|
|
|
|
|
|
|
namespace => $args{namespace}, |
566
|
|
|
|
|
|
|
subsystem => $args{subsystem}, |
567
|
2
|
|
|
|
|
15
|
}, $class; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
foreach my $method (qw( new_gauge new_counter new_summary new_histogram )) { |
571
|
7
|
|
|
7
|
|
46
|
no strict 'refs'; |
|
7
|
|
|
|
|
15
|
|
|
7
|
|
|
|
|
780
|
|
572
|
|
|
|
|
|
|
*$method = sub { |
573
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
574
|
|
|
|
|
|
|
$self->{prometheus}->$method( |
575
|
|
|
|
|
|
|
namespace => $self->{namespace}, |
576
|
|
|
|
|
|
|
subsystem => $self->{subsystem}, |
577
|
2
|
|
|
|
|
11
|
@_, |
578
|
|
|
|
|
|
|
); |
579
|
|
|
|
|
|
|
}; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
=head1 COLLECTORS |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
The toplevel C object stores a list of "collector" instances, |
586
|
|
|
|
|
|
|
which are used to generate the values that will be made visible via the |
587
|
|
|
|
|
|
|
L method. A collector can be any object instance that has a method |
588
|
|
|
|
|
|
|
called C, which when invoked is passed no arguments and expected to |
589
|
|
|
|
|
|
|
return a list of L structures. |
590
|
|
|
|
|
|
|
|
591
|
|
|
|
|
|
|
@metricsamples = $collector->collect( $opts ) |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
The L class is already a valid collector (and hence, |
594
|
|
|
|
|
|
|
so too are the individual metric type subclasses). This interface allows the |
595
|
|
|
|
|
|
|
creation of new custom collector objects, that more directly collect |
596
|
|
|
|
|
|
|
information to be exported. |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
Collectors might choose to behave differently in the presence of some |
599
|
|
|
|
|
|
|
specifically-named option; typically to provide extra detail not normally |
600
|
|
|
|
|
|
|
provided (maybe at the expense of extra processing time to calculate it). |
601
|
|
|
|
|
|
|
Collectors must not complain about the presence of unrecognised options; the |
602
|
|
|
|
|
|
|
hash is shared among all potential collectors. |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
=cut |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
=head1 TODO |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
=over 8 |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=item * |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
Histogram/Summary 'start_timer' support |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=item * |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
Add other C methods for other event systems and HTTP-serving |
617
|
|
|
|
|
|
|
frameworks, e.g. L. |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
=back |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
=cut |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
=head1 AUTHOR |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
Paul Evans |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=cut |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
0x55AA; |