The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Dancer2::Plugin::OpenTelemetry - Use OpenTelemetry in your Dancer2 app

SYNOPSIS

    use Dancer2;
    use Dancer2::Plugin::OpenTelemetry;

    # Or via config, probably in your config file
    BEGIN {
        set plugins => {
            OpenTelemetry => {
                tracer => {
                    # Tracer parameters, see below
                },
            },
        };
    }

    # Will generate a span named
    # GET /static/url
    get '/static/url' => sub { 'OK' };

    # Will use placeholders for reduced span cardinality
    # POST /url/with/:placeholder
    post '/url/with/:placeholder' => sub { 'OK' }

    # Use it also with async actions!
    get '/async' => sub {
        delayed {
            flush;
            content 'Hello ';
            content 'World';
            done;
        }
    };

    # Errors will be correctly captured in the span
    get '/error' => sub {
        die 'oops';
    };

    dance;

DESCRIPTION

This plugin allows a Dancer2 application to automatically generate telemetry data using the OpenTelemetry API. The plugin will add a "before" hook and "after" hook to capture telemetry data about handled requests in a OpenTelemetry::Trace::Span object. Errors will be captured via an "on_route_exception" hook, which will set the span status accordingly.

During the "before" hook, a server span will be created and injected into the current context, together with any propagation data retrieved from the incoming request headers by the currently installed propagator.

The value of the tracer parameter set in the plugin configuration will be used to obtain a tracer via "tracer" in OpenTelemetry::Trace::TracerProvider. Setting this is optional. If no value is set, the name will be read from the "OTEL_SERVICE_NAME" environment variable. If this variable is also not set, it will default to "dancer2". The name is the only parameter that has a default: all other values will be left unspecified.

The name of the generated span will be derived from the current request method, and the "spec_route" of the request's route, as in GET /foo/:bar, to reduce the span cardinality.

The span will be created with the following attributes, which will therefore be available for any sampling decision.

http.request.method

Set to the request method. It will be the same value that was concatenated to the route in the span's name.

network.protocol.version

Set to the version number of the request protocol.

url.path

Set to the path of the request URL. This will be the raw path, without using any placeholders. It will not include the query string.

url.scheme

Set to the scheme of the request URL.

http.route

Set to the "spec_route" of the request's route. This will use placeholders, and will be the same value that was concatenated to the method in the span's name.

client.address

Set to the request's address.

server.address

Set to the host portion of the host value in the leftmost entry in the Forwarded header, falling back to the value of the X-Forwarded-Proto header, or to the value of the Host header if no other is set. The host portion is the part before an optional port number.

See the semantic conventions entry for this attribute for more details on this logic.

If no value could be determined, this attribute will not be present.

server.port

Set to the port number in the host value in the leftmost entry in the Forwarded header, falling back to the value of the X-Forwarded-Proto header, or to the value of the Host header if no other is set.

See the semantic conventions entry for this attribute for more details on this logic.

The port number in these is optional. If none is set, or none could be determined, this attribute will not be present.

user_agent.original

Set to the value of the user agent header. If not set, this attribute will not be present.

url.query

Set to the query of the request URL, if present. If the URL had no query parameters, this attribute will not be present.

The attributes described below will be set in the span once the action is completed, but will not be available for the sampler.

error.type

If an error is encountered during the execution of the action, this attribute will be set to the package name of the error (as reported by "ref"), or the value string if the error is not blessed into any package.

If there were no errors, this attribute will not be present.

http.response.status_code

Set to the status code of the response. If an error was encountered while handling the request, this will be set to 500.

The span will be unconditionally ended after the action has completed, and the status will be set to an error status if the response result in a server error (any HTTP status greater than or equal to 500).

If an exception is raised during the execution of the action, this will be caught and the description of the error status will be based on the message in that exception (otherwise no description will be set). The description will contain the first line of the exception body, minus any trailing markers of where the error took place, with the aim to make it safe to be exposed without leaking too much internal information.

Any exceptions caught by this integration will be re-thrown to be handled downstream.

Internal redirects

Dancer2 supports the use of keywords to internally forward a request from one handler to another, or to pass from one handler to another. In this case, this plugin will only execute for the first matching route. The following handlers will continue to be executed as expected, but the plugin will ignore them.

This is done on the assumption that the requests that are of interest from a telemetry point of view are those that come from outside the application.

Users for which this is undesirable are free to create new spans for those routes that this plugin would otherwise ignore.

SEE ALSO

Dancer2
Dancer2::Plugin
OpenTelemetry
OpenTelemetry::Context
OpenTelemetry::Constants
OpenTelemetry::Trace::Span
OpenTelemetry::Trace::Tracer
OpenTelemetry::Trace::TracerProvider

Integrations with other web frameworks

Plack::Middleware::OpenTelemetry

A lower-level, Plack-based implementation of the same idea behind this plugin. Because it executes outside the scope of Dancer2, it will not have access to the routes with placeholders at span-creation time, but it's worth a look.

Mojolicious::Plugin::OpenTelemetry

A similar version of this plugin, but integrating with the Mojolicious web framework.

COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by José Joaquín Atria.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.