File Coverage

blib/lib/OpenTracing/Implementation/DataDog/Agent.pm
Criterion Covered Total %
statement 33 51 64.7
branch 0 2 0.0
condition n/a
subroutine 10 15 66.6
pod 2 2 100.0
total 45 70 64.2


line stmt bran cond sub pod time code
1             package OpenTracing::Implementation::DataDog::Agent;
2              
3             =head1 NAME
4              
5             OpenTracing::Implementation::DataDog::Agent - A Client that sends off the data
6              
7             =head1 SYNOPSIS
8              
9             use alias OpenTracing::Implementation::DataDog::Agent;
10            
11             my $dd_agent = Agent->new(
12             user_agent => LWP::UserAgent->new();
13             host => 'localhost',
14             port => '8126',
15             path => 'v0.3/traces',
16             ); # these are defaults
17              
18             and later:
19              
20             $dd_agent->send_span( $span );
21              
22             =cut
23              
24              
25              
26             =head1 DESCRIPTION
27              
28             The main responsabillity of this C<Agent> is to provide the C<send_span> method,
29             that will send the data to the local running DataDog agent.
30              
31             It does this by calling L<to_struct> that massages the generic OpenTracing data,
32             like C<baggage_items> from L<SpanContext> and C<tags> from C<Span>, together
33             with the DataDog specific data like C<resource_name>.
34              
35             This structure will be send of as a JSON string to the local installed DataDog
36             agent.
37              
38             =cut
39              
40              
41              
42             our $VERSION = 'v0.40.0.6-TRIAL';
43              
44 6     6   254982 use Moo;
  6         23420  
  6         35  
45 6     6   7318 use MooX::Attribute::ENV;
  6         59749  
  6         47  
46              
47 6     6   1305 use Carp;
  6         19  
  6         317  
48 6     6   3196 use HTTP::Request ();
  6         122121  
  6         227  
49 6     6   2950 use JSON::MaybeXS qw(JSON);
  6         34861  
  6         372  
50 6     6   4163 use LWP::UserAgent;
  6         148976  
  6         263  
51 6     6   1059 use PerlX::Maybe qw/maybe provided/;
  6         4971  
  6         48  
52 6     6   3674 use Types::Standard qw/HasMethods/;
  6         510047  
  6         90  
53              
54 6         3331 use OpenTracing::Implementation::DataDog::Utils qw(
55             nano_seconds
56 6     6   8579 );
  6         19  
