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   1417766412 use v5.20;
  30         505  
6              
7 30     30   1282 use Moo 1.000000;
  30         8407  
  30         1603  
8              
9 30     30   36382 use Carp qw/ croak /;
  30         281  
  30         8259  
10 30     30   683 use Devel::StrictMode;
  30         608  
  30         5045  
11 30     30   19086 use Digest::SHA 5.96 qw/ hmac_sha256_base64 /;
  30         127288  
  30         10244  
12 30     30   753 use IO::Socket::IP;
  30         31672  
  30         1815  
13 30     30   49664 use MooX::TypeTiny;
  30         12552  
  30         190  
14 30     30   37241 use Ref::Util qw/ is_plain_hashref /;
  30         1870  
  30         3746  
15 30     30   224 use Scalar::Util qw/ refaddr /;
  30         67  
  30         3811  
16 30     30   633 use Sub::Quote qw/ quote_sub /;
  30         5627  
  30         4094  
17 30     30   217 use Sub::Util 1.40 qw/ set_subname /;
  30         716  
  30         4170  
18 30         299 use Types::Common 2.000000 qw/ Bool Enum InstanceOf Int IntRange NonEmptySimpleStr
19             NumRange PositiveInt PositiveOrZeroInt PositiveOrZeroNum SimpleStr StrMatch Value
20 30     30   12040 /;
  30         5820551  
21              
22 30     30   118608 use namespace::autoclean;
  30         14977  
  30         1562  
23              
24 30     30   18349 use experimental qw/ signatures /;
  30         112742  
  30         15233  
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.2';
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         50 has socket => (
78             is => 'lazy',
79             isa => InstanceOf ['IO::Socket'],
80 24     24   799 builder => sub($self) {
  24         44  
81 24 50       1334 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         24645 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   130 my $class = __PACKAGE__;
105              
106 30         144 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         219274 foreach my $name ( keys %PROTOCOL ) {
116              
117 180         133496 my $type = $PROTOCOL{$name}[1];
118 180         247 my $rate = $PROTOCOL{$name}[2];
119              
120 180         272 my $code = q{ my ($self, $metric, $value, $opts) = @_; };
121              
122 180 100       389 if (defined $rate) {
123 90         232 $code .= q[ $opts = { rate => $opts } unless is_plain_hashref($opts); ] .
124             q[ my $rate = $opts->{rate} // 1; ]
125             }
126             else {
127 90         201 $code .= q[ $opts //= {}; ];
128             }
129              
130 180         224 if (STRICT) {
131              
132 180         556 $code .= $type->inline_assert('$value');
133              
134 180 100       24996 if (defined $rate) {
135 90         401 my $range = NumRange[0,1];
136 90         83436 $code .= $range->inline_assert('$rate') . ';';
137             }
138             }
139              
140 180         21542 my $tmpl = $PROTOCOL{$name}[0];
141              
142 180 100       398 if ( defined $rate ) {
143              
144 90         184 $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         155 $code .= q{$self->record_metric( $tmpl, $metric, $value, $opts );};
153              
154             }
155              
156 180         1054 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   323349 no strict 'refs'; ## no critic (ProhibitNoStrict)
  30         71  
  30         4697  
  30         25389  
166              
167 30         207 *{"${class}::update"} = set_subname "update" => \&counter;
  30         113  
168 30         140 *{"${class}::timing_ms"} = set_subname "timing_ms" => \&timing;
  30         187  
169              
170             }
171              
172             }
173              
174 6     6 1 5001532 sub increment( $self, $metric, $opts = undef ) {
  6         90  
  6         280  
  6         20  
  6         27  
175 6         447 $self->counter( $metric, 1, $opts );
176             }
177              
178 2     2 1 2000935 sub decrement( $self, $metric, $opts = undef ) {
  2         80  
  2         6  
  2         9  
  2         6  
179 2         182 $self->counter( $metric, -1, $opts );
180             }
181              
182 2     2 1 2000584 sub secure_set_add( $self, $metric, $value, $opts = undef ) {
  2         48  
  2         8  
  2         5  
  2         7  
  2         3  
183 2         141 $self->set_add( $metric, hmac_sha256_base64( $value, $self->secure_set_key ), $opts );
184             }
185              
186              
187 29     29 1 17008531 sub record_metric( $self, $suffix, $metric, $value, $ ) {
  29         482  
  29         330  
  29         119  
  29         87  
  29         58  
188              
189 29 50       287 croak "malformed suffix" if $suffix =~ /[\n]/;
190 29 50       148 croak "malformed metric" if $metric =~ /[\N{U+00}-\N{U+1f}:|]/;
191 29 50       115 croak "malformed value" if $value =~ /[\N{U+00}-\N{U+1f}:|]/;
192              
193 29         228 my $data = $self->prefix . $metric . ':' . $value . $suffix . "\n";
194              
195 29 100       169 if ( $self->autoflush ) {
196 23         633 $self->_send( $data, 0 );
197 23         3277 return;
198             }
199              
200 6         11 my $index = refaddr $self;
201 6         18 my $avail = $self->max_buffer_size - length( $Buffers{$index} );
202              
203 6 100       16 $self->flush if length($data) > $avail;
204              
205 6         48 $Buffers{$index} .= $data;
206              
207             }
208              
209              
210 32     32 1 66 sub flush($self) {
  32         59  
  32         48  
211 32         68 my $index = refaddr $self;
212 32 100       228 if ( $Buffers{$index} ne '' ) {
213 3         49 $self->_send( $Buffers{$index}, 0 );
214 3         298 $Buffers{$index} = '';
215             }
216             }
217              
218 30     30 0 382484 sub BUILD($self, $) {
  30         65  
  30         51  
219 30         234 $Buffers{ refaddr $self } = '';
220             }
221              
222 30     30 0 3045179 sub DEMOLISH( $self, $is_global ) {
  30         73  
  30         93  
  30         77  
223              
224 30 50       124 return if $is_global;
225              
226 30         176 $self->flush;
227              
228 30         539 delete $Buffers{ refaddr $self };
229             }
230              
231              
232             1;
233              
234             __END__