File Coverage

lib/Net/Prometheus/Pushgateway.pm
Criterion Covered Total %
statement 17 78 21.7
branch 0 14 0.0
condition 0 29 0.0
subroutine 6 15 40.0
pod 4 6 66.6
total 27 142 19.0


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