File Coverage

blib/lib/WebService/DataDog/Metric.pm
Criterion Covered Total %
statement 12 63 19.0
branch 0 42 0.0
condition 0 12 0.0
subroutine 4 7 57.1
pod 2 2 100.0
total 18 126 14.2


line stmt bran cond sub pod time code
1             package WebService::DataDog::Metric;
2              
3 1     1   45969 use strict;
  1         1  
  1         23  
4 1     1   3 use warnings;
  1         1  
  1         19  
5              
6 1     1   3 use base qw( WebService::DataDog );
  1         1  
  1         286  
7 1     1   6 use Carp qw( carp croak );
  1         2  
  1         552  
8              
9              
10              
11             =head1 NAME
12              
13             WebService::DataDog::Metric - Interface to Metric functions in DataDog's API.
14              
15             =head1 VERSION
16              
17             Version 1.0.3
18              
19             =cut
20              
21             our $VERSION = '1.0.3';
22              
23              
24             =head1 METHODS
25              
26              
27             =head2 post_metric()
28              
29             Deprecated. Please use emit() instead.
30              
31             =cut
32              
33             sub post_metric
34             {
35 0     0 1   my ( $self, %args ) = @_;
36            
37 0           carp "post_metric() is deprecated. Please use emit() instead.";
38            
39 0           return $self->emit( %args );
40             }
41              
42              
43             =head2 emit()
44              
45             Post single/multiple time-series metrics. NOTE: only metrics of type 'gauge'
46             and type 'counter' are supported. You must use a dogstatsd client such as
47             Net::Dogstatsd to post metrics of other types (ex: 'timer', 'histogram', 'sets'
48             or use increment() or decrement() on a counter). The primary advantage of the
49             API vs dogstatsd for posting metrics: API allows posting metrics from the past.
50              
51             Per DataDog: "The metrics end-point allows you to post metrics data so it
52             can be graphed on Datadog's dashboards."
53              
54             my $metric = $datadog->build('Metric');
55             $metric->emit(
56             name => $metric_name,
57             type => $metric_type, # Optional - gauge|counter. Default=gauge.
58             value => $metric_value, # For posting a single data point, time 'now'
59             data_points => $data_points, # 1+ data points, with timestamps
60             host => $hostname, # Optional - host that produced the metric
61             tags => $tag_list, # Optional - tags associated with the metric
62             );
63            
64             Examples:
65             + Submit a single point with a timestamp of `now`.
66             $metric->emit(
67             name => 'page_views',
68             value => 1000,
69             );
70            
71             + Submit a point with a timestamp.
72             $metric->emit(
73             name => 'my.pair',
74             data_points => [ [ 1317652676, 15 ] ],
75             );
76            
77             + Submit multiple points.
78             $metric->emit(
79             name => 'my.series',
80             data_points =>
81             [
82             [ 1317652676, 15 ],
83             [ 1317652800, 16 ],
84             ]
85             );
86            
87             + Submit a point with a host and tags.
88             $metric->emit(
89             name => 'my.series',
90             value => 100,
91             host => "myhost.example.com",
92             tags => [ "version:1" ],
93             );
94            
95            
96             Parameters:
97              
98             =over 4
99              
100             =item * name
101              
102             The metric name.
103              
104             =item * type
105              
106             Optional. Metric type. Allowed values: gauge, counter. Default = gauge.
107              
108             =item * value
109              
110             Metric value. Used when you only need to post a single data point, with
111             timestamp 'now'. Use 'data_points' to post a single metric with a timestamp.
112              
113             =item * data_points
114              
115             Array of arrays of timestamp and metric value.
116              
117             =item * host
118              
119             Optional. Host that generated the metric.
120              
121             =item * tags
122              
123             Optional. List of tags associated with the metric.
124              
125             =back
126              
127             =cut
128              
129             sub emit
130             {
131 0     0 1   my ( $self, %args ) = @_;
132 0           my $verbose = $self->verbose();
133            
134             # Perform various error checks before attempting to send metrics
135 0           $self->_error_checks( %args );
136            
137 0           my $data = {};
138 0           my $series =
139             {
140             # Force to lowercase because DataDog is case sensitive and we don't want to
141             # end up with multiple metrics of the same name, varying only in case.
142             metric => lc( $args{'name'} ),
143             };
144            
145 0 0         if ( defined $args{'type'} )
146             {
147 0           $series->{'type'} = $args{'type'};
148             }
149            
150 0 0         if ( defined $args{'value'} )
    0          
151             {
152 0           $series->{'points'} = [ [ time(), $args{'value'} ] ];
153             }
154             elsif ( defined $args{'data_points'} )
155             {
156 0           $series->{'points'} = $args{'data_points'};
157             }
158            
159 0 0         if ( defined $args{'host'} )
160             {
161             # Force to lowercase because DataDog is case sensitive and we don't want to
162             # tag metrics with hosts of the same name, varying only in case.
163 0           $series->{'host'} = lc( $args{'host'} );
164             }
165            
166 0 0         if ( defined $args{'tags'} )
167             {
168 0           $series->{'tags'} = $args{'tags'};
169             }
170            
171 0           $data->{'series'} = [ $series ];
172            
173 0           my $url = $WebService::DataDog::API_ENDPOINT . 'series';
174            
175 0           my $response = $self->_send_request(
176             method => 'POST',
177             url => $url,
178             data => $data,
179             );
180            
181             #TODO check that response contains "status:ok"
182            
183 0           return;
184             }
185              
186              
187             =head1 INTERNAL FUNCTIONS
188              
189             =head2 _error_checks()
190              
191             $self->_error_checks( %args );
192              
193             Common error checking for all metric types.
194              
195             =cut
196              
197             sub _error_checks
198             {
199 0     0     my ( $self, %args ) = @_;
200 0           my $verbose = $self->verbose();
201            
202             # Check for mandatory parameters
203 0           foreach my $arg ( qw( name ) )
204             {
205 0 0 0       croak "ERROR - Argument '$arg' is required."
206             if !defined( $args{$arg} ) || ( $args{$arg} eq '' );
207             }
208            
209             # One of these is required
210 0 0 0       if ( !defined $args{'value'} && !defined $args{'data_points'} )
211             {
212 0           croak "ERROR - You must specify argument 'value' for single data points, or argument 'data_points' for multiple data points";
213             }
214            
215             # You cannot specify both
216 0 0 0       if ( defined $args{'value'} && defined $args{'data_points'} )
217             {
218 0           croak "ERROR - You must specify argument 'value' for single data points, OR argument 'data_points' for multiple data points. Both arguments are not allowed.";
219             }
220            
221             # Metric name starts with a letter
222 0 0         if ( $args{'name'} !~ /^[a-zA-Z]/ )
223             {
224 0           croak( "ERROR - invalid metric name >" . $args{'name'} . "<. Names must start with a letter, a-z. Not sending." );
225             }
226            
227 0 0         if ( defined $args{'value'} )
228             {
229 0 0         croak "ERROR - Value >" . $args{'value'} . "< is not a number."
230             unless ( $args{'value'} =~ /^\d+(\.\d+)?$/ );
231             }
232            
233 0 0         if ( defined $args{'data_points'} )
234             {
235 0 0         croak "ERROR - invalid value for argument 'data_points', must be an arrayref."
236             unless Data::Validate::Type::is_arrayref( $args{'data_points'} );
237            
238             # Check that each data point is valid
239 0           foreach my $data_point ( @{ $args{'data_points'} } )
  0            
240             {
241 0 0         croak "ERROR - invalid value for argument 'data_points', must be an arrayref."
242             unless Data::Validate::Type::is_arrayref( $data_point );
243            
244 0           my $timestamp = $data_point->[0];
245 0           my $data_value = $data_point->[1];
246            
247 0 0         croak "ERROR - invalid timestamp >$timestamp< in data_points for >" . $args{'name'} . "<"
248             unless ( $timestamp =~ /^\d{10,}$/ ); #min 10 digits, allowing for older data back to 1/1/2000
249            
250 0 0         croak "ERROR - invalid value >$data_value< in data_points for >" . $args{'name'} . "<. Must be a number."
251             unless ( $data_value =~ /^\d+(\.\d+)?$/ );
252             }
253             }
254            
255             # Tags, if exist...
256 0 0 0       if ( defined( $args{'tags'} ) && scalar( $args{'tags'} ) != 0 )
257             {
258             # is valid
259 0 0         if ( !Data::Validate::Type::is_arrayref( $args{'tags'} ) )
260             {
261 0           croak "ERROR - invalid 'tags' value. Must be an arrayref.";
262             }
263            
264 0           foreach my $tag ( @{ $args{'tags'} } )
  0            
265             {
266             # must start with a letter
267 0 0         croak( "ERROR - invalid tag >" . $tag . "< on metric >" . $args{'name'} . "<. Tags must start with a letter, a-z. Not sending." )
268             if ( $tag !~ /^[a-zA-Z]/ );
269            
270             # must be 200 characters max
271 0 0         croak( "ERROR - invalid tag >" . $tag . "< on metric >" . $args{'name'} . "<. Tags must be 200 characters or less. Not sending." )
272             if ( length( $tag ) > 200 );
273            
274             # NOTE: This check isn't required by DataDog, they will allow this through.
275             # However, this tag will not behave as expected in the graphs, if we were to allow it.
276 0 0         croak( "ERROR - invalid tag >" . $tag . "< on metric >" . $args{'name'} . "<. Tags should only contain a single colon (:). Not sending." )
277             if ( $tag =~ /^\S+:\S+:/ );
278             }
279             }
280            
281 0           return;
282             }
283              
284              
285             1;