File Coverage

blib/lib/WebService/DataDog.pm
Criterion Covered Total %
statement 32 71 45.0
branch 2 38 5.2
condition 1 15 6.6
subroutine 10 13 76.9
pod 3 3 100.0
total 48 140 34.2


line stmt bran cond sub pod time code
1             package WebService::DataDog;
2              
3 37     37   761168 use strict;
  37         57  
  37         960  
4 37     37   126 use warnings;
  37         43  
  37         669  
5              
6 37     37   5372 use Data::Dumper;
  37         62544  
  37         1750  
7 37     37   19575 use LWP::UserAgent qw();
  37         1042634  
  37         800  
8 37     37   241 use HTTP::Request qw();
  37         47  
  37         391  
9 37     37   21088 use JSON qw();
  37         300725  
  37         756  
10 37     37   14587 use Class::Load qw();
  37         669795  
  37         900  
11 37     37   252 use Carp qw( carp croak );
  37         48  
  37         1403  
12 37     37   5088 use Data::Validate::Type qw();
  37         63708  
  37         21350  
13              
14              
15             our $API_ENDPOINT = "https://app.datadoghq.com/api/v1/";
16              
17              
18             =head1 NAME
19              
20             WebService::DataDog - Interface to DataDog's REST API.
21              
22              
23             =head1 VERSION
24              
25             Version 1.0.1
26              
27             =cut
28              
29             our $VERSION = '1.0.1';
30              
31              
32             =head1 SYNOPSIS
33              
34             This module allows you to interact with DataDog, a service that will "Capture
35             metrics and events, then graph, filter, and search to see what's happening and
36             how systems interact." This module encapsulates all the communications with the
37             REST API provided by DataDog to offer a Perl interface to metrics, dashboards,
38             events, alerts, etc.
39              
40             Requests that write data require reporting access and require an API key.
41             Requests that read data require full access and additionally require an
42             application key.
43              
44             use WebService::DataDog;
45              
46             # Create an object to communicate with DataDog
47             my $datadog = WebService::DataDog->new(
48             api_key => 'your_api_key_here',
49             application_key => 'your_application_key',
50             );
51              
52             # For metrics functions, first build a metrics object
53             my $metric = $datadog->build('Metric');
54              
55             # To post metrics (past or present)
56             # NOTE: only use 'value' OR 'data_points', but not both.
57             $metric->emit(
58             name => $metric_name,
59             type => $metric_type, # Optional - gauge|counter. Default=gauge.
60             value => $metric_value, # For posting a single data point, time 'now'
61             data_points => $data_points, # 1+ data points, with timestamps
62             host => $hostname, # Optional - host that produced the metric
63             tags => $tag_list, # Optional - tags associated with the metric
64             );
65              
66             # For dashboard functions, first build a dashboard object
67             my $dashboard = $datadog->build('Dashboard');
68              
69             # Create a new dashboard
70             my $dashboard_id = $dashboard->create(
71             title => $dash_title,
72             description => $dash_description,
73             graphs => $graphs,
74             );
75              
76             # Delete a user-created dashboard that you don't need anymore
77             $dashboard->delete( id => $dash_id );
78              
79             # To make any changes to an existing user-created dashboard:
80             # Specify dash_id and any combination of title, description, graphs
81             $dashboard->update(
82             id => $dash_id,
83             title => $dash_title,
84             description => $dash_description,
85             graphs => $graphs,
86             );
87              
88             # For event functions, first build an event object
89             my $event = $datadog->build('Event');
90              
91             # To search the event stream
92             my $event_list = $event->search(
93             start => $start_time,
94             end => $end_time, # Optional - default 'now'
95             priority => $priority, # Optional - low|normal
96             sources => $sources, # Optional - list of sources. Ex: Datadog, Github, Pingdom, Webmetrics
97             tags => $tag_list, # Optional - list of tags associated with the event
98             );
99              
100             # Find all events in the last 48 hours.
101             my $event_list = $event->search(
102             start => time() - ( 48 * 60 * 60 ),
103             );
104              
105             # To get all details of a specific event
106             my $event_data = $event->retrieve( id => $event_id );
107              
108             # To post a new event to the event stream
109             $event->create(
110             title => $event_title,
111             text => $event_text, # Body/Description of the event.
112             date_happened => $timestamp, # Optional, default "now"
113             priority => $priority, # Optional. normal|low
114             related_event_id => $event_id, # Optional, id of aggregate event
115             tags => $tag_list, # Optional - tags to apply to event (easy to search by)
116             alert_type => $alert_type, # Optional. error|warning|info|success
117             aggregation_key => $agg_key, # Optional. Arbitrary string to use for aggregation.
118             source_type_name => $source_type, # Optional. nagios|hudson|jenkins|user|my apps|feed|chef|puppet|git|bitbucket|fabric|capistrano
119             );
120              
121             # Submit a user event, with timestamp of `now`.
122             $event->create(
123             title => 'Test event',
124             text => 'Testing posting to event stream',
125             source_type_name => 'user',
126             );
127              
128             # For alert functions, first build an alert object
129             my $alert = $datadog->build('Alert');
130              
131             # Get list, with details, of all alerts
132             my $alert_list = $alert->retrieve_all();
133              
134             # Create a new alert
135             my $alert_id = $alert->create(
136             query => $query, # Metric query to alert on
137             name => $alert_name, # Optional. default=dynamic, based on query
138             message => $message, # Optional. default=None
139             silenced => $boolean, # Optional. default=0
140             );
141              
142             # Retrieve details on a specific alert
143             my $alert_data = $alert->retrieve( id => $alert_id );
144              
145             # Update an existing alert
146             $alert->update(
147             id => $alert_id, # ID of alert to modify
148             query => $query, # Metric query to alert on
149             name => $alert_name, # Optional.
150             message => $message, # Optional.
151             silenced => $boolean, # Optional.
152             );
153              
154             # Mute all alerts at once. Example usage: system maintenance.
155             $alert->mute_all();
156              
157             # Unmute all alerts at once. Example usage: completed system maintenance.
158             $alert->unmute_all();
159              
160             # For tag functions, first build a tag object
161             my $tag = $datadog->build('Tag');
162              
163             # Retrieve a mapping of tags to hosts.
164             my $tag_host_list = $tag->retrieve_all();
165              
166             # Return a list of tags for the specified host.
167             my $tag_list = $tag->retrieve( host => $host_name_or_id );
168              
169             # Update tags for specified host.
170             $tag->update(
171             host => $host, # name/ID of host to modify
172             tags => $tag_list, # Updated full list of tags to apply to host
173             );
174              
175             # Add tags to specified host.
176             $tag->add(
177             host => $host, # name/ID of host to modify
178             tags => $tag_list, # Updated full list of tags to apply to host
179             );
180              
181             # Delete all tags from the specified host.
182             $tag->delete( host => $host );
183              
184             # For search, first build a search object
185             my $search = $datadog->build('Search');
186              
187             my $search_results = $search->retrieve(
188             term => $search_term,
189             facet => [ 'hosts', 'metrics' ] #optional
190             );
191              
192             =cut
193              
194              
195             =head1 METHODS
196              
197             =head2 new()
198              
199             Create a new DataDog object that will be used as the interface with
200             DataDog's API
201              
202             use WebService::DataDog;
203              
204             # Create an object to communicate with DataDog
205             my $datadog = WebService::DataDog->new(
206             api_key => 'your_api_key_here',
207             application_key => 'your_application_key',
208             verbose => 1,
209             );
210              
211             Creates a new object to communicate with DataDog.
212              
213             Parameters:
214              
215             =over 4
216              
217             =item * api_key
218              
219             DataDog API key. Found at L
220              
221             =item * application_key
222              
223             DataDog application key. Multiple keys can be generated per account. Generate/View existing at
224             L
225              
226             =item * verbose
227              
228             Optional. Set to 1 to see debugging output of request/response interaction with DataDog service.
229              
230             =back
231              
232             =cut
233              
234             sub new
235             {
236 1     1 1 9 my ( $class, %args ) = @_;
237              
238             # Check for mandatory parameters
239 1         2 foreach my $arg ( qw( api_key application_key ) )
240             {
241 2 50 33     11 croak "Argument '$arg' is required to create the WebService::DataDog object"
242             if !defined( $args{$arg} ) || ( $args{$arg} eq '' );
243             }
244              
245             # Create the object
246 1 50       6 my $self = bless(
247             {
248             api_key => $args{'api_key'},
249             application_key => $args{'application_key'},
250             verbose => defined $args{'verbose'} ? $args{'verbose'} : 0,
251             },
252             $class,
253             );
254              
255 1         2 return $self;
256             }
257              
258              
259             =head2 build()
260              
261             Create a WebService::DataDog::* object with the correct connection parameters.
262              
263             # Use the factory to get a WebService::DataDog::* object with
264             # the correct DataDog connection parameters.
265             my $metric = $datadog->build( 'Metric' );
266              
267             Parameters:
268              
269             =over
270              
271             =item *
272              
273             The submodule name, such as Metric for WebService::DataDog::Metric.
274              
275             =back
276              
277             =cut
278              
279             sub build
280             {
281 0     0 1   my ( $self, $module ) = @_;
282              
283             # Check required arguments
284 0 0 0       croak 'Please specify the name of the module to build'
285             if !defined( $module ) || ( $module eq '' );
286              
287             # Load the class corresponding to the submodule requested.
288 0           my $class = __PACKAGE__ . '::' . $module;
289 0 0         Class::Load::load_class( $class ) || croak "Failed to load $class, double-check the class name";
290              
291             # Instantiate a new object of that class. Since it's a subclass
292             # of WebService::DataDog, we pass all the non-hidden properties
293             # of the datadog object to build it.
294 0           my $object = $class->new(
295 0           map { $_ => $self->{$_} }
296 0           grep { substr( $_, 0, 1 ) ne '_' }
297             keys %$self
298             );
299              
300 0           return $object;
301             }
302              
303              
304             =head2 verbose()
305              
306             Get or set the 'verbose' property.
307              
308             my $verbose = $self->verbose();
309             $self->verbose( 1 );
310              
311             =cut
312              
313             sub verbose
314             {
315 0     0 1   my ( $self, $value ) = @_;
316              
317 0 0 0       if ( defined $value && $value =~ /^[01]$/ )
318             {
319 0           $self->{'verbose'} = $value;
320             }
321             else
322             {
323 0           return $self->{'verbose'};
324             }
325              
326 0           return;
327             }
328              
329              
330              
331             =head1 RUNNING TESTS
332              
333             By default, only basic tests that do not require a connection to DataDog's
334             platform are run in t/.
335              
336             To run the developer tests, you will need to do the following:
337              
338             =over 4
339              
340             =item * Sign up to become a DataDog customer ( if you are not already), at
341             L. Free trial accounts are available.
342              
343             =item * Generate an application key at
344             L
345              
346             =back
347              
348             You can now create a file named DataDogConfig.pm in your own directory, with
349             the following content:
350              
351             package DataDogConfig;
352              
353             sub new
354             {
355             return
356             {
357             api_key => 'your_api_key',
358             application_key => 'your_application_key',
359             verbose => 0, # Enable this for debugging output
360             };
361             }
362              
363             1;
364              
365             You will then be able to run all the tests included in this distribution, after
366             adding the path to DataDogConfig.pm to your library paths.
367              
368              
369              
370              
371             =head1 INTERNAL METHODS
372              
373             =head2 _send_request()
374              
375              
376             =cut
377             sub _send_request ## no critic qw( Subroutines::ProhibitUnusedPrivateSubroutines )
378             {
379 0     0     my ( $self, %args ) = @_;
380 0           my $verbose = $self->verbose();
381              
382             # Check for mandatory parameters
383 0           foreach my $arg ( qw( data method url ) )
384             {
385 0 0 0       croak "Argument '$arg' is needed to send a request with the WebService::DataDog object"
386             if !defined( $args{$arg} ) || ( $args{$arg} eq '' );
387             }
388              
389 0           my $url = $args{'url'};
390 0           my $method = $args{'method'};
391              
392             # Add authentication info
393 0 0         if ( $url =~ /\?/ ) # Some endpoints will already have URL params...
394             {
395 0           $url .= '&api_key=' . $self->{'api_key'} . '&application_key=' . $self->{'application_key'};
396             }
397             else
398             {
399 0           $url .= '?api_key=' . $self->{'api_key'} . '&application_key=' . $self->{'application_key'};
400             }
401              
402 0           my $request;
403 0 0         if ( $method =~ /\A(?:GET|POST|DELETE|PUT)\z/x )
404             {
405 0           $request = HTTP::Request->new( $method => $url );
406             }
407             else
408             {
409 0           croak "The method >$method< is not supported. Not sending request.";
410             }
411              
412 0 0         carp "Sending request to URL >" . ( defined( $url ) ? $url : '' ) . "< via method >$method<"
    0          
413             if $verbose;
414              
415              
416 0           my $json_in = JSON::encode_json( $args{'data'} );
417 0 0         carp "Sending JSON request >" . ( defined( $json_in ) ? $json_in : '' ) . "<"
    0          
418             if $verbose;
419              
420 0           $request->content_type('application/json');
421 0           $request->content( $json_in );
422              
423 0 0         carp "Request object: ", Dumper( $request )
424             if $verbose;
425              
426 0           my $user_agent = LWP::UserAgent->new();
427 0           my $response = $user_agent->request($request);
428              
429 0 0         croak "Request failed:" . $response->status_line()
430             if !$response->is_success();
431              
432 0 0         carp "Response >" . ( defined( $response ) ? $response->content() : '' ) . "<"
    0          
433             if $verbose;
434              
435             # Try to parse JSON response, only if one was received.
436             # Some functions, such as Dashboard::delete(), Alert::mute_all, Alert::unmute_all()
437             # return nothing when successful, so there won't be anything to parse.
438 0 0 0       my $json_out = defined( $response ) && defined( $response->content() ) && $response->content() ne ''
439             ? JSON::decode_json( $response->content() )
440             : '';
441              
442 0 0         carp "JSON Response >" . ( defined( $json_out ) ? Dumper($json_out) : '' ) . "<"
    0          
443             if $verbose;
444              
445 0           return $json_out;
446             }
447              
448              
449              
450             =head1 AUTHOR
451              
452             Jennifer Pinkham, C<< >>.
453              
454              
455             =head1 BUGS
456              
457             Please report any bugs or feature requests to C,
458             or through the web interface at L.
459             I will be notified, and then you'll automatically be notified of progress on
460             your bug as I make changes.
461              
462              
463             =head1 SUPPORT
464              
465             You can find documentation for this module with the perldoc command.
466              
467             perldoc WebService::DataDog
468              
469              
470             You can also look for information at:
471              
472             =over 4
473              
474             =item * Github Bug/Issue tracker
475              
476             L
477              
478             =item * AnnoCPAN: Annotated CPAN documentation
479              
480             L
481              
482             =item * CPAN Ratings
483              
484             L
485              
486             =item * MetaCPAN
487              
488             L
489              
490             =back
491              
492              
493             =head1 ACKNOWLEDGEMENTS
494              
495             I originally developed this project for ThinkGeek (L).
496             Thanks for allowing me to open-source it!
497              
498             Special thanks for architecture advice, and code contributions, from Guillaume
499             Aubert L.
500              
501             =head1 COPYRIGHT & LICENSE
502              
503             Copyright 2015 Jennifer Pinkham.
504              
505             This program is free software: you can redistribute it and/or modify it under
506             the terms of the GNU General Public License version 3 as published by the Free
507             Software Foundation.
508              
509             This program is distributed in the hope that it will be useful, but WITHOUT ANY
510             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
511             PARTICULAR PURPOSE. See the GNU General Public License for more details.
512              
513             You should have received a copy of the GNU General Public License along with
514             this program. If not, see http://www.gnu.org/licenses/
515              
516             =cut
517              
518             1;