OpenTelemetry ASGI Instrumentation

pypi

This library provides a ASGI middleware that can be used on any ASGI framework (such as Django, Starlette, FastAPI or Quart) to track requests timing through OpenTelemetry.

Installation

pip install opentelemetry-instrumentation-asgi

API

The opentelemetry-instrumentation-asgi package provides an ASGI middleware that can be used on any ASGI framework (such as Django-channels / Quart) to track requests timing through OpenTelemetry.

Usage (Quart)

from quart import Quart
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware

app = Quart(__name__)
app.asgi_app = OpenTelemetryMiddleware(app.asgi_app)

@app.route("/")
async def hello():
    return "Hello!"

if __name__ == "__main__":
    app.run(debug=True)

Usage (Django 3.0)

Modify the application’s asgi.py file as shown below.

import os
from django.core.asgi import get_asgi_application
from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'asgi_example.settings')

application = get_asgi_application()
application = OpenTelemetryMiddleware(application)

Usage (Raw ASGI)

from opentelemetry.instrumentation.asgi import OpenTelemetryMiddleware

app = ...  # An ASGI application.
app = OpenTelemetryMiddleware(app)

Configuration

Request/Response hooks

Utilize request/reponse hooks to execute custom logic to be performed before/after performing a request. The server request hook takes in a server span and ASGI scope object for every incoming request. The client request hook is called with the internal span and an ASGI scope which is sent as a dictionary for when the method recieve is called. The client response hook is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.

 def server_request_hook(span: Span, scope: dict):
     if span and span.is_recording():
         span.set_attribute("custom_user_attribute_from_request_hook", "some-value")

 def client_request_hook(span: Span, scope: dict):
     if span and span.is_recording():
         span.set_attribute("custom_user_attribute_from_client_request_hook", "some-value")

 def client_response_hook(span: Span, message: dict):
     if span and span.is_recording():
         span.set_attribute("custom_user_attribute_from_response_hook", "some-value")

OpenTelemetryMiddleware().(application, server_request_hook=server_request_hook, client_request_hook=client_request_hook, client_response_hook=client_response_hook)

Capture HTTP request and response headers

You can configure the agent to capture predefined HTTP headers as span attributes, according to the semantic convention.

Request headers

To capture predefined HTTP request headers as span attributes, set the environment variable OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST to a comma-separated list of HTTP header names.

For example,

export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="content-type,custom_request_header"

will extract content-type and custom_request_header from request headers and add them as span attributes.

It is recommended that you should give the correct names of the headers to be captured in the environment variable. Request header names in ASGI are case insensitive. So, giving header name as CUStom-Header in environment variable will be able capture header with name custom-header.

The name of the added span attribute will follow the format http.request.header.<header_name> where <header_name> being the normalized HTTP header name (lowercase, with - characters replaced by _ ). The value of the attribute will be single item list containing all the header values.

Example of the added span attribute, http.request.header.custom_request_header = ["<value1>,<value2>"]

Response headers

To capture predefined HTTP response headers as span attributes, set the environment variable OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE to a comma-separated list of HTTP header names.

For example,

export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="content-type,custom_response_header"

will extract content-type and custom_response_header from response headers and add them as span attributes.

It is recommended that you should give the correct names of the headers to be captured in the environment variable. Response header names captured in ASGI are case insensitive. So, giving header name as CUStomHeader in environment variable will be able capture header with name customheader.

The name of the added span attribute will follow the format http.response.header.<header_name> where <header_name> being the normalized HTTP header name (lowercase, with - characters replaced by _ ). The value of the attribute will be single item list containing all the header values.

Example of the added span attribute, http.response.header.custom_response_header = ["<value1>,<value2>"]

Note

Environment variable names to capture http headers are still experimental, and thus are subject to change.

API

class opentelemetry.instrumentation.asgi.ASGIGetter(*args, **kwds)[source]

Bases: opentelemetry.propagators.textmap.Getter[dict]

get(carrier, key)[source]

Getter implementation to retrieve a HTTP header value from the ASGI scope.

Parameters
  • carrier (dict) – ASGI scope object

  • key (str) – header name in scope

Return type

Optional[List[str]]

Returns

A list with a single string with the header value if it exists,

else None.

keys(carrier)[source]

Function that can retrieve all the keys in a carrier object.

Parameters

carrier (dict) – An object which contains values that are used to construct a Context.

Return type

List[str]

Returns

list of keys from the carrier.

class opentelemetry.instrumentation.asgi.ASGISetter(*args, **kwds)[source]

Bases: opentelemetry.propagators.textmap.Setter[dict]

set(carrier, key, value)[source]

Sets response header values on an ASGI scope according to the spec.

Parameters
  • carrier (dict) – ASGI scope object

  • key (str) – response header name to set

  • value (str) – response header value

Return type

None

Returns

None

opentelemetry.instrumentation.asgi.collect_request_attributes(scope)[source]

Collects HTTP request attributes from the ASGI scope and returns a dictionary to be used as span creation attributes.

opentelemetry.instrumentation.asgi.collect_custom_request_headers_attributes(scope)[source]

returns custom HTTP request headers to be added into SERVER span as span attributes Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers

opentelemetry.instrumentation.asgi.collect_custom_response_headers_attributes(message)[source]

returns custom HTTP response headers to be added into SERVER span as span attributes Refer specification https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers

opentelemetry.instrumentation.asgi.get_host_port_url_tuple(scope)[source]

Returns (host, port, full_url) tuple.

opentelemetry.instrumentation.asgi.set_status_code(span, status_code)[source]

Adds HTTP response attributes to span using the status_code argument.

opentelemetry.instrumentation.asgi.get_default_span_details(scope)[source]

Default implementation for get_default_span_details :type scope: dict :param scope: the asgi scope dictionary

Return type

Tuple[str, dict]

Returns

a tuple of the span name, and any attributes to attach to the span.

class opentelemetry.instrumentation.asgi.OpenTelemetryMiddleware(app, excluded_urls=None, default_span_details=None, server_request_hook=None, client_request_hook=None, client_response_hook=None, tracer_provider=None, meter_provider=None)[source]

Bases: object

The ASGI application middleware.

This class is an ASGI middleware that starts and annotates spans for any requests it is invoked with.

Parameters
  • app – The ASGI application callable to forward requests to.

  • default_span_details – Callback which should return a string and a tuple, representing the desired default span name and a dictionary with any additional span attributes to set. Optional: Defaults to get_default_span_details.

  • server_request_hook (Optional[Callable[[Span, dict], None]]) – Optional callback which is called with the server span and ASGI scope object for every incoming request.

  • client_request_hook (Optional[Callable[[Span, dict], None]]) – Optional callback which is called with the internal span and an ASGI scope which is sent as a dictionary for when the method recieve is called.

  • client_response_hook (Optional[Callable[[Span, dict], None]]) – Optional callback which is called with the internal span and an ASGI event which is sent as a dictionary for when the method send is called.

  • tracer_provider – The optional tracer provider to use. If omitted the current globally configured one is used.