diff --git a/Etsy/StatsD.pm b/Etsy/StatsD.pm new file mode 100644 index 0000000..a78dd0b --- /dev/null +++ b/Etsy/StatsD.pm @@ -0,0 +1,125 @@ +package Etsy::StatsD; +use strict; +use warnings; +use IO::Socket; + +=head1 NAME + +Etsy::StatsD + +=head1 DESCRIPTION + +=cut + +=over + +=item new (HOST, PORT, SAMPLE_RATE) + +Create a new instance. + +=cut + +sub new { + my ($class, $host, $port, $sample_rate) = @_; + $host = 'localhost' unless defined $host; + $port = 8125 unless defined $port; + bless {host=>$host, port=>$port, sample_rate=>$sample_rate}, $class; +} + +=item timing(STAT, TIME, SAMPLE_RATE) + +Log timing information + +=cut + +sub timing { + my ($self, $stat, $time, $sample_rate) = @_; + $self->send({$stat => "$time|ms"}, $sample_rate); +} + +=item increment(STATS, SAMPLE_RATE) + +Increment one of more stats counters. + +=cut + +sub increment { + my ($self, $stats, $sample_rate) = @_; + $self->update($stats, 1, $sample_rate); +} + +=item increment(STATS, SAMPLE_RATE) + +Decrement one of more stats counters. + +=cut + +sub decrement { + my ($self, $stats, $sample_rate) = @_; + $self->update($stats, -1, $sample_rate); +} + +=item increment(STATS, DELTA, SAMPLE_RATE) + +Update one of more stats counters by arbitrary amounts. + +=cut + +sub update { + my ($self, $stats, $delta, $sample_rate) = @_; + $delta = 1 unless defined $delta; + my %data; + if (ref($stats) eq 'ARRAY') { + %data = map {$_ => "$delta|c"} @$stats; + } else { + %data = ($stats => "$delta|c"); + } + $self->send(\%data, $sample_rate); +} + +=item send(DATA, SAMPLE_RATE) + +Sending logging data; implicitly called by most of the other methods. + +=back + +=cut + +sub send { + my ($self, $data, $sample_rate) = @_; + $sample_rate = $self->{sample_rate} unless defined $sample_rate; + + my $sampled_data; + if ( defined($sample_rate) and $sample_rate < 1 ){ + while (my($stat,$value) = each %$sampled_data) { + $sampled_data->{$stat} = "$value|\@$sample_rate" if rand() <= $sample_rate; + } + } else { + $sampled_data = $data; + } + + return '0 but true' unless keys %$sampled_data; + + my $sock = new IO::Socket::INET( + PeerAddr => $self->{host}, + PeerPort => $self->{port}, + Proto => 'udp', + ) or return undef; + + #failures in any of this can be silently ignored + my $count=0; + while (my($stat,$value) = each %$sampled_data) { + print $sock "$stat:$value\n"; + ++$count; + } + close $sock; + return $count; +} + +=head1 AUTHOR + +Steve Sanbeg L + +=cut + +1; diff --git a/perl-example.pl b/perl-example.pl new file mode 100755 index 0000000..08b0e12 --- /dev/null +++ b/perl-example.pl @@ -0,0 +1,28 @@ +#! /usr/bin/perl + +use strict; +use warnings; +use Getopt::Long; +use lib '.'; +use Etsy::StatsD; + +my %opt; + +GetOptions(\%opt, 'host=s', 'port=s', 'sample=f', 'time=f', 'increment', 'decrement', 'update=i') or die; + +my $bucket = shift or die "Need to provide a bucket"; + +my $statsd = Etsy::StatsD->new($opt{host}, $opt{port}, $opt{rate}); +if ($opt{time}) { + $statsd->timing($bucket,$opt{time}); +} +if ($opt{increment}) { + $statsd->increment($bucket); +} +if ($opt{update}) { + $statsd->update($bucket, $opt{update}); +} +if ($opt{decrement}) { + $statsd->decrement($bucket); +} +