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 29     29   1312616343 use v5.20;
  29         606  
6              
7 29     29   784 use Moo 1.000000;
  29         3003  
  29         1732  
8              
9 29     29   38835 use Carp qw/ croak /;
  29         290  
  29         8790  
10 29     29   537 use Devel::StrictMode;
  29         270  
  29         5625  
11 29     29   21184 use Digest::SHA 5.96 qw/ hmac_sha256_base64 /;
  29         137467  
  29         4739  
12 29     29   314 use IO::Socket 1.18 ();
  29         1411  
  29         926  
13 29     29   14016 use MooX::TypeTiny;
  29         13545  
  29         280  
14 29     29   32261 use Ref::Util qw/ is_plain_hashref /;
  29         123  
  29         3641  
15 29     29   257 use Scalar::Util qw/ refaddr /;
  29         78  
  29         4213  
16 29     29   224 use Sub::Quote qw/ quote_sub /;
  29         100  
  29         4332  
17 29     29   188 use Sub::Util 1.40 qw/ set_subname /;
  29         1179  
  29         8982  
18 29         288 use Types::Common 2.000000 qw/ Bool Enum InstanceOf Int IntRange NonEmptySimpleStr
19             NumRange PositiveInt PositiveOrZeroInt PositiveOrZeroNum SimpleStr StrMatch Value
20 29     29   12872 /;
  29         6124360  
21              
22 29     29   120340 use namespace::autoclean;
  29         545  
  29         1000  
23              
24 29     29   19281 use experimental qw/ signatures /;
  29         94424  
  29         182  