57              
58              
59              
60             =head1 OPTIONAL ATTRIBUTES
61              
62             The attributes below can be set during instantiation, but none are required and
63             have sensible defaults, that may actually play nice with known DataDog
64             environment variables
65              
66             =cut
67              
68              
69              
70             =head2 C<user_agent>
71              
72             A HTTP User Agent that connects to the locally running DataDog agent. This will
73             default to a L<LWP::UserAgent>, but any User Agent will suffice, as long as it
74             has a required delegate method C<request>, that takes a L<HTTP::Request> object
75             and returns a L<HTTP::Response> compliant response object.
76              
77             =cut
78              
79             has user_agent => (
80             is => 'lazy',
81             isa => HasMethods[qw/request/],
82             handles => { send_http_request => 'request' },
83             );
84              
85             sub _build_user_agent {
86 0     0   0 return LWP::UserAgent->new( )
87             }
88              
89              
90              
91             =head2 C<host>
92              
93             The host-name where the DataDog agent is running, which defaults to
94             C<localhost> or the value of the C<DD_AGENT_HOST> environment variable if set.
95              
96             =cut
97              
98             has host => (
99             is => 'ro',
100             env_key => 'DD_AGENT_HOST',
101             default => 'localhost',
102             );
103              
104              
105              
106             =head2 C<port>
107              
108             The port-number the DataDog agent is listening at, which defaults to C<8126> or
109             the value of the C<DD_TRACE_AGENT_PORT> environment variable if set.
110              
111             =cut
112              
113             has port => (
114             is => 'ro',
115             env_key => 'DD_TRACE_AGENT_PORT',
116             default => '8126',
117             );
118              
119              
120              
121             =head2 C<path>
122              
123             The path the DataDog agent is expecting requests to come in, which defaults to
124             C<v0.3/traces>.
125              
126             =cut
127              
128             has path => (
129             is => 'ro',
130             default => 'v0.3/traces',
131             );
132             #
133             # maybe a 'version number' would be a better option ?
134              
135              
136              
137             has uri => (
138             is => 'lazy',
139             init_arg => undef,
140             );
141              
142             sub _build_uri {
143 0     0   0 my $self = shift;
144            
145 0         0 return "http://$self->{ host }:$self->{ port }/$self->{ path }"
146             }
147             #
148             # URI::Template is a nicer solution for this and more dynamic
149              
150              
151             has _json_encoder => (
152             is => 'lazy',
153             init_arg => undef,
154             handles => { json_encode => 'encode' },
155             );
156              
157             sub _build__json_encoder {
158 0     0   0 JSON()->new->utf8->canonical->pretty
159             }
160             #
161             # I just love readable and consistant JSONs
162              
163              
164              
165             =head1 DELEGATED INSTANCE METHODS
166              
167             The following method(s) are required by the L<DataDog::Tracer|
168             OpenTracing::Implementation::DataDog::Tracer>:
169              
170             =cut
171              
172              
173              
174             =head2 C<send_span>
175              
176             This method gets called by the L<DataDog::Tracer|
177             OpenTracing::Implementation::DataDog::Tracer> to send a L<Span> with its
178             specific L<DataDog::SpanContext|OpenTracing::Implementation::DataDog::Tracer>.
179              
180             This will typically get called during C<on_finish>.
181              
182             =head3 Required Positional Arguments
183              
184             =over
185              
186             =item C<$span>
187              
188             A L<OpenTracing Span|OpenTracing::Interface::Span> compliant object, that will
189             be serialised (using L<to_struct> and converted to JSON).
190              
191             =back
192              
193             =head3 Returns
194              
195             A boolean, that comes from L<< C<is_succes>|HTTP::Response#$r->is_success >>.
196              
197             =cut
198              
199             sub send_span {
200 0     0 1 0 my $self = shift;
201 0         0 my $span = shift;
202            
203 0         0 my $data = __PACKAGE__->to_struct( $span );
204            
205 0         0 my $resp = $self->_http_post_struct_as_json( [[ $data ]] );
206            
207 0         0 return $resp->is_success
208             }
209              
210              
211              
212             =head1 INSTANCE METHODS
213              
214             =cut
215              
216              
217              
218             =head2 C<to_struct>
219              
220             Gather required data from the span and it's context, tags and baggage items.
221              
222             =head3 Required Positional Arguments
223              
224             =over
225              
226             =item C<$span>
227              
228             =back
229              
230             =head3 Returns
231              
232             a hashreference with the following keys:
233              
234             =over
235              
236             =item C<trace_id>
237              
238             =item C<resource>
239              
240             =item C<service>
241              
242             =item C<type> (optional)
243              
244             =item C<span_id>
245              
246             =item C<name>
247              
248             =item C<start>
249              
250             =item C<duration>
251              
252             =item C<parent_id> (optional)
253              
254             =item C<error> (TODO)
255              
256             =item C<meta> (optional)
257              
258             =item C<metrics>
259              
260             =back
261              
262             =head3 Notes
263              
264             This data structure is specific for sending it through the DataDog agent and
265             therefore can not be a intance method of the DataDog::Span object.
266              
267             =cut
268              
269             sub to_struct {
270 1     1 1 332 my $class = shift;
271 1         3 my $span = shift;
272            
273 1         11 my $context = $span->get_context();
274            
275 1         24 my $meta_data = {
276             $span->get_tags,
277             $context->get_baggage_items,
278             };
279            
280 1         276 my $data = {
281             trace_id => $context->trace_id,
282             span_id => $context->span_id,
283             resource => $context->get_resource_name,
284             service => $context->get_service_name,
285            
286             maybe
287             type => $context->get_service_type,
288            
289             name => $span->get_operation_name,
290             start => nano_seconds( $span->start_time() ),
291             duration => nano_seconds( $span->duration() ),
292            
293             maybe
294             parent_id => $span->parent_span_id(),
295             #
296             # this method may get depricated, once moving to 'references'
297            
298             # error => ... ,
299            
300             provided %$meta_data,
301             meta => $meta_data,
302            
303             # metrics => ... ,
304             };
305            
306             # TODO: use Hash::Ordered, so we can control what will be the first item in
307             # the long string of JSON text. But this needs investigation on how
308             # this behaves with JSON
309            
310 1         24 return $data
311             }
312              
313              
314              
315             sub _http_post_struct_as_json {
316 0     0     my $self = shift;
317 0           my $struct = shift;
318            
319 0           my $encoded_data = $self->json_encode($struct);
320 0           do { warn "$encoded_data\n" }
321 0 0         if $ENV{OPENTRACING_DEBUG};
322            
323            
324 0           my $header = ['Content-Type' => 'application/json; charset=UTF-8'];
325 0           my $rqst = HTTP::Request->new( 'POST', $self->uri, $header, $encoded_data );
326            
327 0           my $resp = $self->send_http_request( $rqst );
328            
329 0           return $resp;
330             }
331              
332              
333              
334             =head1 SEE ALSO
335              
336             =over
337              
338             =item L<OpenTracing::Implementation::DataDog>
339              
340             Sending traces to DataDog using Agent.
341              
342             =item L<DataDog Docs API Tracing|https://docs.datadoghq.com/api/v1/tracing/>
343              
344             The DataDog B<Agent API> Documentation.
345              
346             =item L<LWP::UserAgent>
347              
348             Web user agent class
349              
350             =item L<JSON::Maybe::XS>
351              
352             Use L<Cpanel::JSON::XS> with a fallback to L<JSON::XS> and L<JSON::PP>
353              
354             =item L<HTTP::Request>
355              
356             HTTP style request message
357              
358             =item L<HTTP::Response>
359              
360             HTTP style response message
361              
362             =back
363              
364              
365              
366             =head1 AUTHOR
367              
368             Theo van Hoesel <tvanhoesel@perceptyx.com>
369              
370              
371              
372             =head1 COPYRIGHT AND LICENSE
373              
374             'OpenTracing::Implementation::DataDog'
375             is Copyright (C) 2019 .. 2020, Perceptyx Inc
376              
377             This library is free software; you can redistribute it and/or modify it under
378             the terms of the Artistic License 2.0.
379              
380             This package is distributed in the hope that it will be useful, but it is
381             provided "as is" and without any express or implied warranties.
382              
383             For details, see the full text of the license in the file LICENSE.
384              
385              
386             =cut
387              
388             1;