File Coverage

blib/lib/Metrics/Any/Collector.pm
Criterion Covered Total %
statement 140 154 90.9
branch 40 48 83.3
condition 24 33 72.7
subroutine 27 29 93.1
pod 15 17 88.2
total 246 281 87.5


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::Collector 0.09;
7              
8 12     12   143 use v5.14;
  12         44  
9 12     12   62 use warnings;
  12         33  
  12         326  
10              
11 12     12   62 use Carp;
  12         24  
  12         766  
12              
13 12     12   3658 use Metrics::Any::Adapter;
  12         29  
  12         66  
14              
15 12     12   76 use List::Util 1.29 qw( pairkeys );
  12         260  
  12         13571  
16              
17             =head1 NAME
18              
19             C - module-side of the monitoring metrics reporting API
20              
21             =head1 SYNOPSIS
22              
23             use Metrics::Any '$metrics',
24             strict => 0,
25             name_prefix => [ 'my_module_name' ];
26              
27             sub do_thing {
28             $metrics->inc_counter( 'things_done' );
29             }
30              
31             =head1 DESCRIPTION
32              
33             Instances of this class provide an API for individual modules to declare
34             metadata about metrics they will report, and to report individual values or
35             observations on those metrics. An instance should be obtained for a reporting
36             module by the C statement.
37              
38             The collector acts primarily as a proxy for the application's configured
39             L instance. The proxy will lazily create an adapter
40             when required to first actually report a metric value, but until then any
41             metadata stored by any of the C methods will not create one. This lazy
42             deferral allows a certain amount of flexibility with module load order and
43             application startup. By carefully writing module code to not report any values
44             of metrics until the main activity has actually begin, it should be possible
45             to allow programs to configure the metric reporting in a flexible manner
46             during program startup.
47              
48             =head2 Batch-Mode Reporting
49              
50             Some adapters may support an optional API for implementing metrics in a more
51             high-performance manner, suitable for use in low-level (perhaps even XS) code
52             that might be invoked at high speed or many times over.
53              
54             Such code often needs to keep simple counters of particular events that happen
55             a lot. Rather than incurring the cost of a full stack of method calls into the
56             collector and adapter implementation on every event, the instrumented code can
57             register a callback function that the adapter will call on some schedule, that
58             will report the actual metrics. In the meantime, the instrumented code can
59             maintain its own counters of events, using plain Perl scalars (or native
60             integers in XS code), to be reported in bulk when required. This reduces the
61             overall CPU cost involved in collecting metrics.
62              
63             This is most effective with pull-based adapters such as Test or Prometheus,
64             where the callback might only need to be invoked at the end of a test run, or
65             when the prometheus server polls the C HTTP endpoint.
66              
67             my $evcounter = 0;
68              
69             $metrics->add_batch_mode_callback( sub {
70             $metrics->inc_counter_by( events => $evcounter );
71             $evcounter = 0;
72             } );
73              
74             sub do_thing {
75             $evcounter++;
76             ...
77             }
78              
79             Because not every adapter may implement this mode, instrumented code should be
80             prepared to fall back on the regular API to report its counters.
81              
82             my $evcounter = 0;
83              
84             my $use_batch = $metrics->add_batch_mode_callback( sub {
85             $metrics->inc_counter_by( events => $evcounter );
86             $evcounter = 0;
87             } );
88              
89             sub do_thing {
90             $use_batch ? $evcounter++ : $metrics->inc_counter( events => );
91             ...
92             }
93              
94             Each adapter implementation should document if and how it handles batch mode.
95              
96             =head1 ENVIRONMENT
97              
98             =head2 METRICS_ANY_DISABLE
99              
100             I
101              
102             Provides a list of packages and namespaces in which to disable L
103             reporting entirely.
104              
105             This variable gives a comma-separated list of name patterns. Patterns may end
106             with C<::*>, where they will match any package whose name starts with that
107             prefix, or they may be literal package names. If any code in matching packages
108             attempts to use L to report metrics, that code will
109             be given a C adapter, and no metrics will be reported from here.
110              
111             For example, to disable the metrics that C itself
112             creates when exporting Prometheus metrics:
113              
114             $ METRICS_ANY_DISABLE=Net::Async::HTTP::Server ./program.pl
115              
116             =cut
117              
118             # Not public API; used by Metrics::Any::import_into
119             sub new
120             {
121 15     15 0 31 my $class = shift;
122 15         40 my ( $package, %args ) = @_;
123              
124             return bless {
125             package => $package,
126             adapter => undef,
127             deferred => [],
128             name_prefix => $args{name_prefix},
129             metrics => {},
130 15   100     461 strict => $args{strict} // 1,
131             }, $class;
132             }
133              
134             my %disable_for_package;
135             my %disable_for_namespace;
136             if( my $val = $ENV{METRICS_ANY_DISABLE} ) {
137             foreach my $pattern ( split m/,/, $val ) {
138             if( $pattern =~ s/\*$// ) {
139             $pattern =~ s/::$//;
140             $disable_for_namespace{$pattern} = 1;
141             }
142             else {
143             $disable_for_package{$pattern} = 1;
144             }
145             }
146              
147             require Metrics::Any::Adapter::Null;
148             }
149              
150             sub _enabled_for_package
151             {
152 14     14   45 my ( $pkg ) = @_;
153              
154 14 100       84 return 0 if $disable_for_package{$pkg};
155 13 100       119 return 1 unless %disable_for_namespace;
156              
157 3         5 do {
158 4 100       31 return 0 if $disable_for_namespace{$pkg};
159             } while( $pkg =~ s/::[^:]+// );
160              
161 1         7 return 1;
162             }
163              
164             sub adapter
165             {
166 87     87 0 1910 my $self = shift;
167 87 100       614 return $self->{adapter} if $self->{adapter};
168              
169             my $adapter = $self->{adapter} =
170 14 100       53 ( _enabled_for_package( $self->{package} ) ? Metrics::Any::Adapter->adapter
171             : Metrics::Any::Adapter::Null->new );
172 14         54 foreach my $call ( @{ $self->{deferred} } ) {
  14         52  
173 9         31 my ( $method, @args ) = @$call;
174 9         46 $adapter->$method( @args );
175             }
176 14         61 undef $self->{deferred};
177 14         60 return $adapter;
178             }
179              
180             sub _adapter_call
181             {
182 49     49   90 my $self = shift;
183 49         134 my ( $method, @args ) = @_;
184              
185 49 100       126 if( $self->{adapter} ) {
186 40         190 $self->{adapter}->$method( @args );
187             }
188             else {
189 9         13 push @{ $self->{deferred} }, [ $method, @args ];
  9         73  
190             }
191             }
192              
193             sub _metricname
194             {
195 53     53   93 my $self = shift;
196 53         95 my ( $suffix ) = @_;
197              
198 53 100       290 return $suffix unless defined $self->{name_prefix};
199 8         13 return [ @{ $self->{name_prefix} }, @$suffix ];
  8         24  
200             }
201              
202             sub _labelvalues
203             {
204 74     74   111 my $self = shift;
205 74         150 my ( $type, $handle, @args ) = @_;
206              
207 74         222 my $meta = $self->{$handle};
208 74 100       169 if( $meta ) {
    100          
209 60 50       188 $meta->[0] eq $type or croak "Metric '$handle' is not a $type";
210             }
211             elsif( !$self->{strict} ) {
212 10         16 my @labelnames;
213 10 100       25 if( !@args ) {
    50          
    0          
214             # no labels
215             }
216             elsif( ref $args[0] eq "ARRAY" ) {
217 2         3 @labelnames = pairkeys @{ $args[0] };
  2         11  
218             }
219             elsif( ref $args[0] eq "HASH" ) {
220             carp "Lazily creating a labelled metric with multiple labels using a HASH reference yields unreliable label order"
221 0 0       0 if keys %{ $args[0] } > 1;
  0         0  
222 0         0 @labelnames = keys %{ $args[0] };
  0         0  
223             }
224             else {
225 0         0 croak "Cannot lazily create a labelled metric from label values specified in a flat list";
226             }
227              
228 10         26 my $make_method = "make_$type";
229 10         42 $self->$make_method( $handle, labels => \@labelnames );
230              
231 10         27 $meta = $self->{$handle};
232             }
233             else {
234 4         315 croak "No such metric '$handle'";
235             }
236              
237 70         175 my ( undef, @labelnames ) = @$meta;
238              
239 70 100       208 if( !@args ) {
    100          
240 61         200 return;
241             }
242             elsif( ref $args[0] ) {
243 7 50       20 warn "Received additional arguments to metrics reporting function\n" if @args > 1;
244 7         17 my ( $arg ) = @args;
245 7 100       32 my %v = ( ref $arg eq "ARRAY" ) ? @$arg : %$arg;
246              
247 7         11 my @labelvalues;
248             ( defined $v{$_} or croak "Missing value for label '$_'" ) and push @labelvalues, delete $v{$_}
249 7   33     49 for @labelnames;
      33        
250              
251             # Warn but don't complain about extra values
252 7         22 carp "Found extra label value for '$_'" for keys %v;
253              
254 7         30 return @labelvalues;
255             }
256             else {
257 2         5 return @args;
258             }
259             }
260              
261             =head1 ARGUMENTS
262              
263             =head2 name_prefix
264              
265             I
266              
267             Optional prefix to prepend to any name provided to the C functions.
268              
269             If set, this value and the registered names must be given as array references,
270             not simple strings.
271              
272             use Metrics::Any '$metrics', name_prefix => [qw( my_program_name )];
273              
274             $metrics->make_counter( events =>
275             name => [ "events" ],
276             );
277              
278             # Will create a counter named ["my_program_name", "events"] formed by the
279             # adapter.
280              
281             =head2 strict
282              
283             I
284              
285             Optional boolean which controls whether metrics must be registered by a
286             C method before they can be used (when true), or whether to attempt
287             lazily registering them when first encountered by a reporting method (when
288             false).
289              
290             When strict mode is off and a reporting method (e.g. C) is
291             invoked on an unrecognised handle, it will be lazily registered. If the metric
292             is reported with values, an attempt is made to determine what the list of
293             label names is; which will depend on the form the label values are given in.
294             Labels passed by array reference, or by hash reference for a single label will
295             work fine. If a hash reference is passed with multiple keys, a warning is
296             printed that the order may not be reliable. Finally, for (discouraged) flat
297             lists of values directly it is not possible to recover label name information
298             so an exception is thrown.
299              
300             For this reason, when operating with strict mode off, it is recommended always
301             to use the array reference form of supplying labels, to ensure they are
302             registered correctly.
303              
304             In the current version this parameter defaults true, and thus all metrics must
305             be registered in advance. This may be changed in a future version for
306             convenience in smaller modules, so paranoid authors should set it explicitly:
307              
308             use Metrics::Any::Adapter '$metrics', strict => 1;
309              
310             If strict mode is switched off, it is recommended to set a name prefix to
311             ensure that lazily-registered metrics will at least have a useful name.
312              
313             =cut
314              
315             =head1 BOOLEAN OVERRIDE
316              
317             Instances of this class override boolean truth testing. They are usually true,
318             except in the case that an adapter has already been created and it is the Null
319             type. This allows modules to efficiently test whether to report metrics at all
320             by using code such as
321              
322             if( $metrics ) {
323             $metrics->inc_counter( name => some_expensive_function() );
324             }
325              
326             While the Null adapter will simply ignore any of the methods invoked on it,
327             without this conditional test the caller would otherwise still have to
328             calculate the value that won't be used. This structure allows the calculation
329             to be avoided if metrics are not in use.
330              
331             =cut
332              
333             use overload
334             'bool' => sub {
335 26 100   26   2983 !$_[0]->{adapter} or ref $_[0]->{adapter} ne "Metrics::Any::Adapter::Null"
336             },
337             # stringify as itself otherwise bool takes over and it just prints as 1,
338             # leading to much developer confusion
339 8     8   1315 '""' => sub { $_[0] },
340 12     12   13347 fallback => 1;
  12         11137  
  12         111  
341              
342             =head1 METHODS
343              
344             =cut
345              
346             =head2 package
347              
348             $package = $metrics->package
349              
350             Returns the package name that created the collector; the package in which the
351              
352             use Metrics::Any '$metrics';
353              
354             statement was invoked.
355              
356             =cut
357              
358             sub package
359             {
360 1     1 1 358 my $self = shift;
361 1         5 return $self->{package};
362             }
363              
364             =head2 add_batch_mode_callback
365              
366             $ok = $metrics->add_batch_mode_callback( sub { ... } )
367              
368             I
369              
370             If batch mode is supported on the underlying adapter, adds another callback to
371             its list of callbacks, to be invoked when it wishes to collect more metrics;
372             if this is supported then the method returns a true value.
373              
374             If batch mode is not supported, returns false.
375              
376             =cut
377              
378             sub add_batch_mode_callback
379             {
380 3     3 1 14 my $self = shift;
381 3         11 my ( $cb ) = @_;
382              
383 3 50 33     9 unless( $self->adapter->can( "HAVE_BATCH_MODE" ) and $self->adapter->HAVE_BATCH_MODE ) {
384 0         0 return 0;
385             }
386              
387 3         24 $self->adapter->add_batch_mode_callback( $cb );
388 3         10 return 1;
389             }
390              
391             =head1 METRIC TYPES
392              
393             Each type of metric is created by one of the C methods. They all take
394             the following common arguments:
395              
396             =over 4
397              
398             =item name => ARRAY[ STRING ] | STRING
399              
400             Optional. An array of string parts, or a plain string name to use for
401             reporting this metric to its upstream service.
402              
403             Modules should preferrably use an array of string parts to specify their
404             metric names, as different adapter types may have different ways to represent
405             this hierarchially. Base-level parts of the name should come first, followed
406             by more specific parts. It is common for related metrics to be grouped by name
407             having identical prefixes but differing only in the final part.
408              
409             The name is optional; if unspecified then the handle will be used to form the
410             name, combined with a C argument if one was set for the package.
411              
412             =item description => STRING
413              
414             Optional human-readable description. May be used for debugging or other
415             purposes.
416              
417             =item labels => ARRAY[ STRING ]
418              
419             Optional reference to an array of string names to use as label names.
420              
421             A labelled metric will expect to receive additional information in its
422             reporting method to give values for these labels. This information should be
423             in either an even-length array reference of name/value pairs, or a hash
424             reference. E.g.
425              
426             $metrics->inc_counter( handle => [ labelname => $labelvalue ] );
427             $metrics->inc_counter( handle => { labelname => $labelvalue } );
428              
429             A legacy form where a plain list of values is passed, each corresponding to a
430             named label in the same order, is currently accepted but discouraged in favour
431             of the above forms.
432              
433             $metrics->inc_counter( handle => $labelvalue );
434              
435             Note that not all metric reporting adapters may be able to represent all of
436             the labels. Each should document what its behaviour will be.
437              
438             =back
439              
440             =cut
441              
442             =head2 Counter
443              
444             The L method creates a new metric which counts occurances of
445             some event within the application. Its value begins at zero, and can be
446             incremented by L whenever the event occurs.
447              
448             Some counters may simple count occurances of events, while others may count
449             in other units, for example counts of bytes. Adapters may make use of the
450             C parameter of the distribution to perform some kind of
451             adapter-specific behaviour. The following units are suggested:
452              
453             =head3 bytes
454              
455             Observations give sizes in bytes (perhaps memory buffer or network message
456             sizes), and should be integers.
457              
458             =cut
459              
460             =head2 make_counter
461              
462             $collector->make_counter( $handle, %args )
463              
464             Requests the creation of a new counter metric. The C<$handle> name should be
465             unique within the collector instance, though does not need to be unique across
466             the entire program, as it will be namespaced by the collector instance.
467              
468             The following extra arguments may be passed:
469              
470             =over 4
471              
472             =item units => STRING
473              
474             A hint to the adapter about what kind of measurements are being observed, so
475             it might take specific behaviour.
476              
477             =back
478              
479             =cut
480              
481             sub make_counter
482             {
483 22     22 1 1929 my $self = shift;
484 22         74 my ( $handle, %args ) = @_;
485              
486 22   100     137 $args{name} = $self->_metricname( $args{name} // [ $handle ] );
487              
488 22 100       227 $self->{$handle} and croak "Already have a metric '$handle'";
489 21   100     42 $self->{$handle} = [ counter => @{ $args{labels} // [] } ];
  21         125  
490              
491 21         134 $self->_adapter_call( make_counter => "$self->{package}/$handle",
492             collector => $self,
493             %args
494             );
495             }
496              
497             =head2 inc_counter
498              
499             $collector->inc_counter( $handle, $labels )
500              
501             Reports that the counter metric value be incremented by one. The C<$handle>
502             name must match one earlier created by L.
503              
504             =cut
505              
506             sub inc_counter
507             {
508 28     28 1 982 my $self = shift;
509 28         69 my ( $handle, @args ) = @_;
510              
511 28         91 my @labelvalues = $self->_labelvalues( counter => $handle, @args );
512              
513 27         76 $self->adapter->inc_counter_by( "$self->{package}/$handle", 1, @labelvalues );
514             }
515              
516             =head2 inc_counter_by
517              
518             $collector->inc_counter_by( $handle, $amount, $labels )
519              
520             Reports that a counter metric value be incremented by some specified value.
521              
522             =cut
523              
524             sub inc_counter_by
525             {
526 3     3 1 640 my $self = shift;
527 3         11 my ( $handle, $amount, @args ) = @_;
528              
529 3         10 my @labelvalues = $self->_labelvalues( counter => $handle, @args );
530              
531 3         15 $self->adapter->inc_counter_by( "$self->{package}/$handle", $amount, @labelvalues );
532             }
533              
534             =head2 Distribution
535              
536             The L method creates a new metric which counts individual
537             observations of some numerical quantity (which may or may not be integral).
538             New observations can be added by the L method.
539              
540             Some adapter types may only store an aggregated total; others may store some
541             sort of statistical breakdown, either total + count, or a bucketed histogram.
542             The specific adapter documentation should explain how it handles
543             distributions.
544              
545             Adapters may make use of the C parameter of the distribution to perform
546             some kind of adapter-specific behaviour. The following units are suggested:
547              
548             =head3 bytes
549              
550             Observations give sizes in bytes (perhaps memory buffer or network message
551             sizes), and should be integers.
552              
553             =head3 seconds
554              
555             Observations give durations in seconds.
556              
557             =cut
558              
559             =head2 make_distribution
560              
561             $collector->make_distribution( $handle, %args )
562              
563             Requests the creation of a new distribution metric.
564              
565             The following extra arguments may be passed:
566              
567             =over 4
568              
569             =item units => STRING
570              
571             A hint to the adapter about what kind of measurements are being observed, so
572             it might take specific behaviour. If unspecified, a default of C will
573             apply.
574              
575             =back
576              
577             =cut
578              
579             sub make_distribution
580             {
581 11     11 1 1302 my $self = shift;
582 11         33 my ( $handle, %args ) = @_;
583              
584 11   100     75 $args{name} = $self->_metricname( $args{name} // [ $handle ] );
585              
586 11   50     84 $args{units} //= "bytes";
587              
588 11 100       137 $self->{$handle} and croak "Already have a metric '$handle'";
589 10   100     25 $self->{$handle} = [ distribution => @{ $args{labels} // [] } ];
  10         72  
590              
591 10         64 $self->_adapter_call( make_distribution => "$self->{package}/$handle",
592             collector => $self,
593             %args
594             );
595             }
596              
597             =head2 report_distribution
598              
599             $collector->report_distribution( $handle, $amount, $labels )
600              
601             I
602              
603             Reports a new observation for the distribution metric. The C<$handle> name
604             must match one earlier created by L. The C<$amount> may
605             be interpreted by the adapter depending on the defined C type for the
606             distribution.
607              
608             This method used to be called C and is currently still
609             available as an alias.
610              
611             =cut
612              
613             sub report_distribution
614             {
615 16     16 1 1408 my $self = shift;
616 16         58 my ( $handle, $amount, @args ) = @_;
617              
618 16         50 my @labelvalues = $self->_labelvalues( distribution => $handle, @args );
619              
620 15         66 my $adapter = $self->adapter;
621              
622             # Support new and legacy name
623 15   50     138 my $method = $adapter->can( "report_distribution" ) // "inc_distribution_by";
624 15         90 $adapter->$method( "$self->{package}/$handle", $amount, @labelvalues );
625             }
626              
627             *inc_distribution_by = \&report_distribution;
628              
629             =head2 Gauge
630              
631             The L method creates a new metric which reports on the
632             instantaneous value of some measurable quantity. Unlike the other metric types
633             this does not have to only increment forwards when certain events occur, but
634             can measure a quantity that may both increase and decrease over time; such as
635             the number some kind of object in memory, or the size of some data structure.
636              
637             As an alternative to incrementing or decrementing the value when particular
638             events occur, the absolute value of the gauge can also be set directly.
639              
640             =cut
641              
642             =head2 make_gauge
643              
644             $collector->make_gauge( $handle, %args )
645              
646             Requests the creation of a new gauge metric.
647              
648             =cut
649              
650             sub make_gauge
651             {
652 10     10 1 1363 my $self = shift;
653 10         36 my ( $handle, %args ) = @_;
654              
655 10   100     64 $args{name} = $self->_metricname( $args{name} // [ $handle ] );
656              
657 10 100       114 $self->{$handle} and croak "Already have a metric '$handle'";
658 9   100     29 $self->{$handle} = [ gauge => @{ $args{labels} // [] } ];
  9         110  
659              
660 9         89 $self->_adapter_call( make_gauge => "$self->{package}/$handle",
661             collector => $self,
662             %args
663             );
664             }
665              
666             =head2 inc_gauge
667              
668             $collector->inc_gauge( $handle, $labels )
669              
670             =head2 dec_gauge
671              
672             $collector->dec_gauge( $handle, $labels )
673              
674             =head2 inc_gauge_by
675              
676             $collector->inc_gauge_by( $handle, $amount, $labels )
677              
678             =head2 dec_gauge_by
679              
680             $collector->dec_gauge_by( $handle, $amount, $labels )
681              
682             Reports that the observed value of the gauge has increased or decreased by the
683             given amount (or 1).
684              
685             =cut
686              
687             sub inc_gauge
688             {
689 7     7 1 759 my $self = shift;
690 7         21 my ( $handle, @args ) = @_;
691              
692 7         60 my @labelvalues = $self->_labelvalues( gauge => $handle, @args );
693              
694 6         20 $self->adapter->inc_gauge_by( "$self->{package}/$handle", 1, @labelvalues );
695             }
696              
697             sub dec_gauge
698             {
699 0     0 1 0 my $self = shift;
700 0         0 my ( $handle, @args ) = @_;
701              
702 0         0 my @labelvalues = $self->_labelvalues( gauge => $handle, @args );
703              
704 0         0 $self->adapter->inc_gauge_by( "$self->{package}/$handle", -1, @labelvalues );
705             }
706              
707             sub inc_gauge_by
708             {
709 4     4 1 584 my $self = shift;
710 4         11 my ( $handle, $amount, @args ) = @_;
711              
712 4         17 my @labelvalues = $self->_labelvalues( gauge => $handle, @args );
713              
714 4         18 $self->adapter->inc_gauge_by( "$self->{package}/$handle", $amount, @labelvalues );
715             }
716              
717             sub dec_gauge_by
718             {
719 0     0 1 0 my $self = shift;
720 0         0 my ( $handle, $amount, @args ) = @_;
721              
722 0         0 my @labelvalues = $self->_labelvalues( gauge => $handle, @args );
723              
724 0         0 $self->adapter->inc_gauge_by( "$self->{package}/$handle", -$amount, @labelvalues );
725             }
726              
727             =head2 set_gauge_to
728              
729             $collector->set_gauge_to( $handle, $amount, $labels )
730              
731             Reports that the observed value of the gauge is now the given amount.
732              
733             The C<$handle> name must match one earlier created by L.
734              
735             =cut
736              
737             sub set_gauge_to
738             {
739 4     4 1 18 my $self = shift;
740 4         12 my ( $handle, $amount, @args ) = @_;
741              
742 4         13 my @labelvalues = $self->_labelvalues( gauge => $handle, @args );
743              
744 4         11 $self->adapter->set_gauge_to( "$self->{package}/$handle", $amount, @labelvalues );
745             }
746              
747             =head2 Timer
748              
749             The L method creates a new metric which measures durations of
750             time consumed by the application. New observations of durations can be added
751             by the L method.
752              
753             Timer metrics may be handled by the adapter similarly to distribution metrics.
754             Moreover, adapters may choose to implement timers as distributions with units
755             of C.
756              
757             =cut
758              
759             =head2 make_timer
760              
761             $collector->make_timer( $handle, %args )
762              
763             Requests the creation of a new timer metric.
764              
765             =cut
766              
767             sub make_timer
768             {
769 10     10 1 1263 my $self = shift;
770 10         39 my ( $handle, %args ) = @_;
771              
772 10   100     62 $args{name} = $self->_metricname( $args{name} // [ $handle ] );
773              
774 10 100       160 $self->{$handle} and croak "Already have a metric '$handle'";
775 9   100     24 $self->{$handle} = [ timer => @{ $args{labels} // [] } ];
  9         67  
776              
777 9         65 $self->_adapter_call( make_timer => "$self->{package}/$handle",
778             collector => $self,
779             %args
780             );
781             }
782              
783             =head2 report_timer
784              
785             $collector->report_timer( $handle, $duration, $labels )
786              
787             I
788              
789             Reports a new duration for the timer metric. The C<$handle> name must match
790             one earlier created by L. The C<$duration> gives a time measured
791             in seconds, and may be fractional.
792              
793             This method used to called C and is currently still available as
794             an alias.
795              
796             =cut
797              
798             sub report_timer
799             {
800 12     12 1 1355 my $self = shift;
801 12         32 my ( $handle, $duration, @args ) = @_;
802              
803 12         35 my @labelvalues = $self->_labelvalues( timer => $handle, @args );
804              
805 11         84 my $adapter = $self->adapter;
806              
807             # Support new and legacy name
808 11   50     62 my $method = $adapter->can( "report_timer" ) // "inc_timer_by";
809 11         58 $adapter->$method( "$self->{package}/$handle", $duration, @labelvalues );
810             }
811              
812             *inc_timer_by = \&report_timer;
813              
814             =head1 AUTHOR
815              
816             Paul Evans
817              
818             =cut
819              
820             0x55AA;