File Coverage

blib/lib/Net/Statsd/Tiny.pm
Criterion Covered Total %
statement 68 75 90.6
branch 15 24 62.5
condition 4 6 66.6
subroutine 15 16 93.7
pod 4 5 80.0
total 106 126 84.1


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   9454320 use v5.12;
  24         48  
6              
7 24     24   96 use warnings;
  24         48  
  24         2352  
8              
9 24     24   216 use parent qw/ Class::Accessor::Fast /;
  24         48  
  24         216  
10              
11 24     24   70056 use Carp ();
  24         72  
  24         360  
12 24     24   48 use IO::Socket::IP qw( SOCK_DGRAM );
  24         48  
  24         120  
13 24     24   2592 use Socket 2.026 ();
  24         528  
  24         7632  
14              
15             our $VERSION = 'v0.4.1';
16              
17              
18             __PACKAGE__->mk_ro_accessors(
19             qw/ host port proto prefix
20             autoflush max_buffer_size socket /
21             );
22              
23             sub new {
24 23     23 1 375337472 my ( $class, @args ) = @_;
25              
26 23         931 my %args;
27 23 50 33     2808 if ( ( @args == 1 ) && ( ref( $args[0] ) eq 'HASH' ) ) {
28 0         0 %args = %{ $args[0] };
  0         0  
29             }
30             else {
31 23         3306 %args = @args;
32             }
33              
34 23         1605 my %DEFAULTS = (
35             host => '127.0.0.1',
36             port => 8125,
37             proto => 'udp',
38             prefix => '',
39             autoflush => 1,
40             max_buffer_size => 512,
41             );
42              
43 23         1288 foreach my $attr ( keys %DEFAULTS ) {
44 138 50       1349 next if exists $args{$attr};
45 0         0 $args{$attr} = $DEFAULTS{$attr};
46             }
47              
48 23 50       675 if ( my $socket = delete $args{socket} ) {
49 0         0 $args{socket} = $socket;
50             }
51             else {
52             $args{socket} = IO::Socket::IP->new(
53             PeerHost => $args{host},
54             PeerService => $args{port},
55             Proto => $args{proto},
56 23 50       3068 Type => SOCK_DGRAM,
57             ) or die "Failed to initialize socket: $!";
58             }
59              
60 23         48160 my $self = $class->SUPER::new( \%args );
61              
62 23         2630 $self->{_buffer} = '';
63              
64 23         278 return $self;
65             }
66              
67              
68             BEGIN {
69 24     24   72 my $class = __PACKAGE__;
70              
71 24         120 my %PROTOCOL = (
72             set_add => 's',
73             counter => 'c',
74             gauge => 'g',
75             histogram => 'h',
76             meter => 'm',
77             timing => 'ms',
78             );
79              
80 24         72 foreach my $name ( keys %PROTOCOL ) {
81              
82 24     24   120 no strict 'refs'; ## no critic (ProhibitNoStrict)
  24         48  
  24         3024  
83              
84 144         192 my $suffix = '|' . $PROTOCOL{$name};
85              
86 144         408 *{"${class}::${name}"} = sub {
87 27     27   17014530 my ( $self, $metric, $value, $rate ) = @_;
88 27 100 100     1193 if ( ( defined $rate ) && ( $rate < 1 ) ) {
89 4 100       442 $self->_record( $suffix . '|@' . $rate, $metric, $value )
90             if rand() < $rate;
91             }
92             else {
93 23         441 $self->_record( $suffix, $metric, $value );
94             }
95 144         432 };
96              
97             }
98              
99             # Alises for other Net::Statsd::Client or Etsy::StatsD
100              
101             {
102 24     24   144 no strict 'refs'; ## no critic (ProhibitNoStrict)
  24         24  
  24         1800  
  24         48  
103              
104 24         24 *{"${class}::update"} = \&counter;
  24         72  
105 24         144 *{"${class}::timing_ms"} = \&timing;
  24         7560  
106              
107             }
108              
109             }
110              
111             sub increment {
112 4     4 1 4001991 my ( $self, $metric, $rate ) = @_;
113 4         31 $self->counter( $metric, 1, $rate );
114             }
115              
116             sub decrement {
117 2     2 1 2001087 my ( $self, $metric, $rate ) = @_;
118 2         58 $self->counter( $metric, -1, $rate );
119             }
120              
121             sub _record {
122 25     25   321 my ( $self, $suffix, $metric, $value ) = @_;
123              
124 25 50       1016 Carp::croak "malformed metric" if $metric =~ /[\N{U+00}-\N{U+1f}:|]/;
125 25 50       452 Carp::croak "malformed value" if $value =~ /[\N{U+00}-\N{U+1f}:|]/;
126              
127 25         7249 my $data = $self->prefix . $metric . ':' . $value . $suffix . "\n";
128              
129 25 100       1397 if ( $self->autoflush ) {
130 19         1193 $self->socket->send( $data, 0 );
131 19         3859 return;
132             }
133              
134 6         217 my $avail = $self->max_buffer_size - length( $self->{_buffer} );
135 6 100       76 $self->flush if length($data) > $avail;
136              
137 6         40 $self->{_buffer} .= $data;
138             }
139              
140              
141              
142             sub flush {
143 2     2 1 50 my ($self) = @_;
144              
145 2 50       13 if ( length($self->{_buffer}) ) {
146 2         96 $self->socket->send( $self->{_buffer}, 0 );
147 2         432 $self->{_buffer} = '';
148             }
149             }
150              
151             sub DEMOLISH {
152 0     0 0   my ( $self, $is_global ) = @_;
153              
154 0 0         return if $is_global;
155              
156 0           $self->flush;
157             }
158              
159              
160             1;
161              
162             __END__