NAME
OpenTracing::Manual::Implementation - For Tracing Service Implementations
DESCRIPTION
This part of the OpenTracing::Manual will describe how communication with the backend is established through some sort of Agent. This manual also provides information on how to extract_context
or inject_context
and carriers.
TABLE OF CONTENTS
- "Bootstrapping a Tracer Implementation"
- "Writing your own Implementation and using Roles"
- "Adding Implementation Specific Information to Traces"
- "Sending Span Information to a Service Provider Backend"
- "Propagating Tracer Information between Services"
- "OpenTracing Roles and Types"
- "Testing your Implementation"
INTRODUCTION
OpenTracing merely describes the API, see the OpenTracing::Interface documentation. It only requires that any implementation has a minimal set of methods that have a signature, or defined argument list. It is a deliberate choice to have the specification as POD and leaving the implementation to the Service Provider. The OpenTracing SDK for Perl however, comes with quite some useful tools to help building your own.
THE DETAILS
Bootstrapping a Tracer Implementation
Because of directory structure, Perl best practices and more, an implementation consists of several files, grouped under a single namespace. However, the API has no higher level definition of what an implementation is, it only speaks of the Tracer being the entry point of the API. It only looks more natural to be able to do things like:
use "OpenTracing::Implementation qw/YourServiceProvider/;
Which bootstraps the OpenTracing::GlobalTracer.
Or be more specific in your own code:
use aliased
"OpenTracing::Implementation::MyServiceProvider",
"Implementation" ;
my $tracer = Implementation->bootstrap_tracer( %options );
Although the 'Implementation' could do all sorts of things with that call, it basically is the same as:
use aliased
"OpenTracing::Implementation::MyServiceProvider::Tracer" ;
my $tracer = Tracer->new( %options );
Writing your own Implementation and using Roles
Since a lot of the responsibilities described in the <OpenTracing::Interface> are common across all implementations, there is a whole set of Moo::Roles files to quickly build your own classes.
package OpenTracing::Implementation::MyServiceProvider::Scope
use Moo;
...
with 'OpenTracing::Role::Scope'
1;
Look at OpenTracing::Roles to see what each of those roles provides.
Adding Implementation Specific Information to Traces.
The OpenTracing::Interface::SpanContext carries data across process boundaries. Specifically, it has two major components:
- An implementation-dependent state to refer to the distinct span within a trace
-
for example the implementing Tracer's definition of spanID and traceID
- Any Baggage Items
-
These are key:value pairs that cross process-boundaries. These may be useful to have some data available for access throughout the trace (https://opentracing.io/docs/overview/tags-logs-baggage/#baggage-items).
Depending on the purpose, it is most likely that you want to add additional information like a ServiceEndpoint to the SpanContext as 'private' attributes. As an implementor you do want to have a reliable way to persist that information. The BaggageItems can be altered at application level, as they are part of the 'public' API.
package OpenTracing::Implementation::MyServiceProvider::SpanContext
use Moo;
with 'OpenTracing::Role::SpanContext'
has service_endpoint => (
is => 'ro',
default => { 'index.cgi' },
isa => Str,
);
1;
As implementor, it's your own responsibility to send that information back to the service provider.
Sending Span Information to a Service Provider Backend
How information is being send back to a service provider backend is beyond the scope of this manual. There are different scenarios to do so. Some may want to collect a larger number of spans and send those straight to the backend. Others may have a locally installed agent that will gather spans coming from multiple threads and send them as a batch to the backend.
Either way, as a implementor, you will need to add to the Tracer a send method that will communicate with the outer world.
package OpenTracing::Implementation::MyServiceProvider::Tracer
use Moo;
with 'OpenTracing::Role::Tracer'
has your_agent => (
is => 'lazy',
isa => 'OpenTracing::Implementation::MyServiceProvider::Agent',
handles => qw/send_the_span/,
);
1;
Then, at the time you call finish
, calling such method as mentioned (send_the_span
) in the above example through a call back added as a on_finish
attribute, would transmit the span.
Propagating Tracer Information between Services
At the boundary or edges of an application, Frameworks use the two methods inject_context
and extract_context
(https://opentracing.io/docs/overview/tracers/#propagating-a-trace-with-inject-extract).
It is required that these methods are provided in the implementation. There are at least three OpenTracing required formats that need to be support. Only the OPENTRACING_CARRIER_FORMAT_HTTP_HEADERS
is being described in the manual.
An inject_context
might be implemented like:
package OpenTracing::Implementation::MyServiceProvider::Tracer
our $injectors = {
OPENTRACING_CARRIER_FORMAT_HTTP_HEADERS => sub {
my $http_headers = shift;
my $context = shift;
return $http_headers->clone(
X_YOUR_IMPLEMENTATION_TRACE_ID = $context->trace_id,
...
},
OPENTRACING_CARRIER_FORMAT_BINARY => sub { ... },
OPENTRACING_CARRIER_FORMAT_TEXT_MAP => sub { ... },
}
sub inject_tracer {
my $self = shift;
my $carrier_format = shift;
my $carrier;
croak "unsupported carrier format [$carrier_format]"
unless exists %$injectors{$carrier_format};
my $context = $self->get_active_span->get_context;
return $injectors->{$carrier_format}->($carrier, $context)
}
Where the X_YOUR_IMPLEMENTATION_TRACE_ID
is fully provider dependent. The other (micro) service you want to talk may be implemented using a complete different technology stack or language. But since (most likely) that service will use the same Distributed Tracing Backend, it expects the carrier to hold the trace information in a known format.
OpenTracing Roles and Types
The entire API is described in POD, it also provides a set of Roles. These roles can optionally be consumed to do all the type-checking for parameters, options, and returned results.
It also provides OpenTracing::Types. These duck-type checking types check that a object will at least have the methods described in the API. A isa
check will dictate a subclassing, which is what is deliberately avoided.
Testing your Implementation
There are a few tests available for Implementation developers. Those will check that the implementation is at least compliant with the OpenTracing::Interface and can be found at Test::OpenTracing::Interface.
use Test::Most;
use Test::OpenTracing::Interface::Span;
use YourImplementation::Span;
my $test_span = new_ok( 'YourImplementation::Span' => { %options },
"Created a Span object"
);
interface_can_ok( $test_span,
"... and can do all the required methods defined"
);
interface_lives_ok( $test_span,
"... and each method accepts described parameters and options"
);
interface_dies_ok( $test_span,
"... and will not tollerate bad input"
);
The latter one should work, but only if your implementation does do some sort of checking.
WARNING: If you do not check for the parameters and their types, please do check manually that the child_of
and references
options are mutual exclusive in start_active_span
and start_span
Testing that your implementation is executing the inject_context
and extract_context
correctly, is entirely up to you. Also, it is up to you to check that the correct span information is being send to the tracer backend at finish
.
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::Integration
-
For Framework or Integration Developers
- 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.