25              
26             # RECOMMEND PREREQ: Ref::Util::XS
27             # RECOMMEND PREREQ: Socket 2.026
28             # RECOMMEND PREREQ: Type::Tiny::XS
29              
30             our $VERSION = 'v0.10.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 25         54 has _socket => (
77             is => 'lazy',
78             isa => InstanceOf ['IO::Socket::INET'],
79 25     25   354 builder => sub($self) {
  25         55  
80 25 50       934 my $sock = IO::Socket::INET->new(
81             PeerAddr => $self->host,
82             PeerPort => $self->port,
83             Proto => $self->proto,
84             ) or croak "Failed to initialize socket: $!";
85 25         16462 return $sock;
86             },
87             handles => { _send => 'send' },
88             );
89              
90              
91 0         0 has secure_set_key => (
92             is => 'lazy',
93             isa => Value,
94 0     0   0 builder => sub($self) {
  0         0  
95 0         0 croak "secure_set_key has not been set";
96             },
97             );
98              
99              
100             BEGIN {
101 29     29   106 my $class = __PACKAGE__;
102              
103 29         150 my %PROTOCOL = (
104             set_add => [ '|s', SimpleStr, ],
105             counter => [ '|c', Int, 1 ],
106             gauge => [ '|g', StrMatch[ qr{\A[\-\+]?[0-9]+\z} ] ],
107             histogram => [ '|h', PositiveOrZeroNum, 1 ],
108             meter => [ '|m', PositiveOrZeroNum ],
109             timing => [ '|ms', PositiveOrZeroNum, 1 ],
110             );
111              
112 29         215729 foreach my $name ( keys %PROTOCOL ) {
113              
114 174         130967 my $type = $PROTOCOL{$name}[1];
115 174         284 my $rate = $PROTOCOL{$name}[2];
116              
117 174         224 my $code = q{ my ($self, $metric, $value, $opts) = @_; };
118              
119 174 100       389 if (defined $rate) {
120 87         225 $code .= q[ $opts = { rate => $opts } unless is_plain_hashref($opts); ] .
121             q[ my $rate = $opts->{rate} // 1; ]
122             }
123             else {
124 87         189 $code .= q[ $opts //= {}; ];
125             }
126              
127 174         190 if (STRICT) {
128              
129 174         577 $code .= $type->inline_assert('$value');
130              
131 174 100       24310 if (defined $rate) {
132 87         419 my $range = NumRange[0,1];
133 87         81029 $code .= $range->inline_assert('$rate') . ';';
134             }
135             }
136              
137 174         21459 my $tmpl = $PROTOCOL{$name}[0];
138              
139 174 100       347 if ( defined $rate ) {
140              
141 87         177 $code .= q/ if ($rate<1) {
142             $self->record_metric( $tmpl . '|@' . $rate, $metric, $value, $opts )
143             if rand() <= $rate;
144             } else {
145             $self->record_metric( $tmpl, $metric, $value, $opts ); } /;
146             }
147             else {
148              
149 87         161 $code .= q{$self->record_metric( $tmpl, $metric, $value, $opts );};
150              
151             }
152              
153 174         1064 quote_sub "${class}::${name}", $code,
154             { '$tmpl' => \$tmpl },
155             { no_defer => 1 };
156              
157             }
158              
159             # Alises for other Net::Statsd::Client or Etsy::StatsD
160              
161             {
162 29     29   362717 no strict 'refs'; ## no critic (ProhibitNoStrict)
  29         99  
  29         4413  
  29         24012  
163              
164 29         175 *{"${class}::update"} = set_subname "update" => \&counter;
  29         144  
165 29         128 *{"${class}::timing_ms"} = set_subname "timing_ms" => \&timing;
  29         263  
166              
167             }
168              
169             }
170              
171 5     5 1 5002340 sub increment( $self, $metric, $opts = undef ) {
  5         68  
  5         83  
  5         73  
  5         16  
172 5         388 $self->counter( $metric, 1, $opts );
173             }
174              
175 2     2 1 2002313 sub decrement( $self, $metric, $opts = undef ) {
  2         8  
  2         61  
  2         5  
  2         5  
176 2         99 $self->counter( $metric, -1, $opts );
177             }
178              
179 2     2 1 2000644 sub secure_set_add( $self, $metric, $value, $opts = undef ) {
  2         85  
  2         11  
  2         7  
  2         7  
  2         5  
180 2         194 $self->set_add( $metric, hmac_sha256_base64( $value, $self->secure_set_key ), $opts );
181             }
182              
183              
184 29     29 1 18012014 sub record_metric( $self, $suffix, $metric, $value, $ ) {
  29         210  
  29         235  
  29         165  
  29         89  
  29         164  
185              
186 29 50       184 croak "malformed suffix" if $suffix =~ /[\n]/;
187 29 50       144 croak "malformed metric" if $metric =~ /[\N{U+00}-\N{U+1f}:|]/;
188 29 50       125 croak "malformed value" if $value =~ /[\N{U+00}-\N{U+1f}:|]/;
189              
190 29         200 my $data = $self->prefix . $metric . ':' . $value . $suffix . "\n";
191              
192 29 100       173 if ( $self->autoflush ) {
193 23         668 send( $self->_socket, $data, 0 );
194 23         1845 return;
195             }
196              
197 6         11 my $index = refaddr $self;
198 6         24 my $avail = $self->max_buffer_size - length( $Buffers{$index} );
199              
200 6 100       23 $self->flush if length($data) > $avail;
201              
202 6         43 $Buffers{$index} .= $data;
203              
204             }
205              
206              
207 31     31 1 66 sub flush($self) {
  31         54  
  31         92  
208 31         90 my $index = refaddr $self;
209 31 100       197 if ( $Buffers{$index} ne '' ) {
210 3         86 send( $self->_socket, $Buffers{$index}, 0 );
211 3         277 $Buffers{$index} = '';
212             }
213             }
214              
215 29     29 0 213431 sub BUILD($self, $) {
  29         69  
  29         48  
216 29         253 $Buffers{ refaddr $self } = '';
217             }
218              
219 29     29 0 2045830 sub DEMOLISH( $self, $is_global ) {
  29         84  
  29         85  
  29         47  
220              
221 29 50       127 return if $is_global;
222              
223 29         239 $self->flush;
224              
225 29         590 delete $Buffers{ refaddr $self };
226             }
227              
228              
229             1;
230              
231             __END__