File Coverage

lib/Net/Prometheus/Pushgateway.pm
Criterion Covered Total %
statement 17 76 22.3
branch 0 14 0.0
condition 0 27 0.0
subroutine 6 15 40.0
pod 4 6 66.6
total 27 138 19.5


line stmt bran cond sub pod time code
1             package Net::Prometheus::Pushgateway;
2              
3 1     1   54221 use 5.14.2;
  1         3  
4 1     1   3 use strict;
  1         2  
  1         15  
5 1     1   3 use warnings;
  1         2  
  1         17  
6 1     1   508 use utf8;
  1         14  
  1         3  
7 1     1   28 use Carp qw/croak carp/;
  1         1  
  1         38  
8 1     1   512 use LWP::UserAgent;
  1         39276  
  1         771  
9              
10             our $VERSION = '0.02';
11              
12             my %METRIC_VALID_TYPES = (
13             'untyped' => 1,
14             'counter' => 1,
15             'gauge' => 1,
16             'histogram' => 1,
17             'summary' => 1,
18             );
19              
20             sub new {
21 0     0 1   my ($class, %opt) = @_;
22 0           my $self = {};
23 0   0       $self->{'host'} = $opt{'-host'} // croak "You must specify '-host' param";
24 0   0       $self->{'port'} = $opt{'-port'} // croak "You must specify '-port' param";
25 0           my $path = $opt{'-path'};
26 0           $self->{'ua'} = LWP::UserAgent->new();
27 0           $self->{'url'} = 'http://' . $self->{host} . ':' . $self->{'port'} . $path;
28              
29 0           return bless $self, $class;
30             }
31              
32             sub add {
33 0     0 0   my $self = shift;
34 0           my $raw_str = $self->_add(@_);
35 0           return $self->_send_to_prometheus($raw_str);
36             }
37              
38             sub increment {
39 0     0 1   my $self = shift;
40 0           my $raw_str = $self->_add(
41             @_,
42             '-value' => 1,
43             '-type' => 'counter',
44             );
45 0           return $self->_send_to_prometheus($raw_str);
46             }
47              
48             sub summary {
49 0     0 1   my $self = shift;
50 0           my $raw_str = $self->_add(
51             @_,
52             '-type' => 'summary',
53             );
54 0           return $self->_send_to_prometheus($raw_str);
55             }
56              
57             sub gauge {
58 0     0 0   my $self = shift;
59 0           my $raw_str = $self->_add(
60             @_,
61             '-type' => 'gauge',
62             );
63 0           return $self->_send_to_prometheus($raw_str);
64             }
65              
66             sub histogram {
67 0     0 1   my ($self, %opt) = @_;
68 0   0       my $metric_name = $opt{'-metric_name'} // croak "You must specify '-metric_name' param";
69 0   0       my $label = $opt{'-label'} // {};
70 0   0       my $value = $opt{'-value'} // croak "You must specify '-value' param";
71 0   0       my $buckets = $opt{'-buckets'} // croak "You must specify '-buckets' param";
72 0 0         croak "Param '-buckets' must be arrayref" if ref($buckets) ne 'ARRAY';
73 0 0         croak "Label must be hashref" if ref($label) ne 'HASH';
74              
75 0           my @metrics;
76 0           push @metrics, "# TYPE $metric_name histogram\n";
77 0           push @metrics, $self->_prepare_raw_metric($metric_name . '_count', $label, 1);
78 0           push @metrics, $self->_prepare_raw_metric($metric_name . '_sum', $label, $value);
79              
80 0           for my $bucket (@$buckets) {
81 0 0         push @metrics, $self->_prepare_raw_metric($metric_name . '_bucket', { %$label, 'le' => $bucket}, $value <= $bucket ? 1 : 0);
82             }
83 0           push @metrics, $self->_prepare_raw_metric($metric_name . '_bucket', { %$label, 'le' => '+Inf'}, 1);
84              
85 0           return $self->_send_to_prometheus(join('', @metrics));
86             }
87              
88             sub _add {
89 0     0     my ($self, %opt) = @_;
90 0   0       my $metric_name = $opt{'-metric_name'} // croak "You must specify '-metric_name' param";
91 0   0       my $label = $opt{'-label'} // {};
92 0   0       my $value = $opt{'-value'} // croak "You must specify '-value' param";
93 0   0       my $type = $opt{'-type'} // 'untyped';
94 0           $type = lc($type);
95              
96 0 0         croak "Label must be hashref" if ref($label) ne 'HASH';
97 0 0         croak "Unvalid metric type: '$type'. Valid types: " . join(', ', keys %METRIC_VALID_TYPES) if not $METRIC_VALID_TYPES{$type};
98              
99 0           my $type_str = "# TYPE $metric_name $type\n";
100              
101 0           my $raw_metric = $self->_prepare_raw_metric($metric_name, $label, $value);
102              
103 0           return $type_str . $raw_metric;
104             }
105              
106             sub _prepare_raw_metric {
107 0     0     my ($self, $metric_name, $label, $value) = @_;
108 0           my $raw_str = $metric_name;
109 0 0         if ($label) {
110 0           $raw_str .= '{' . join (', ', map {$_ . '="' . $label->{$_} . '"'} keys %$label) . '}';
  0            
111             }
112 0           $raw_str .= " $value\n";
113 0           return $raw_str;
114             }
115              
116             sub _send_to_prometheus {
117 0     0     my ($self, $str) = @_;
118              
119 0           my $request = HTTP::Request->new('POST', $self->{'url'});
120 0           $request->content($str);
121 0           my $response = $self->{'ua'}->request($request);
122 0 0         return 1 if ($response->is_success);
123              
124 0           croak "Can't send POST request to '$self->{'url'}'. MSG: " . $response->decoded_content . " Code: " . $response->code;
125             }
126              
127             1;
128              
129             =pod
130              
131             =encoding UTF-8
132              
133             =head1 NAME
134              
135             B - client module for pushing metrics to prometheus exporter (pushgateway, prometheus aggregation gateway)
136              
137             =head1 SYNOPSYS
138              
139             use Net::Prometheus::Pushgateway;
140              
141             # Create Net::Prometheus::Pushgateway object for pushgateway exporter
142             my $metric = Net::Prometheus::Pushgateway->new(
143             '-host' => '127.0.0.1',
144             '-port' => 9091,
145             '-path' => '/metrics/job//instance/',
146             );
147             # OR
148             # Create Net::Prometheus::Pushgateway object for prometheus aggregation gateway
149             my $metric = Net::Prometheus::Pushgateway->new(
150             '-host' => '127.0.0.1',
151             '-port' => 9091,
152             '-path' => '/api/ui/metrics',
153             );
154              
155             # Send increment metric
156             $metric->increment(-metric_name => 'perl_metric_increment', -label => {'perl_label' => 5});
157              
158             # Send summary metric
159             $metric->summary(-metric_name => 'perl_metric_summary', -label => {'perl_label' => 5}, -value => 15);
160              
161             # Send histogram metric
162             $metric->histogram(-metric_name => 'perl_metric_histogram', -label => {'perl_label' => 5}, -value => 15, -buckets => [qw/1 2 3 4 5/]);
163              
164              
165             =head1 METHODS
166              
167             =head2 new(%opt)
168              
169             Create Net::Prometheus::Pushgateway object
170              
171             Options:
172             -host => Prometheus exporter host
173             -port => Prometheus exporter port number
174             -path => Path to prometheus exporter host (/api/ui/metrics - prometheus aggregation gateway, /metrics/job//instance/ - prometeus
175              
176             =head1 PUSH METRICS
177              
178             =head1 add(%opt)
179              
180             Push custom metrics
181              
182             Options:
183             -metric_name => Name of pushed metrics
184             -label => HashRef to metric labels
185             -value => Metric value
186             -type => Metric type (default: untyped. Valid metric types in %Net::Prometheus::Pushgateway::METRIC_VALID_TYPE)S
187              
188             =head2 increment(%opt)
189              
190             Push increment metrics
191              
192             Options:
193             -metric_name => Name of pushed metrics
194             -label => HashRef to metric labels
195              
196             =head2 summary(%opt)
197              
198             Push summary metrics
199              
200             Options:
201             -metric_name => Name of pushed metrics
202             -value => Metric value
203             -label => HashRef to metric labels (default: {})
204              
205             =head2 histogram(%opt)
206              
207             Push histogram metric
208              
209             Options:
210             -metric_name => Name of pushed metrics
211             -value => Metric value
212             -label => HashRef to metric labels (default: {})
213             -buckets => ArayRef to buckets values
214              
215             =head1 DEPENDENCE
216              
217             L
218              
219             =head1 AUTHORS
220              
221             =over 4
222              
223             =item *
224              
225             Pavel Andryushin
226              
227             =back
228              
229             =head1 COPYRIGHT AND LICENSE
230              
231             This software is copyright (c) 2020 by Pavel Andryushin.
232              
233             This is free software; you can redistribute it and/or modify it under
234             the same terms as the Perl 5 programming language system itself.
235              
236             =cut