File Coverage

blib/lib/Net/Statsd/Tiny.pm
Criterion Covered Total %
statement 62 68 91.1
branch 12 18 66.6
condition 4 6 66.6
subroutine 14 15 93.3
pod 4 5 80.0
total 96 112 85.7


line stmt bran cond sub pod time code
1             package Net::Statsd::Tiny;
2              
3             # ABSTRACT: A tiny StatsD client that supports multimetric packets
4              
5 24     24   1181832 use v5.10.1;
  24         96  
6              
7 24     24   144 use strict;
  24         48  
  24         552  
8 24     24   120 use warnings;
  24         48  
  24         888  
9              
10 24     24   168 use base qw/ Class::Accessor::Fast /;
  24         24  
  24         12624  
11              
12 24     24   87480 use IO::Socket 1.18 ();
  24         480  
  24         6792  
13              
14             our $VERSION = 'v0.3.5';
15              
16              
17             __PACKAGE__->mk_ro_accessors(
18             qw/ host port proto prefix
19             autoflush max_buffer_size _socket /
20             );
21              
22             sub new {
23 23     23 1 349697484 my ( $class, @args ) = @_;
24              
25 23         697 my %args;
26 23 50 33     5438 if ( ( @args == 1 ) && ( ref( $args[0] ) eq 'HASH' ) ) {
27 0         0 %args = %{ $args[0] };
  0         0  
28             }
29             else {
30 23         1704 %args = @args;
31             }
32              
33 23         826 my %DEFAULTS = (
34             host => '127.0.0.1',
35             port => 8125,
36             proto => 'udp',
37             prefix => '',
38             autoflush => 1,
39             max_buffer_size => 512,
40             );
41              
42 23         770 foreach my $attr ( keys %DEFAULTS ) {
43 138 50       541 next if exists $args{$attr};
44 0         0 $args{$attr} = $DEFAULTS{$attr};
45             }
46              
47             $args{_socket} = IO::Socket::INET->new(
48             PeerAddr => $args{host},
49             PeerPort => $args{port},
50             Proto => $args{proto},
51 23 50       1952 ) or die "Failed to initialize socket: $!";
52              
53 23         22980 my $self = $class->SUPER::new( \%args );
54              
55 23         1015 $self->{_buffer} = '';
56              
57 23         218 return $self;
58             }
59              
60              
61             BEGIN {
62 24     24   72 my $class = __PACKAGE__;
63              
64 24         120 my %PROTOCOL = (
65             set_add => 's',
66             counter => 'c',
67             gauge => 'g',
68             histogram => 'h',
69             meter => 'm',
70             timing => 'ms',
71             );
72              
73 24         72 foreach my $name ( keys %PROTOCOL ) {
74              
75 24     24   192 no strict 'refs'; ## no critic (ProhibitNoStrict)
  24         48  
  24         3672  
76              
77 144         288 my $suffix = '|' . $PROTOCOL{$name};
78              
79 144         888 *{"${class}::${name}"} = sub {
80 27     27   17007151 my ( $self, $metric, $value, $rate ) = @_;
81 27 100 100     723 if ( ( defined $rate ) && ( $rate < 1 ) ) {
82 4 100       508 $self->_record( $suffix . '|@' . $rate, $metric, $value )
83             if rand() < $rate;
84             }
85             else {
86 23         515 $self->_record( $suffix, $metric, $value );
87             }
88 144         1104 };
89              
90             }
91              
92             # Alises for other Net::Statsd::Client or Etsy::StatsD
93              
94             {
95 24     24   168 no strict 'refs'; ## no critic (ProhibitNoStrict)
  24         48  
  24         1536  
  24         72  
96              
97 24         48 *{"${class}::update"} = \&counter;
  24         96  
98 24         48 *{"${class}::timing_ms"} = \&timing;
  24         8520  
99              
100             }
101              
102             }
103              
104             sub increment {
105 4     4 1 4001678 my ( $self, $metric, $rate ) = @_;
106 4         106 $self->counter( $metric, 1, $rate );
107             }
108              
109             sub decrement {
110 2     2 1 2000808 my ( $self, $metric, $rate ) = @_;
111 2         48 $self->counter( $metric, -1, $rate );
112             }
113              
114             sub _record {
115 24     24   187 my ( $self, $suffix, $metric, $value ) = @_;
116              
117 24         5241 my $data = $self->prefix . $metric . ':' . $value . $suffix . "\n";
118              
119 24 100       1288 if ( $self->autoflush ) {
120 18         806 send( $self->_socket, $data, 0 );
121 18         2930 return;
122             }
123              
124 6         148 my $avail = $self->max_buffer_size - length( $self->{_buffer} );
125 6 100       53 $self->flush if length($data) > $avail;
126              
127 6         34 $self->{_buffer} .= $data;
128             }
129              
130              
131              
132             sub flush {
133 2     2 1 13 my ($self) = @_;
134              
135 2 50       44 if ( length($self->{_buffer}) ) {
136 2         53 send( $self->_socket, $self->{_buffer}, 0 );
137 2         308 $self->{_buffer} = '';
138             }
139             }
140              
141             sub DEMOLISH {
142 0     0 0   my ( $self, $is_global ) = @_;
143              
144 0 0         return if $is_global;
145              
146 0           $self->flush;
147             }
148              
149              
150             1;
151              
152             __END__
153              
154             =pod
155              
156             =encoding UTF-8
157              
158             =head1 NAME
159              
160             Net::Statsd::Tiny - A tiny StatsD client that supports multimetric packets
161              
162             =head1 VERSION
163              
164             version v0.3.5
165              
166             =head1 SYNOPSIS
167              
168             use Net::Statsd::Tiny;
169              
170             my $stats = Net::Statsd::Tiny->new(
171             prefix => 'myapp.',
172             autoflush => 0,
173             max_buffer_size => 8192,
174             );
175              
176             ...
177              
178             $stats->increment('this.counter');
179              
180             $stats->set_add( $username ) if $username;
181              
182             $stats->timing( $run_time * 1000 );
183              
184             $stats->flush;
185              
186             =head1 DESCRIPTION
187              
188             This is a small StatsD client that supports the
189             L<StatsD Metrics Export Specification v0.1|https://github.com/b/statsd_spec>.
190              
191             It supports the following features:
192              
193             =over
194              
195             =item Multiple metrics can be sent in a single UDP packet.
196              
197             =item It supports the meter and histogram metric types.
198              
199             =back
200              
201             Note that the specification requires the measured values to be
202             integers no larger than 64-bits, but ideally 53-bits.
203              
204             The current implementation does not validate that the values you pass
205             to metrics conform to the spec, which allows you to take advantage of
206             extensions to some StatsD daemons. But the downside is that other
207             daemons may ignore those metrics.
208              
209             For simplicity, it will allow you to specify a sampling rate for any
210             metric, not just the ones where it is documented below. But again,
211             some daemons may ignore or reject this.
212              
213             =head1 ATTRIBUTES
214              
215             =head2 C<host>
216              
217             The host of the statsd daemon. It defaults to C<127.0.0.1>.
218              
219             =head2 C<port>
220              
221             The port that the statsd daemon is listening on. It defaults to
222             C<8125>.
223              
224             =head2 C<proto>
225              
226             The network protocol that the statsd daemon is using. It defaults to
227             C<udp>.
228              
229             =head2 C<prefix>
230              
231             The prefix to prepend to metric names. It defaults to a blank string.
232              
233             =head2 C<autoflush>
234              
235             A flag indicating whether metrics will be send immediately. It
236             defaults to true.
237              
238             When it is false, metrics will be saved in a buffer and only sent when
239             the buffer is full, or when the L</flush> method is called.
240              
241             Note that when this is disabled, you will want to flush the buffer
242             regularly at the end of each task (e.g. a website request or job).
243              
244             Not all StatsD daemons support receiving multiple metrics in a single
245             packet.
246              
247             =head2 C<max_buffer_size>
248              
249             Specifies the maximum buffer size. It defaults to C<512>.
250              
251             =head1 METHODS
252              
253             =head2 C<counter>
254              
255             $stats->counter( $metric, $value, $rate );
256              
257             This adds the C<$value> to the counter specified by the C<$metric>
258             name.
259              
260             If a C<$rate> is specified and less than 1, then a sampling rate will
261             be added. C<$rate> must be between 0 and 1.
262              
263             =head2 C<update>
264              
265             This is an alias for L</counter>, for compatability with
266             L<Etsy::StatsD> or L<Net::Statsd::Client>.
267              
268             =head2 C<increment>
269              
270             $stats->increment( $metric, $rate );
271              
272             This is an alias for
273              
274             $stats->counter( $metric, 1, $rate );
275              
276             =head2 C<decrement>
277              
278             $stats->decrement( $metric, $rate );
279              
280             This is an alias for
281              
282             $stats->counter( $metric, -1, $rate );
283              
284             =head2 C<metric>
285              
286             $stats->metric( $metric, $value );
287              
288             This is a counter that only accepts positive (increasing) values. It
289             is appropriate for counters that will never decrease (e.g. the number
290             of requests processed.) However, this metric type is not supported by
291             many StatsD daemons.
292              
293             =head2 C<gauge>
294              
295             $stats->gauge( $metric, $value );
296              
297             A gauge can be thought of as a counter that is maintained by the
298             client instead of the daemon, where C<$value> is a positive integer.
299              
300             However, this also supports gauge increment extensions. If the number
301             is prefixed by a "+", then the gauge is incremented by that amount,
302             and if the number is prefixed by a "-", then the gauge is decremented
303             by that amount.
304              
305             =head2 C<timing>
306              
307             $stats->timing( $metric, $value, $rate );
308              
309             This logs a "timing" in milliseconds, so that statistics about the
310             metric can be gathered. The C<$value> must be positive number,
311             although the specification recommends that integers be used.
312              
313             In actually, any values can be logged, and this is often used as a
314             generic histogram for non-timing values (especially since many StatsD
315             daemons do not support the L</histogram> metric type).
316              
317             If a C<$rate> is specified and less than 1, then a sampling rate will
318             be added. C<$rate> must be between 0 and 1. Note that sampling
319             rates for timings may not be supported by all statsd servers.
320              
321             =head2 C<timing_ms>
322              
323             This is an alias for L</timing>, for compatability with
324             L<Net::Statsd::Client>.
325              
326             =head2 C<histogram>
327              
328             $stats->histogram( $metric, $value );
329              
330             This logs a value so that statistics about the metric can be
331             gathered. The C<$value> must be a positive number, although the
332             specification recommends that integers be used.
333              
334             =head2 C<set_add>
335              
336             $stats->set_add( $metric, $string );
337              
338             This adds the the C<$string> to a set, for logging the number of
339             unique things, e.g. IP addresses or usernames.
340              
341             =head2 C<flush>
342              
343             This sends the buffer to the L</host> and empties the buffer, if there
344             is any data in the buffer.
345              
346             =head1 SEE ALSO
347              
348             L<Net::Statsd::Lite> which has a similar API but uses L<Moo> and
349             L<Type::Tiny> for data validation. It's also faster.
350              
351             L<https://github.com/b/statsd_spec>
352              
353             =head1 SOURCE
354              
355             The development version is on github at L<https://github.com/robrwo/Net-Statsd-Tiny>
356             and may be cloned from L<git://github.com/robrwo/Net-Statsd-Tiny.git>
357              
358             =head1 BUGS
359              
360             Please report any bugs or feature requests on the bugtracker website
361             L<https://github.com/robrwo/Net-Statsd-Tiny/issues>
362              
363             When submitting a bug or request, please include a test-file or a
364             patch to an existing test-file that illustrates the bug or desired
365             feature.
366              
367             =head1 AUTHOR
368              
369             Robert Rothenberg <rrwo@cpan.org>
370              
371             The initial development of this module was sponsored by Science Photo
372             Library L<https://www.sciencephoto.com>.
373              
374             =head1 CONTRIBUTOR
375              
376             =for stopwords Michael R. Davis
377              
378             Michael R. Davis <mrdvt@cpan.org>
379              
380             =head1 COPYRIGHT AND LICENSE
381              
382             This software is Copyright (c) 2018-2019 by Robert Rothenberg.
383              
384             This is free software, licensed under:
385              
386             The Artistic License 2.0 (GPL Compatible)
387              
388             =cut