File Coverage

blib/lib/Net/Statsd.pm
Criterion Covered Total %
statement 70 83 84.3
branch 25 36 69.4
condition 7 11 63.6
subroutine 10 11 90.9
pod 6 6 100.0
total 118 147 80.2


line stmt bran cond sub pod time code
1             package Net::Statsd;
2             {
3             $Net::Statsd::VERSION = '0.12';
4             }
5              
6             # ABSTRACT: Perl client for Etsy's statsd daemon
7              
8 3     3   73491 use strict;
  3         7  
  3         103  
9 3     3   17 use warnings;
  3         7  
  3         99  
10 3     3   18 use Carp ();
  3         10  
  3         53  
11 3     3   2845 use IO::Socket ();
  3         89658  
  3         3304  
12              
13             our $HOST = 'localhost';
14             our $PORT = 8125;
15              
16             my $SOCK;
17             my $SOCK_PEER;
18              
19              
20              
21             sub timing {
22 1     1 1 1438 my ($name, $time, $sample_rate) = @_;
23              
24 1 50       8 if (! defined $sample_rate) {
25 1         2 $sample_rate = 1;
26             }
27              
28 1         11 my $stats = {
29             $name => sprintf "%d|ms", $time
30             };
31              
32 1         8 return Net::Statsd::send($stats, $sample_rate);
33             }
34              
35              
36             sub increment {
37 10003     10003 1 483326 my ($stats, $sample_rate) = @_;
38              
39 10003         19286 return Net::Statsd::update_stats($stats, 1, $sample_rate);
40             }
41              
42             *inc = *increment;
43              
44              
45             sub decrement {
46 0     0 1 0 my ($stats, $sample_rate) = @_;
47              
48 0         0 return Net::Statsd::update_stats($stats, -1, $sample_rate);
49             }
50              
51             *dec = *decrement;
52              
53              
54             sub update_stats {
55 10003     10003 1 15210 my ($stats, $delta, $sample_rate) = @_;
56              
57 10003 50       20371 if (! defined $delta) {
58 0         0 $delta = 1;
59             }
60              
61 10003 100       18441 if (! defined $sample_rate) {
62 2         6 $sample_rate = 1;
63             }
64              
65 10003 100       18631 if (! ref $stats) {
    50          
66 10002         20417 $stats = [ $stats ];
67             }
68             elsif (ref $stats eq 'HASH') {
69 0         0 Carp::croak("Usage: update_stats(\$str, ...) or update_stats(\\\@list, ...)");
70             }
71              
72 10003         13533 my %data = map { $_ => sprintf "%s|c", $delta } @{ $stats };
  10004         42898  
  10003         17593  
73              
74 10003         23279 return Net::Statsd::send(\%data, $sample_rate)
75             }
76              
77              
78             sub gauge {
79 3     3 1 9501 my $stats = {};
80              
81 3         25 while (my($name, $value) = splice(@_, 0, 2)) {
82 6 50       18 $value = 0 unless defined $value;
83             # Didn't use '%d' because values might be floats
84 6         12 push @{ $stats->{$name} }, sprintf("%s|g", $value);
  6         67  
85             }
86              
87 3         10 return Net::Statsd::send($stats, 1);
88             }
89              
90              
91             sub send {
92 10007     10007 1 15253 my ($data, $sample_rate) = @_;
93              
94 10007         18373 my $sampled_data = _sample_data($data, $sample_rate);
95              
96             # No sampled_data can happen when:
97             # 1) No $data came in
98             # 2) Sample rate was low enough that we don't want to send events
99 10007 100       21073 if (! $sampled_data) {
100 5079         15068 return;
101             }
102              
103             # Cache the socket to avoid dns and socket creation overheads
104             # (this boosts performance from ~6k to >60k sends/sec)
105 4928 100 66     33558 if (!$SOCK || !$SOCK_PEER || "$HOST:$PORT" ne $SOCK_PEER) {
      66        
106              
107             $SOCK = IO::Socket::INET->new(
108             Proto => 'udp',
109             PeerAddr => $HOST,
110             PeerPort => $PORT,
111 1 50       32 ) or do {
112             Carp::carp("Net::Statsd can't create a socket to $HOST:$PORT: $!")
113 0 0       0 unless our $_warn_once->{"$HOST:$PORT"}++;
114             return
115 0         0 };
116 1         9772 $SOCK_PEER = "$HOST:$PORT";
117              
118             # We don't want to die if Net::Statsd::send() doesn't work...
119             # We could though:
120             #
121             # or die "Could not create UDP socket: $!\n";
122             }
123              
124 4928         6480 my $all_sent = 1;
125              
126 4928         5391 keys %{ $sampled_data }; # reset iterator
  4928         9390  
127 4928         6798 while (my ($stat, $value) = each %{ $sampled_data }) {
  9859         28729  
128 4931         5817 my $packet;
129 4931 100       10649 if (ref $value eq 'ARRAY') {
130             # https://github.com/etsy/statsd/blob/master/docs/metric_types.md#multi-metric-packets
131 5         8 $packet = join("\n", map { "$stat:$_" } @{ $value });
  6         24  
  5         12  
132             }
133             else {
134             # Single value as scalar
135 4926         8479 $packet = "$stat:$value";
136             }
137             # send() returns the number of characters sent, or undef on error.
138 4931         51835 my $r = CORE::send($SOCK, $packet, 0);
139 4931 50       18874 if (!defined $r) {
    50          
140             #warn "Net::Statsd send error: $!";
141 0         0 $all_sent = 0;
142             }
143             elsif ($r != length($packet)) {
144             #warn "Net::Statsd send truncated: $!";
145 0         0 $all_sent = 0;
146             }
147             }
148              
149 4928         18462 return $all_sent;
150             }
151              
152              
153             sub _sample_data {
154 20009     20009   58712 my ($data, $sample_rate) = @_;
155              
156 20009 50 33     90232 if (! $data || ref $data ne 'HASH') {
157 0         0 Carp::croak("No data?");
158             }
159              
160 20009 100       37130 if (! defined $sample_rate) {
161 1         3 $sample_rate = 1;
162             }
163              
164             # Sample rate > 1 doesn't make sense though
165 20009 100       40457 if ($sample_rate >= 1) {
166 8         24 return $data;
167             }
168              
169 20001         22489 my $sampled_data;
170              
171             # Perform sampling here, so that clients using Net::Statsd
172             # don't have to do it every time. This is the same
173             # implementation criteria used in the other statsd client libs
174             #
175             # If rand() doesn't trigger, then no data will be sent
176             # to the statsd server, which is what we want.
177              
178 20001 100       42935 if (rand() <= $sample_rate) {
179 10009         12026 while (my ($stat, $value) = each %{ $data }) {
  25105         75497  
180             # Uglier, but if there's no data to be sampled,
181             # we get a clean undef as returned value
182 15096   100     42501 $sampled_data ||= {};
183              
184             # Multi-metric packet:
185             # https://github.com/etsy/statsd/blob/master/docs/metric_types.md#multi-metric-packets
186 15096 50       27306 if (ref $value eq 'ARRAY') {
187 0         0 foreach my $v ( @{ $value } ) {
  0         0  
188 0         0 push @{ $sampled_data->{$stat} }, sprintf("%s|@%s", $v, $sample_rate);
  0         0  
189             }
190             }
191             # Single value as scalar
192             else {
193 15096         76332 $sampled_data->{$stat} = sprintf "%s|@%s", $value, $sample_rate;
194             }
195             }
196             }
197              
198 20001         37657 return $sampled_data;
199             }
200              
201             1;
202              
203             __END__