OpenTracing::Manual::Integration - For Framework or Integration Developers
This part of the OpenTracing::Manual will describe how distributed traces are progressed from one service to the other, using the concept of carriers.
Most of the time, a framework will have some sort of architecture that allows to add plugins into the framework itself. These plugins, conveniently, bootstrap each application running the framework. But with OpenTracing, backends can be easily swapped, with only minor changes in the code itself. All calls should follow the API.
Any framework plugin has the following responsibilities:
On the other hand, on the outgoing side of the server, the responsibility is to:
Plugins should either use a generic way to bootstrap an implementation, or tailored for just one implementation.
use MyFramework::Plugin::OpenTracing; # # Bootstrap implementation from the `OPENTRACING_IMPLEMENTATION` # environment variable
or specifying it on the use statement
use
use MyFramework::Plugin::OpenTracing ( 'SomeImplementation', option => 'foo' );
or having a tailored plugin:
use MyFrameWork::Plugin::OpenTracing::SomeImplementation;
But the latter approach requires writing multiple modules or subclasses.
Whatever way you choose, the responsibility of the Framework Plugin is to set the OpenTracing::GlobalTracer such that it can be used inside the application.
use OpenTracing::Implementation qw/SomeImplementation/;
Or a more verbose way:
use OpenTracing::Implementation my $tracer = OpenTracing::Implementation->bootstrap_global_tracer( SomeImplementation, option_one => 'foo', option_two => 'bar', );
See OpenTracing::Implementation for more on how to bootstrap.
The OpenTracing specification requires a Tracer implementation to understand how a SpanContext will be injected into or extracted from a so-called carrier or request (https://opentracing.io/docs/overview/inject-extract/).
The carrier formats required are text, http, and binary, but there is no strict standard on how those formats look exactly look. The formats are implementation dependent.
For the time being, with this Perl implementation and definition, only HTTP::Headers will be used as a carrier. But once more, there is no definition on what HTTP Header information is used and how it is formatted.
The HTTP::Headers object is most common in Perl programming, and other variants like HTTP::Headers::Fast or HTTP:::Headers::Fast::XS share the same public interface. Most frameworks know how to handle those.
To extract the tracing context from an upstream service, you will need to provide it as an HTTP::Headers object.
my $http_headers = YourFramework->request->headers; # # as long as this is a HTTP::Headers object my $root_context = $TRACER->extract_context( $http_headers );
All Spans are part of a context. That is also true for the rootspan in a framework. Spans are started as a child_of a specific SpanContext.
child_of
The SpanContext for a root-span is the incoming request that may or may not contain tracer information from its requestor. Use the extract_context from a Tracer object
extract_context
Since some implementations may use immutable objects and may have required attributes for a SpanContext and since the entire API is being accessed through the Tracer object, you may need to use builders as part of the attributes. Such builders can bridge the gap between the framework and the implementation like so, at initialization time:
$TRACER->set_default_context_builder sub { my $service_url = YourFramework->request->url; my $service_type = 'WEB', return { tracer_service_endpoint => $service_url, tracer_service_type => $service_type, } };
And as such the returned hash reference might be merged with extracted tracer info and used to create a complete SpanContext object.
But remember, such mechanisms are entirely dependent on the implementation.
To provide your tracing context to a downstream service, create the HTTP::Headers object, then inject your current context into the headers before passing the request on to the microservice.
my $span_context = $TRACER->get_active_span->get_context; use HTTP::Headers; my $http_headers = HTTP::Headers->new( ... ); my $cntx_headers = $tracer->inject_context( $http_headers, $opentracing_spancontext ); my $request = HTTP::Request->new( GET => 'https://...', $cntx_headers ); my response = LWP::UserAgent->request( $request );
The OpenTracing specifications mention a CARRIER_FORMAT, which was used in an earlier version. But since the carrier type can be detected by perl, this has been removed form the Perl interface for the inject_context and extract_context definitions. (See also "Propagating Tracer Information between Services" in OpenTracing::Manual::Implementation for an example)
inject_context
To test that your Framework Plugin is doing the right thing, all you need to do is run it with the Test Implementation. And then compare the collected span information using globaltracer_cmp_deeply with a expected bag of Spans.
globaltracer_cmp_deeply
bag
use Test::Most; use Test::OpenTracing::Integration; my $test_application = Test::Application->new; my $http_request = HTTP::Request->new( ... ); lives_ok{ $test_application->execute_request( $http_request ); } "Can do request"; globaltracer_cmp_deeply [ ... ], "... and produced the expected spans"; done_testing; package Test::Application; use YourFramework; use YourFramework::Plugin::OpenTracing qw/Test/ sub execute_request { ... }
It is crucial is that you use the Test Implementation. This will keep an in-memory recording of all spans and have a additional get_recorded_trace to get the collected spans, which can be used to compare with expected spans.
get_recorded_trace
If you use DBI for database handling, you can
use DBIx::OpenTracing;
to automatically trace all database queries.
See DBIx::OpenTracing for details.
A role that defines the Tracer interface.
A quick overview about Perl5 and OpenTracing
For Application developers and Devops.
For Tracing Service Implementations
An overview of the OpenTracing puzzle pieces.
The OpenTracing API standard.
Theo van Hoesel <tvanhoesel@perceptyx.com>
'OpenTracing API for Perl' is Copyright (C) 2019 .. 2020, Perceptyx Inc
This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.
This library is distributed in the hope that it will be useful, but it is provided "as is" and without any express or implied warranties.
For details, see the full text of the license in the file LICENSE.
To install OpenTracing::Manual, copy and paste the appropriate command in to your terminal.
cpanm
cpanm OpenTracing::Manual
CPAN shell
perl -MCPAN -e shell install OpenTracing::Manual
For more information on module installation, please visit the detailed CPAN module installation guide.