File Coverage

lib/Net/Statsite/Client.pm
Criterion Covered Total %
statement 36 62 58.0
branch 13 26 50.0
condition 0 3 0.0
subroutine 10 14 71.4
pod 7 7 100.0
total 66 112 58.9


line stmt bran cond sub pod time code
1             package Net::Statsite::Client;
2 1     1   558 use 5.008001;
  1         2  
3 1     1   3 use strict;
  1         1  
  1         20  
4 1     1   10 use warnings;
  1         2  
  1         39  
5              
6             our $VERSION = '1.1.0';
7              
8 1     1   3 use IO::Socket;
  1         1  
  1         6  
9 1     1   593 use Carp;
  1         1  
  1         643  
10              
11             =head1 NAME
12              
13             Net::Statsite::Client - Object-Oriented Client for L server
14              
15             =head1 SYNOPSIS
16              
17             use Net::Statsite::Client;
18             my $statsite = Net::Statsite::Client->new(
19             host => 'localhost',
20             prefix => 'test',
21             );
22              
23             $statsite->increment('item'); #increment key test.item
24              
25             =head1 DESCRIPTION
26              
27             Net::Statsite::Client is based on L but with new - C interface and C method.
28              
29              
30             =head1 METHODS
31              
32             =head2 new (host => $host, port => $port, sample_rate => $sample_rate, prefix => $prefix)
33              
34             Create a new instance.
35              
36             I - hostname of statsite server (default: localhost)
37              
38             I - port of statsite server (port: 8125)
39              
40             I - rate of sends metrics (default: 1)
41              
42             I - prefix metric name (default: '')
43              
44             I - protocol (default: 'udp')
45              
46             =cut
47              
48             sub new {
49 4     4 1 2644 my ($class, %options) = @_;
50 4 50       13 $options{host} = 'localhost' unless defined $options{host};
51 4 100       5 $options{port} = 8125 unless defined $options{port};
52 4 50       8 $options{prefix} = '' unless defined $options{prefix};
53 4 100       8 $options{proto} = 'udp' unless defined $options{proto};
54              
55 4 100       21 die "Invalid protocol '$options{proto}' (tcp|udp)" if $options{proto} !~ /^(?:tcp|udp)$/;
56              
57             my $sock = IO::Socket::INET->new(
58             PeerAddr => $options{host},
59             PeerPort => $options{port},
60             Proto => $options{proto},
61 3 50       15 ) or croak "Failed to initialize socket: $!";
62              
63 3         1191 bless { socket => $sock, sample_rate => $options{sample_rate}, prefix => $options{prefix} }, $class;
64             }
65              
66             =head2 timing(STAT, TIME, SAMPLE_RATE)
67              
68             Log timing information (should be in miliseconds)
69              
70             =cut
71              
72             sub timing {
73 1     1 1 319 my ($self, $stat, $time, $sample_rate) = @_;
74 1         4 $self->send({ $stat => "$time|ms" }, $sample_rate);
75             }
76              
77             =head2 increment(STATS, SAMPLE_RATE)
78              
79             Increment one of more stats counters.
80              
81             =cut
82              
83             sub increment {
84 1     1 1 435 my ($self, $stats, $sample_rate) = @_;
85 1         2 $self->update($stats, 1, $sample_rate);
86             }
87              
88             =head2 decrement(STATS, SAMPLE_RATE)
89              
90             Decrement one of more stats counters.
91              
92             =cut
93              
94             sub decrement {
95 1     1 1 425 my ($self, $stats, $sample_rate) = @_;
96 1         66 $self->update($stats, -1, $sample_rate);
97             }
98              
99             =head2 update(STATS, DELTA, SAMPLE_RATE)
100              
101             Update one of more stats counters by arbitrary amounts.
102              
103             =cut
104              
105             sub update {
106 5     5 1 1254 my ($self, $stats, $delta, $sample_rate) = @_;
107 5 100       12 $delta = 1 unless defined $delta;
108 5         5 my %data;
109 5 100       11 if (ref($stats) eq 'ARRAY') {
110 1         2 %data = map { $_ => "$delta|c" } @$stats;
  2         9  
111             }
112             else {
113 4         9 %data = ($stats => "$delta|c");
114             }
115 5         13 $self->send(\%data, $sample_rate);
116             }
117              
118             =head2 unique(STATS, ITEM, SAMPLE_RATE)
119              
120             Unique Set
121              
122             For example if you need count of unique ip adresses (per flush interval)
123             $stats->unique('ip.unique', $ip);
124              
125             =cut
126              
127             sub unique {
128 0     0 1   my ($self, $stats, $item, $sample_rate) = @_;
129 0           my %data = ($stats => "$item|s");
130 0           $self->send(\%data, $sample_rate);
131             }
132              
133             =head2 gauge(STATS, VALUE, SAMPLE_RATE)
134              
135             Gauge Set (Gauge, similar to kv but only the last value per key is retained)
136              
137             =cut
138              
139             sub gauge {
140 0     0 1   my ($self, $stats, $value, $sample_rate) = @_;
141 0           my %data = ($stats => "$value|g");
142 0           $self->send(\%data, $sample_rate);
143             }
144              
145             =head2 send(DATA, SAMPLE_RATE)
146              
147             Sending logging data; implicitly called by most of the other methods.
148              
149             =cut
150              
151             sub send {
152 0     0     my ($self, $data, $sample_rate) = @_;
153 0 0         $sample_rate = $self->{sample_rate} unless defined $sample_rate;
154              
155 0           my $sampled_data;
156 0 0 0       if (defined($sample_rate) and $sample_rate < 1) {
157 0           while (my ($stat, $value) = each %$data) {
158 0 0         $sampled_data->{$stat} = "$value|\@$sample_rate" if rand() <= $sample_rate;
159             }
160             }
161             else {
162 0           $sampled_data = $data;
163             }
164              
165 0 0         return '0 but true' unless keys %$sampled_data;
166              
167             #failures in any of this can be silently ignored
168 0           my $count = 0;
169 0           my $socket = $self->{socket};
170 0           while (my ($stat, $value) = each %$sampled_data) {
171              
172 0           my $key = $stat;
173 0 0         if ($$self{prefix}) {
174 0           $key = "$$self{ prefix }.$stat";
175             }
176              
177             #sanitize key (remove statsite separators)
178             #https://github.com/armon/statsite#protocol
179 0           $key =~ s/[:|\/]/_/g;
180              
181 0           _send_to_sock($socket, "$key:$value\n");
182 0           ++$count;
183             }
184 0           return $count;
185             }
186              
187             sub _send_to_sock( $$ ) {
188 0     0     my ($sock, $msg) = @_;
189 0           CORE::send($sock, $msg, 0);
190             }
191              
192             =head1 CONTRIBUTING
193              
194             the easiest way is use docker (L - with L and L)
195              
196             or L and C itself (commands after C<../perl-extended>)
197              
198             carton (aka ruby bundle) for fetch dependency
199              
200             docker run -v $PWD:/tmp/app -w /tmp/app avastsoftware/perl-extended carton
201              
202             and minil test for tests and regenerate meta and readme
203              
204             docker run -v $PWD:/tmp/app -w /tmp/app avastsoftware/perl-extended carton exec minil test
205              
206              
207             =head1 LICENSE
208              
209             Copyright (C) Avast Software.
210              
211             This library is free software; you can redistribute it and/or modify
212             it under the same terms as Perl itself.
213              
214             =head1 AUTHOR
215              
216             Jan Seidl Eseidl@avast.comE
217              
218             =cut
219              
220             1;