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 3 66.6
total 45 71 63.3


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.7-TRIAL';
43              
44 6     6   260671 use Moo;
  6         24248  
  6         36  
45 6     6   7427 use MooX::Attribute::ENV;
  6         61750  
  6         52  
46              
47 6     6   1263 use Carp;
  6         19  
  6         310  
48 6     6   3240 use HTTP::Request ();
  6         125333  
  6         185  
49 6     6   3045 use JSON::MaybeXS qw(JSON);
  6         36090  
  6         388  
50 6     6   4379 use LWP::UserAgent;
  6         153082  
  6         264  
51 6     6   1181 use PerlX::Maybe qw/maybe provided/;
  6         5161  
  6         52  
52 6     6   3723 use Types::Standard qw/HasMethods/;
  6         523499  
  6         112  
53              
54 6         3523 use OpenTracing::Implementation::DataDog::Utils qw(
55             nano_seconds
56 6     6   8892 );
  6         26  
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 a single span and its 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<span_id>
239              
240             =item C<resource>
241              
242             =item C<service>
243              
244             =item C<type> (optional)
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 385 my $class = shift;
271 1         5 my $span = shift;
272            
273 1         18 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         275 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->get_parent_span_id(),
295            
296             # error => ... ,
297            
298             provided %$meta_data,
299             meta => $meta_data,
300            
301             # metrics => ... ,
302             };
303            
304             # TODO: use Hash::Ordered, so we can control what will be the first item in
305             # the long string of JSON text. But this needs investigation on how
306             # this behaves with JSON
307            
308 1         170 return $data
309             }
310              
311              
312              
313             sub http_post_struct_as_json {
314 0     0 0   my $self = shift;
315 0           my $struct = shift;
316            
317 0           my $encoded_data = $self->json_encode($struct);
318 0           do { warn "$encoded_data\n" }
319 0 0         if $ENV{OPENTRACING_DEBUG};
320            
321            
322 0           my $header = ['Content-Type' => 'application/json; charset=UTF-8'];
323 0           my $rqst = HTTP::Request->new( 'POST', $self->uri, $header, $encoded_data );
324            
325 0           my $resp = $self->send_http_request( $rqst );
326            
327 0           return $resp;
328             }
329              
330              
331              
332             =head1 SEE ALSO
333              
334             =over
335              
336             =item L<OpenTracing::Implementation::DataDog>
337              
338             Sending traces to DataDog using Agent.
339              
340             =item L<DataDog Docs API Tracing|https://docs.datadoghq.com/api/v1/tracing/>
341              
342             The DataDog B<Agent API> Documentation.
343              
344             =item L<LWP::UserAgent>
345              
346             Web user agent class
347              
348             =item L<JSON::Maybe::XS>
349              
350             Use L<Cpanel::JSON::XS> with a fallback to L<JSON::XS> and L<JSON::PP>
351              
352             =item L<HTTP::Request>
353              
354             HTTP style request message
355              
356             =item L<HTTP::Response>
357              
358             HTTP style response message
359              
360             =back
361              
362              
363              
364             =head1 AUTHOR
365              
366             Theo van Hoesel <tvanhoesel@perceptyx.com>
367              
368              
369              
370             =head1 COPYRIGHT AND LICENSE
371              
372             'OpenTracing::Implementation::DataDog'
373             is Copyright (C) 2019 .. 2020, Perceptyx Inc
374              
375             This library is free software; you can redistribute it and/or modify it under
376             the terms of the Artistic License 2.0.
377              
378             This package is distributed in the hope that it will be useful, but it is
379             provided "as is" and without any express or implied warranties.
380              
381             For details, see the full text of the license in the file LICENSE.
382              
383              
384             =cut
385              
386             1;