Introduction to OpenTelemetry¶
OpenTelemetry (OTEL) is an open-source observability framework for cloud-native software. It provides a standard for collecting, processing, and exporting telemetry data (such as traces, metrics, and logs) from applications. By using OpenTelemetry, you can gain deep insights into the performance and behavior of your distributed systems, making it easier to monitor, troubleshoot, and optimize your applications.
OpenTelemetry is vendor-agnostic and supports integration with many popular observability backends, including Grafana, Jaeger, Zipkin, Prometheus, and others. It is a unified standard that simplifies the process of instrumenting code and collecting telemetry data, enabling to build robust monitoring and tracing solutions with small effort.
In the context of web frameworks like BlackSheep, OpenTelemetry can be used to automatically trace incoming requests, outgoing HTTP calls, and other operations, providing end-to-end visibility into your application's execution flow.
Enabling OpenTelemetry in BlackSheep¶
BlackSheep offers built-in support for OpenTelemetry since version 2.3.2
, but
it is anyway simple to use OpenTelemetry in previous versions of the web
framework.
To enable OpenTelemetry integration starting with version 2.3.2
, you can
import common functions from the blacksheep.server.otel
namespace.
# To use exporters of your choice:
from blacksheep.server.otel import use_open_telemetry
# To use a service that supports the OpenTelemetry Protocol:
from blacksheep.server.otel.otlp import use_open_telemetry_otlp
More information is provided below.
To enable OpenTelemetry integration before version 2.3.2
, follow the example
included in BlackSheep-Examples/otel.
- Copy the code in the
otel/__init__.py
andotel/otlp.py
folder into your own system. - Follow the instructions in the repository and the information below.
The common code consists of a middleware that enables tracing of all web requests
and exceptions (OTELMiddleware
), logging instrumentation, and application
callbacks to ensure logs are collected and flushed properly.
The common code is vendor-agnostic because it is intentionally abstracted
from specific observability services. Depending on the service you want to use,
you need to configure specific exporters, which are classes responsible to
send collected logs and traces towards a certain OTEL service. If the service
you intend to use supports the OpenTelemetry Protocol (OTLP), you can use the
use_open_telemetry_otlp
method imported from otlp.py
. This method uses
OTLP
exporters, which rely on standard environment variables. The
documentation below provides an example on how to use Grafana in such scenario.
Requirements¶
To work using the OTLP protocol (for instance, with Grafana),
install the opentelemetry-exporter-otlp
package:
Install other dependencies depending on the backend service you intend to use.
For instance, for Azure Application Insights, install azure-monitor-opentelemetry-exporter
:
Example: Grafana¶
There are several ways to integrate with Grafana. This documentation describes
only manual setup using environment variables for the OTLP protocol. This
approach is flexible because it only requires outgoing HTTP connections, and
works well in scenarios where installing agents like Grafana Alloy
is not
easy or not possible (e.g. cloud PaaS
services).
- Install the required dependencies like described above.
- Obtain environment variables for the OTLP protocol, using the OpenTelemetry getting started guide and the option Send OpenTelemetry data directly to the Grafana Cloud OTLP endpoint.
Obtain the environment variables with the following names:
OTEL_RESOURCE_ATTRIBUTES="..."
OTEL_EXPORTER_OTLP_ENDPOINT="..."
OTEL_EXPORTER_OTLP_HEADERS="..."
OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
Configure env variables for your application.
Python dot-env.
One way to handle env variables for local development is using the
python-dotenv
library and storing the env variables in a .env
file, then
load variables using the load_dotenv
function imported from dotenv
.
In such case, ensure that .env
is included in .gitignore
.
Enable data collection:
from blacksheep import Application
from blacksheep.server.otel.otlp import use_open_telemetry_otlp
app = Application()
use_open_telemetry_otlp(app)
A trace is produced for each web request and for all handled and unhandled exceptions. For unhandled exceptions, OpenTelemetry includes the full stacktrace of the exception.
Example: Azure Application Insights¶
This documentation describes how to integrate with an Azure Application Insights service.
- Install the required dependencies like described above.
- Obtain the connection string of an Azure Application Insights service.
- Configure tracing like in the following example:
from azure.monitor.opentelemetry.exporter import (
AzureMonitorLogExporter,
AzureMonitorTraceExporter,
)
from blacksheep import Application
from blacksheep.server.otel import use_open_telemetry
def use_application_insights(
app: Application,
connection_string: str,
):
"""
Configures OpenTelemetry for a BlackSheep application using Azure Application Insights.
Sets up logging and tracing exporters for Azure Monitor using the provided connection string.
Args:
app: The BlackSheep Application instance.
connection_string: Azure Application Insights connection string.
"""
use_open_telemetry(
app,
AzureMonitorLogExporter(connection_string=connection_string),
AzureMonitorTraceExporter(connection_string=connection_string),
)
app = Application()
use_application_insights(app, "YOUR_CONN_STRING")
Observe how web requests and errors are displayed:
Logging dependencies¶
When using OpenTelemetry you can handle your own tracing explicitly. The BlackSheep code includes an asynchronous context manager and example code for granular control of tracing.
from blacksheep.server.otel import logcall
@logcall("Example")
async def dependency_example():
await asyncio.sleep(0.1)
@app.router.get("/")
async def home(request) -> Response:
await dependency_example()
return text("Hello, traced BlackSheep!")
The following screenshots illustrate how dependencies are displayed in Grafana and Azure Application Insights:
Dependencies with Azure Application Insights.
For Azure, use a decorator that sets the az.namespace
information like in
the example provided in BlackSheep-Examples.
Working with spans¶
The provided OTELMiddleware
ensures that a tracing context, called "span", is
created for each request and response cycle. To include additional information
to a web request cycle, obtain the current span like in the following example:
from opentelemetry import trace
@app.router.get("/")
async def home(request) -> Response:
span = trace.get_current_span()
span.set_attribute("custom.info", "This is extra info for the request")
return text("Hello, traced BlackSheep!")
trace.get_current_span()
works by using context variables (such as Python's
contextvars.ContextVar
)
under the hood. Context variables allow each asynchronous task or coroutine to
have its own independent context, so the current span is correctly tracked even
when code is running concurrently. This ensures that in async code, each task
sees its own current span, avoiding conflicts or leaks between concurrent
executions.
Last modified on: 2025-06-19 11:50:01