File Coverage

blib/lib/Net/Graphite.pm
Criterion Covered Total %
statement 58 82 70.7
branch 20 44 45.4
condition 7 15 46.6
subroutine 11 14 78.5
pod 5 7 71.4
total 101 162 62.3


line stmt bran cond sub pod time code
1             package Net::Graphite;
2 4     4   52362 use strict;
  4         5  
  4         90  
3 4     4   12 use warnings;
  4         4  
  4         86  
4 4     4   1507 use Errno qw(EINTR);
  4         3334  
  4         348  
5 4     4   17 use Carp qw/confess/;
  4         4  
  4         157  
6 4     4   1660 use IO::Socket::INET;
  4         56750  
  4         17  
7 4     4   1344 use Scalar::Util qw/reftype/;
  4         4  
  4         2511  
8              
9             $Net::Graphite::VERSION = '0.17';
10              
11             our $TEST = 0; # if true, don't send anything to graphite
12              
13             sub new {
14 6     6 0 1157 my $class = shift;
15 6 100 66     31 my %args = @_ == 1 && ref $_[0] eq 'HASH' ? %{$_[0]} : @_;
  1         4  
16 6         37 return bless {
17             host => '127.0.0.1',
18             port => 2003,
19             fire_and_forget => 0,
20             return_connect_error => 0,
21             proto => 'tcp',
22             timeout => 1,
23             # path
24             # transformer
25             %args,
26             # _socket
27             }, $class;
28             }
29              
30             sub send {
31 6     6 1 1991 my $self = shift;
32 6         6 my $value;
33 6 100       16 $value = shift if @_ % 2; # single value passed in
34 6         13 my %args = @_;
35              
36 6         5 my $plaintext;
37 6 100       11 if ($args{data}) {
38 3   33     15 my $xform = $args{transformer} || $self->transformer;
39 3 50       4 if ($xform) {
40 0         0 $plaintext = $xform->($args{data});
41             }
42             else {
43 3 100       9 if (ref $args{data}) {
44 2         8 my $reftype = reftype $args{data};
45              
46             # default transformers
47 2 50       5 if ($reftype eq 'HASH') {
48             # hash structure from Yves
49 2 100       5 my $start_path = $args{path} ? $args{path} : $self->path;
50 2         2 foreach my $epoch (sort {$a <=> $b} keys %{ $args{data} }) {
  2         6  
  2         9  
51 4         7 _fill_lines_for_epoch(\$plaintext, $epoch, $args{data}{$epoch}, $start_path);
52             }
53             }
54             # TODO - not sure what structure is most useful;
55             # an aref of [$path, $value, $epoch] seems a bit trivial?
56             # elsif ($reftype eq 'ARRAY') {
57             #
58             # }
59             # TODO
60             # elsif ($reftype eq 'CODE') {
61             # my $iter = $args{data};
62             # while (my $text = $iter->()) {
63             # $plaintext .= $text;
64             # }
65             # }
66             # how about sth of DBI? XML? maybe not
67             else {
68 0         0 confess "Arg 'data' passed to send method is a ref but has no plaintext transformer";
69             }
70             }
71             else {
72             # this obsoletes plaintext; just pass 'data' without a transformer
73 1         1 $plaintext = $args{data};
74             }
75             }
76             }
77             else {
78 3 100       8 $value = $args{value} unless defined $value;
79 3   66     11 my $path = $args{path} || $self->path;
80 3   66     13 my $time = $args{time} || time;
81              
82 3         7 $plaintext = "$path $value $time\n";
83             }
84              
85 6 50       14 $self->trace($plaintext) if $self->{trace};
86              
87 6 50       11 unless ($Net::Graphite::TEST) {
88 0 0       0 if ($self->connect()) {
89 0         0 my $buf = $plaintext;
90 0         0 while (length($buf)) {
91 0         0 my $res = $self->{_socket}->send($buf);
92 0 0       0 if (not defined $res) {
93 0 0       0 next if $! == EINTR;
94 0         0 last; # not sure what to do here
95             }
96              
97 0 0       0 last unless $res; # should never happen
98 0         0 substr($buf, 0, $res, '');
99             }
100             }
101             # I didn't close the socket!
102             }
103              
104 6         12 return $plaintext;
105             }
106              
107             sub _fill_lines_for_epoch {
108             # note: $in_out_str_ref is a reference to a string,
109             # not so much for performance but as an accumulator in this recursive function
110 28     28   19 my ($in_out_str_ref, $epoch, $hash, $path) = @_;
111              
112             # still in the "branches"
113 28 100       29 if (ref $hash) {
114 12         19 foreach my $key (sort keys %$hash) {
115 24         20 my $value = $hash->{$key};
116 24         31 _fill_lines_for_epoch($in_out_str_ref, $epoch, $value, "$path.$key");
117             }
118             }
119             # reached the "leaf" value
120             else {
121 16         35 $$in_out_str_ref .= "$path $hash $epoch\n";
122             }
123             }
124              
125             sub connect {
126 0     0 1 0 my $self = shift;
127             return $self->{_socket}
128 0 0 0     0 if $self->{_socket} && $self->{_socket}->connected;
129              
130             $self->{_socket} = IO::Socket::INET->new(
131             PeerHost => $self->{host},
132             PeerPort => $self->{port},
133             Proto => $self->{proto},
134             Timeout => $self->{timeout},
135 0         0 );
136              
137 0 0       0 unless ($self->{_socket}) {
138 0 0       0 if ($self->{return_connect_error}) {
    0          
139             # This is probably only used if you call $graphite->connect before ->send
140             # in order to check if there is a connection;
141             # otherwise, it'll just "forget" (without even "firing").
142 0         0 return;
143             }
144             elsif (not $self->{fire_and_forget}) {
145 0         0 confess "Error creating socket: $!";
146             }
147             }
148 0         0 return $self->{_socket};
149             }
150              
151             # if you need to close/flush for some reason
152             sub close {
153 0     0 1 0 my $self = shift;
154 0 0       0 return unless my $socket = delete $self->{_socket};
155 0         0 $socket->close();
156             }
157              
158             sub trace {
159 0     0 0 0 my (undef, $val_line) = @_;
160 0         0 print STDERR $val_line;
161             }
162              
163             ### mutators
164             sub path {
165 3     3 1 4 my ($self, $path) = @_;
166 3 50       7 $self->{path} = $path if defined $path;
167 3         6 return $self->{path};
168             }
169             sub transformer {
170 3     3 1 4 my ($self, $xform) = @_;
171 3 50       5 $self->{transformer} = $xform if defined $xform;
172 3         14 return $self->{transformer};
173             }
174              
175             1;
176             __END__