NAME
OpenTracing::Manual::Integration - For Framework or Integration Developers
DESCRIPTION
This part of the OpenTracing::Manual will describe how distributed traces are progressed from one service to the other, using the concept of carriers.
TABLE Of CONTENTS
- "Bootstrap a Implementation"
- "OpenTracing Carriers"
- "Extracting Context from an Incoming Request"
- "Inject Context into an Outgoing Request"
- "Testing your Framework Plugins"
INTRODUCTION
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 a specific Implementation, but those backends should be easilly swapped, without having too much impact on the code itself. All calls should follow the API.
Any framework plugin has the following responsabillities:
- Bootstrap a implenetation and GlobalTracer
- Extracting posible exisitng trace information to create a Context
On the otherhand, on the outgoing side of the server, the responsabillity is to:
THE DETAILS
Bootstrap a Implementation
Plugins should either use a generic way to bootstrap a implementation or tailord for just one implementation.
use MyFramework::Plugin::OpenTracing;
#
# Bootstrap implementation from the `OPENTRACING_IMPLEMENTATION`
# environment variable
or specifying it on the use
statement
use MyFramework::Plugin::OpenTracing ( 'SomeImplementation',
option => 'foo'
);
or having a tailord plugin:
use MyFrameWork::Plugin::OpenTracing::SomeImplementation;
But the latter approach requires writing multiple modules or subclasses.
Whatever way you choose, the responsabillity 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.
OpenTracing Carriers
The OpenTracing specification requires a Tracer implementation to understand how a SpanContext will be inject into or extract from a so called carrier or request.
The carrier formats required are text, http, and binary, but there is no standard on how thoses formats exactly look like and 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.
Extracting Context from an Incoming Request
my $http_headers = YourFramework->request->headers;
#
# as long as this is a HTTP::Headers object
my $root_context = $TRACER->extract_context(
OPENTRACING_CARRIER_FORMAT_HTTP_HEADERS => $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.
The SpanContext for a root-span is the incomming request that may or may not contain tracer information from its requestor. Use the extract_context
from a Tracer object
Since some implementations may use inmutable 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 initialisation time:
$TRACER->set_default_context_builder sub {
my $service_url = YourFramework->request->url;
my $service_type = 'WEB',
return {
tracer_service_endpoint => $service_url,
trecer_service_type => $service_type,
}
};
And as such the returned hashreference might be merged with extracted tracerinfo and used to create a complete SpanContext object.
But remember, such mechanisms are entirely dependent on the implmentation.
Inject Context into an Outgoing Request
my $span_context = $TRACER->get_active_span->get_context;
use HTTP::Headers;
my $http_headers = HTTP::Headers->new( ... );
my $cntx_headers =
$tracer->inject_context(
OPENTRACING_CARRIER_FORMAT_HTTP_HEADERS => $http_headers,
$opentracing_spancontext
);
my $request = HTTP::Request->new(
GET => 'https://...', $cntx_headers
);
my response = LWP::UserAgent->request( $request );
Testing your Framework Plugins
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.
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 { ... }
Crucial is that you use the Test Implementation. This will keep an in-memmory 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.
SEE ALSO
- OpenTracing::Interface
-
A role that defines the Tracer interface.
- OpenTracing::Manual
-
A quick overview about Perl5 and OpenTracing
- OpenTracing::Manual::Instrumentation
-
For Application developers and Devops.
- OpenTracing::Manual::Implementation
-
For Tracing Service Implementations
- OpenTracing::Manual::Ecosystem
-
An overview of the OpenTracing puzzle pieces.
- OpenTracing Overview
-
The OpenTracing API standard.
AUTHOR
Theo van Hoesel <tvanhoesel@perceptyx.com>
COPYRIGHT AND LICENSE
'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.