File Coverage

blib/lib/Net/Statsd/Lite.pm
Criterion Covered Total %
statement 127 131 96.9
branch 17 22 77.2
condition n/a
subroutine 24 25 96.0
pod 5 7 71.4
total 173 185 93.5


line stmt bran cond sub pod time code
1             package Net::Statsd::Lite;
2              
3             # ABSTRACT: A StatsD client that supports multimetric packets
4              
5 30     30   1517082328 use v5.20;
  30         684  
6              
7 30     30   1394 use Moo 1.000000;
  30         13000  
  30         4079  
8              
9 30     30   49414 use Carp qw/ croak /;
  30         476  
  30         10451  
10 30     30   1183 use Devel::StrictMode;
  30         842  
  30         6512  
11 30     30   26227 use Digest::SHA 5.96 qw/ hmac_sha256_base64 /;
  30         169149  
  30         5306  
12 30     30   1111 use IO::Socket::IP;
  30         62208  
  30         1931  
13 30     30   63528 use MooX::TypeTiny;
  30         14994  
  30         254  
14 30     30   55413 use Ref::Util qw/ is_plain_hashref /;
  30         3231  
  30         4768  
15 30     30   278 use Scalar::Util qw/ refaddr /;
  30         114  
  30         4603  
16 30     30   920 use Sub::Quote qw/ quote_sub /;
  30         9782  
  30         4999  
17 30     30   233 use Sub::Util 1.40 qw/ set_subname /;
  30         1061  
  30         5252  
18 30         381 use Types::Common 2.000000 qw/ Bool Enum InstanceOf Int IntRange NonEmptySimpleStr
19             NumRange PositiveInt PositiveOrZeroInt PositiveOrZeroNum SimpleStr StrMatch Value
20 30     30   15559 /;
  30         8264434  
21              
22 30     30   184046 use namespace::autoclean;
  30         37664  
  30         1661  
23              
24 30     30   25918 use experimental qw/ signatures /;
  30         148677  
  30         316  
