NAME

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

SYNOPSIS

use Mojolicious::Lite -signatures;

plugin OpenTelemetry => {
    # Passed to OpenTelemetry::Trace::TracerProvider->tracer
    tracer => {
        name    => 'my_app', # defaults to OTEL_SERVICE_NAME or moniker
        version => '1.234',  # optional
    },
};

# Will generate a span named
# GET /static/url
get '/static/url' => sub ( $c, @ ) {
    $c->render( text => 'OK' );
};

# Will use placeholders for reduced span cardinality
# POST /url/with/:placeholder
post '/url/with/:placeholder' => sub ( $c, @ ) {
    $c->render( text => 'OK' );
};

# Use it also with async actions!
get '/async' => sub ( $c, @ ) {
    $c->ua->get_p('https://httpbin.org/delay/1')
        ->then( sub {
            $c->render( json => shift->result->json );
        });
};

# Errors will be correctly captured in the span
get '/error' => sub ( $c, @ ) {
    die 'oops';
};

app->start;

DESCRIPTION

This plugin allows a Mojolicious application to automatically generate telemetry data using the OpenTelemetry API. The plugin will wrap around any endpoint actions, and capture telemetry data about it on a OpenTelemetry::Trace::Span object.

METHODS

Mojolicious::Plugin::OpenTelemetry inherits all methods from Mojolicious::Plugin and implements the following new ones.

register

$plugin->register(
    Mojolicious->new,
    {
        tracer => \%tracer_args,
    },
);

Registers this plugin in a Mojolicious application.

When registered, this plugin will install a wrapper around any endpoint action (as described in "around_action" in Mojolicious). Actions that are not endpoints will be unaffected.

Before the action is executed, 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 passed on registration will be used to obtain a tracer via "tracer" in OpenTelemetry::Trace::TracerProvider. Setting this is optional. If no value is set, the value will be read from the "OTEL_SERVICE_NAME" environment variable, or from the app's moniker, will be used as the default name, but all other values will be left unspecified.

The name of the generated span will be derived from the current request method, and the stringified endpoint of the matching 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 request version.

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 stringified endpoint of the matching 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 remote address of the transaction. This will respect the value set in the X-Forwarded-For header, if any.

client.port

Set to the remote port of the transaction.

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 during the execution of the action, 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.

LIMITATIONS

  • Routes generated with under are NOT currently instrumented. The code in the generated route will in effect run before the span is created, when no valid span is present in the context (unless one has been added by you).

    That said, the final routes that execute (ie. the ones that are under the generated one) will correctly execute in a context with a span.

SEE ALSO

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

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.