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:

Inject the current Context into a 'Carrier'

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.