25              
26             # RECOMMEND PREREQ: Ref::Util::XS
27             # RECOMMEND PREREQ: Socket 2.026
28             # RECOMMEND PREREQ: Type::Tiny::XS
29              
30             our $VERSION = 'v0.11.1';
31              
32              
33             has host => (
34             is => 'ro',
35             isa => NonEmptySimpleStr,
36             default => '127.0.0.1',
37             );
38              
39              
40             has port => (
41             is => 'ro',
42             isa => IntRange[ 0, 65535 ],
43             default => 8125,
44             );
45              
46              
47             has proto => (
48             is => 'ro',
49             isa => Enum [qw/ tcp udp /],
50             default => 'udp',
51             );
52              
53              
54             has prefix => (
55             is => 'ro',
56             isa => SimpleStr,
57             default => '',
58             );
59              
60              
61             has autoflush => (
62             is => 'ro',
63             isa => Bool,
64             default => 1,
65             );
66              
67             my %Buffers;
68              
69              
70             has max_buffer_size => (
71             is => 'ro',
72             isa => PositiveInt,
73             default => 512,
74             );
75              
76              
77 24         60 has socket => (
78             is => 'lazy',
79             isa => InstanceOf ['IO::Socket'],
80 24     24   1132 builder => sub($self) {
  24         50  
81 24 50       1155 my $sock = IO::Socket::IP->new(
82             PeerHost => $self->host,
83             PeerService => $self->port,
84             Proto => $self->proto,
85             Type => SOCK_DGRAM,
86             ) or croak "Failed to initialize socket: $!";
87 24         26424 return $sock;
88             },
89             handles => { _send => 'send' },
90             init_arg => 'socket',
91             );
92              
93              
94 0         0 has secure_set_key => (
95             is => 'lazy',
96             isa => Value,
97 0     0   0 builder => sub($self) {
  0         0  
98 0         0 croak "secure_set_key has not been set";
99             },
100             );
101              
102              
103             BEGIN {
104 30     30   132 my $class = __PACKAGE__;
105              
106 30         32735 my %PROTOCOL = (
107             set_add => [ '|s', SimpleStr, ],
108             counter => [ '|c', Int, 1 ],
109             gauge => [ '|g', StrMatch[ qr{\A[\-\+]?[0-9]+\z} ] ],
110             histogram => [ '|h', PositiveOrZeroNum, 1 ],
111             meter => [ '|m', PositiveOrZeroNum ],
112             timing => [ '|ms', PositiveOrZeroNum, 1 ],
113             );
114              
115 30         253154 foreach my $name ( keys %PROTOCOL ) {
116              
117 180         196614 my $type = $PROTOCOL{$name}[1];
118 180         392 my $rate = $PROTOCOL{$name}[2];
119              
120 180         333 my $code = q{ my ($self, $metric, $value, $opts) = @_; };
121              
122 180 100       550 if (defined $rate) {
123 90         281 $code .= q[ $opts = { rate => $opts } unless is_plain_hashref($opts); ] .
124             q[ my $rate = $opts->{rate} // 1; ]
125             }
126             else {
127 90         269 $code .= q[ $opts //= {}; ];
128             }
129              
130 180         336 if (STRICT) {
131              
132 180         789 $code .= $type->inline_assert('$value');
133              
134 180 100       37197 if (defined $rate) {
135 90         543 my $range = NumRange[0,1];
136 90         124238 $code .= $range->inline_assert('$rate') . ';';
137             }
138             }
139              
140 180         32931 my $tmpl = $PROTOCOL{$name}[0];
141              
142 180 100       535 if ( defined $rate ) {
143              
144 90         266 $code .= q/ if ($rate<1) {
145             $self->record_metric( $tmpl . '|@' . $rate, $metric, $value, $opts )
146             if rand() <= $rate;
147             } else {
148             $self->record_metric( $tmpl, $metric, $value, $opts ); } /;
149             }
150             else {
151              
152 90         241 $code .= q{$self->record_metric( $tmpl, $metric, $value, $opts );};
153              
154             }
155              
156 180         1375 quote_sub "${class}::${name}", $code,
157             { '$tmpl' => \$tmpl },
158             { no_defer => 1 };
159              
160             }
161              
162             # Alises for other Net::Statsd::Client or Etsy::StatsD
163              
164             {
165 30     30   429965 no strict 'refs'; ## no critic (ProhibitNoStrict)
  30         133  
  30         6401  
  30         37727  
166              
167 30         319 *{"${class}::update"} = set_subname "update" => \&counter;
  30         188  
168 30         179 *{"${class}::timing_ms"} = set_subname "timing_ms" => \&timing;
  30         386  
169              
170             }
171              
172             }
173              
174 6     6 1 5006803 sub increment( $self, $metric, $opts = undef ) {
  6         22  
  6         120  
  6         52  
  6         45  
175 6         316 $self->counter( $metric, 1, $opts );
176             }
177              
178 2     2 1 2002222 sub decrement( $self, $metric, $opts = undef ) {
  2         40  
  2         8  
  2         40  
  2         6  
179 2         114 $self->counter( $metric, -1, $opts );
180             }
181              
182 2     2 1 2001423 sub secure_set_add( $self, $metric, $value, $opts = undef ) {
  2         60  
  2         8  
  2         6  
  2         5  
  2         6  
183 2         99 $self->set_add( $metric, hmac_sha256_base64( $value, $self->secure_set_key ), $opts );
184             }
185              
186              
187 29     29 1 17018254 sub record_metric( $self, $suffix, $metric, $value, $ ) {
  29         290  
  29         192  
  29         150  
  29         120  
  29         121  
188              
189 29 50       192 croak "malformed suffix" if $suffix =~ /[\n]/;
190 29 50       157 croak "malformed metric" if $metric =~ /[\N{U+00}-\N{U+1f}:|]/;
191 29 50       129 croak "malformed value" if $value =~ /[\N{U+00}-\N{U+1f}:|]/;
192              
193 29         219 my $data = $self->prefix . $metric . ':' . $value . $suffix . "\n";
194              
195 29 100       255 if ( $self->autoflush ) {
196 23         775 $self->_send( $data, 0 );
197 23         5178 return;
198             }
199              
200 6         13 my $index = refaddr $self;
201 6         22 my $avail = $self->max_buffer_size - length( $Buffers{$index} );
202              
203 6 100       21 $self->flush if length($data) > $avail;
204              
205 6         41 $Buffers{$index} .= $data;
206              
207             }
208              
209              
210 32     32 1 77 sub flush($self) {
  32         300  
  32         70  
211 32         110 my $index = refaddr $self;
212 32 100       227 if ( $Buffers{$index} ne '' ) {
213 3         87 $self->_send( $Buffers{$index}, 0 );
214 3         454 $Buffers{$index} = '';
215             }
216             }
217              
218 30     30 0 639302 sub BUILD($self, $) {
  30         107  
  30         63  
219 30         348 $Buffers{ refaddr $self } = '';
220             }
221              
222 30     30 0 3072231 sub DEMOLISH( $self, $is_global ) {
  30         122  
  30         102  
  30         64  
223              
224 30 50       146 return if $is_global;
225              
226 30         3319 $self->flush;
227              
228 30         826 delete $Buffers{ refaddr $self };
229             }
230              
231              
232             1;
233              
234             __END__