File Coverage

blib/lib/Graphite/Enumerator.pm
Criterion Covered Total %
statement 27 57 47.3
branch 6 24 25.0
condition 0 8 0.0
subroutine 7 11 63.6
pod 6 6 100.0
total 46 106 43.4


line stmt bran cond sub pod time code
1             package Graphite::Enumerator;
2              
3 1     1   698 use 5.14.1;
  1         4  
  1         43  
4 1     1   6 use Carp qw/croak/;
  1         3  
  1         62  
5 1     1   986 use LWP::UserAgent;
  1         60003  
  1         30  
6 1     1   10 use JSON;
  1         2  
  1         11  
7 1     1   171 use Scalar::Util 'reftype';
  1         2  
  1         1099  
8              
9             our $VERSION = '0.02';
10              
11             # Recognized constructor options:
12             # - host (base URL)
13             # - basepath (top-level metric to scan)
14             # - lwp_options (hashref)
15              
16             sub new {
17 1     1 1 886 my ($class, %args) = @_;
18 1 50       7 $args{host} or croak "No host provided";
19 1 50       6 $args{host} =~ m{^https?://} or $args{host} = "http://".$args{host};
20 1 50       6 $args{host} =~ m{/$} or $args{host} .= '/';
21 1 50       4 if (defined $args{basepath}) {
22 1 50       5 $args{basepath} =~ /\.$/ or $args{basepath} .= '.';
23             }
24             else {
25 0         0 $args{basepath} = '';
26             }
27 1         3 $args{_finder} = $args{host} . 'metrics/find?format=completer&query=';
28 1 50       3 $args{_ua} = LWP::UserAgent->new( %{ $args{lwp_options} || {} } );
  1         15  
29 1         3730 bless \%args, $class;
30             }
31              
32             sub enumerate {
33 0     0 1 0 my ($self, $callback, $path, $level) = @_;
34 0 0       0 if (reftype $callback ne "ARRAY") {
35 0         0 $callback = [ $callback ];
36             }
37 0   0     0 $path //= $self->{basepath};
38 0   0     0 $level //= 0;
39 0         0 my $url = $self->{_finder} . $path;
40 0         0 my $res = $self->{_ua}->get($url);
41 0 0       0 if ($res->is_success) {
42 0         0 my $completer_answer = eval { decode_json($res->content) };
  0         0  
43 0 0       0 if (!$completer_answer) {
44 0         0 $self->log_warning("URL <$url>: Couldn't decode JSON string: <" . $res->content . ">: $@");
45 0         0 return 0;
46             }
47 0 0       0 return 0 if !$completer_answer->{metrics};
48 0         0 for my $metric (@{ $completer_answer->{metrics} }) {
  0         0  
49 0 0 0     0 next if ($callback->[1] && $callback->[1]($metric->{path}, $level));
50 0 0       0 if ($metric->{is_leaf}) {
51 0         0 $callback->[0]($metric->{path}, $level);
52             }
53             else {
54 0         0 $self->enumerate($callback, $metric->{path}, $level + 1);
55             }
56             }
57 0         0 return 1;
58             }
59             else {
60 0         0 $self->log_warning("Can't get <$url>: " . $res->status_line);
61 0         0 return 0;
62             }
63             }
64              
65             sub host {
66 1     1 1 6 my ($self) = @_;
67 1         10 return $self->{host};
68             }
69              
70             sub ua {
71 0     0 1   my ($self) = @_;
72 0           return $self->{_ua};
73             }
74              
75             sub log_message {
76 0     0 1   my ($self, $message) = @_;
77 0           print $message, "\n";
78             }
79              
80             sub log_warning {
81 0     0 1   my ($self, $message) = @_;
82 0           warn $message, "\n";
83             }
84              
85             1;
86              
87             =head1 NAME
88              
89             Graphite::Enumerator - Utility module to recursively enumerate graphite metrics
90              
91             =head1 SYNOPSIS
92              
93             my $gren = Graphite::Enumerator->new(
94             host => 'https://graphite.example.com',
95             basepath => 'general.metrics',
96             lwp_options => {
97             env_proxy => 1,
98             keep_alive => 1,
99             },
100             );
101             $gren->enumerate(sub {
102             my ($path) = @_;
103             print "Found metric $path !\n";
104             });
105              
106             =head1 METHODS
107              
108             =head2 Graphite::Enumerator->new(%args)
109              
110             The constructor recognizes 3 arguments:
111              
112             host => host name (in that case, the protocol defaults to http) or base URL
113             basepath => top-level metric namespace to scan
114             lwp_options => hash of options to initialize LWP::UserAgent internally
115              
116             =head2 $g->enumerate($coderef)
117              
118             =head2 $g->enumerate([ $coderef, $filter_coderef ])
119              
120             Calls C<$coderef> for each metric under the basepath, with two parameters:
121             1. the metric name as a string; 2. the depth level of the metric relative
122             to the base path (starting at 0).
123              
124             If an array reference of 2 coderefs is provided, the second coderef will be
125             used as a an input filter called with the same parameters as above. This will
126             allow, for instance, to stop recursion on a given path by providing a regex, or
127             to stop recursion past a certain level. The code should return false to allow
128             further processing, and true, indicating a match, to prevent further processing
129             along that path.
130              
131             =head2 $g->host
132              
133             Returns the host passed to the constructor (with eventually
134             C prepended).
135              
136             =head2 $g->ua
137              
138             Returns the internal LWP::UserAgent object.
139              
140             =head2 $g->log_message($message)
141              
142             Prints the C<$message> to STDOUT.
143              
144             =head2 $g->log_warning($message)
145              
146             Warns about the C<$message>.
147              
148             =head1 ACKNOWLEDGMENT
149              
150             This module was originally developed for Booking.com.
151             With approval from Booking.com, this module was generalized
152             and put on CPAN, for which the author would like to express
153             his gratitude.
154              
155             =head1 AUTHOR
156              
157             Rafael Garcia-Suarez, Ergs@consttype.orgE
158              
159             This code is available under the same license as Perl version 5.10.1 or higher.
160              
161             A git repository for this module is available at L.
162              
163             =cut