File Coverage

blib/lib/Metrics/Any/Adapter/File.pm
Criterion Covered Total %
statement 55 64 85.9
branch 5 12 41.6
condition n/a
subroutine 14 15 93.3
pod 0 10 0.0
total 74 101 73.2


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, 2020 -- leonerd@leonerd.org.uk
5              
6             package Metrics::Any::Adapter::File 0.08;
7              
8 2     2   805 use v5.14;
  2         7  
9 2     2   9 use warnings;
  2         3  
  2         43  
10              
11 2     2   8 use Carp;
  2         7  
  2         1646  
12              
13             =head1 NAME
14              
15             C - write metrics to a file
16              
17             =head1 SYNOPSIS
18              
19             use Metrics::Any::Adapter 'File', path => "metrics.log";
20              
21             =head1 DESCRIPTION
22              
23             This L adapter type writes observations of metric values into a
24             file. This may be helpful while debugging or otherwise testing code that
25             reports metrics.
26              
27             For example, by setting the C environment variable to
28             configure the adapter, a metric log will be written as a side-effect of
29             running a unit test:
30              
31             $ METRICS_ANY_ADAPTER=File:path=metrics.log perl -Mblib t/01test.t
32              
33             The generated file can then be inspected to see what metric values were
34             reported while the program was running.
35              
36             In particular, specifying the file F allows the full metrics
37             generation path to be tested with the code under test seeing a "real" adapter
38             even though the output goes nowhere.
39              
40             $ METRICS_ANY_ADAPTER=File:path=/dev/null ./Build test
41              
42             Distribution and timing metrics are tracked with a running total and count of
43             observations.
44              
45             =head1 ARGUMENTS
46              
47             The following additional arguments are recognised
48              
49             =head2 path
50              
51             The path to the file to write to.
52              
53             =cut
54              
55             my %metrics;
56              
57             sub new
58             {
59 1     1 0 2 my $class = shift;
60 1         3 my %args = @_;
61              
62 1         2 my $fh;
63 1 50       3 if( $args{fh} ) {
    0          
64             # fh isn't documented but useful for unit testing
65 1         2 $fh = $args{fh};
66             }
67             elsif( $args{path} ) {
68 0 0       0 open $fh, ">>", $args{path} or die "Cannot open $args{path} for writing - $!\n";
69             }
70             else {
71 0         0 croak "Require a 'path' argument";
72             }
73              
74 1         8 $fh->autoflush;
75              
76 1         54 return bless {
77             __fh => $fh,
78             }, $class;
79             }
80              
81             sub _make
82             {
83 4     4   6 my $self = shift;
84 4         11 my ( $type, $handle, %args ) = @_;
85              
86 4         4 my $name = $args{name};
87 4 50       20 $name = join "_", @$name if ref $name eq "ARRAY";
88              
89             $self->{$handle} = {
90             type => $type,
91             name => $name,
92             labels => $args{labels},
93 4         23 };
94             }
95              
96             sub _key
97             {
98 16     16   21 my $self = shift;
99 16         20 my ( $handle, $suffix, @labelvalues ) = @_;
100              
101 16         22 my $meta = $self->{$handle};
102              
103 16         17 my $key = $meta->{name};
104 16 100       28 $key .= $suffix if defined $suffix;
105              
106 16 50       36 if( my $labels = $meta->{labels} ) {
107 0         0 $key .= " $labels->[$_]:$labelvalues[$_]" for 0 .. $#$labels;
108             }
109              
110 16         71 return $key;
111             }
112              
113 1     1 0 3 sub make_counter { shift->_make( counter => @_ ) }
114              
115             sub inc_counter_by
116             {
117 2     2 0 3 my $self = shift;
118 2         3 my ( $handle, $amount, @labelvalues ) = @_;
119 2         3 my $fh = $self->{__fh};
120              
121 2         5 my $key = $self->_key( $handle, undef, @labelvalues );
122 2         4 my $current = $metrics{$key} += $amount;
123              
124 2         24 printf $fh "METRIC COUNTER %s %+g => %g\n",
125             $key, $amount, $current;
126             }
127              
128 1     1 0 2 sub make_distribution { shift->_make( distribution => @_ ) }
129              
130             sub report_distribution
131             {
132 2     2 0 4 my $self = shift;
133 2         4 my ( $handle, $amount, @labelvalues ) = @_;
134 2         3 my $fh = $self->{__fh};
135              
136 2         4 my $count = $metrics{ $self->_key( $handle, "_count", @labelvalues ) } += 1;
137 2         4 my $total = $metrics{ $self->_key( $handle, "_total", @labelvalues ) } += $amount;
138              
139 2         4 printf $fh "METRIC DISTRIBUTION %s +%g => %g/%d [avg=%g]\n",
140             $self->_key( $handle, undef, @labelvalues ), $amount, $total, $count, $total/$count;
141             }
142              
143 1     1 0 3 sub make_gauge { shift->_make( gauge => @_ ) }
144              
145             sub inc_gauge_by
146             {
147 2     2 0 3 my $self = shift;
148 2         4 my ( $handle, $amount, @labelvalues ) = @_;
149 2         2 my $fh = $self->{__fh};
150              
151 2         4 my $key = $self->_key( $handle, undef, @labelvalues );
152 2         4 my $current = $metrics{$key} += $amount;
153              
154 2         45 printf $fh "METRIC GAUGE %s %+g => %g\n",
155             $key, $amount, $current;
156             }
157              
158             sub set_gauge_to
159             {
160 0     0 0 0 my $self = shift;
161 0         0 my ( $handle, $amount, @labelvalues ) = @_;
162 0         0 my $fh = $self->{__fh};
163              
164 0         0 my $key = $self->_key( $handle, undef, @labelvalues );
165 0         0 my $current = $metrics{$key} = $amount;
166              
167 0         0 printf $fh "METRIC GAUGE %s => %g\n",
168             $key, $current;
169             }
170              
171 1     1 0 2 sub make_timer { shift->_make( timer => @_ ) }
172              
173             sub report_timer
174             {
175 2     2 0 3 my $self = shift;
176 2         4 my ( $handle, $duration, @labelvalues ) = @_;
177 2         2 my $fh = $self->{__fh};
178              
179 2         5 my $count = $metrics{ $self->_key( $handle, "_count", @labelvalues ) } += 1;
180 2         5 my $total = $metrics{ $self->_key( $handle, "_total", @labelvalues ) } += $duration;
181              
182 2         4 printf $fh "METRIC TIMER %s +%.3g => %.3g/%d [avg=%g]\n",
183             $self->_key( $handle, undef, @labelvalues ), $duration, $total, $count, $total/$count;
184             }
185              
186             =head1 AUTHOR
187              
188             Paul Evans
189              
190             =cut
191              
192             0x55AA;