Skip to main content

Python

Implement OpenTelemetry custom instrumentation for Python applications to collect logs, metrics, and traces using the Python OTel SDK.

Note: This guide provides a concise overview based on the official OpenTelemetry documentation. For complete information, please consult the official OpenTelemetry documentation.

Overview

This guide demonstrates how to:

  • Set up OpenTelemetry custom instrumentation for Python
  • Configure manual tracing using spans
  • Create and manage custom metrics
  • Add semantic attributes and events
  • Export telemetry data to OpenTelemetry Collector

Prerequisites

Before starting, ensure you have:

  • Python 3.7 or later installed
  • A Python project set up
  • Access to package installation (pip)

Required Packages

Install the following necessary packages or add them to requirements.txt:

pip install opentelemetry-api
pip install opentelemetry-sdk

# Optional but recommended
pip install opentelemetry-semantic-conventions

Traces

Traces give us the big picture of what happens when a request is made to an application. Whether your application is a monolith with a single database or a sophisticated mesh of services, traces are essential to understanding the full “path” a request takes in your application.

Initialization

To Start tracing, first a tracer should be acquired and a TraceProvider should be initialized optionally we can pass a resource to TraceProvider.

A Resource is an immutable representation of the entity producing telemetry. For example, a process producing telemetry that is running in a container on Kubernetes has a Pod name, it is in a namespace and possibly is part of a Deployment which also has a name. All three of these attributes can be included in the Resource.

Sample Reference code for Initialization

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
from opentelemetry.sdk.trace.export import (
BatchSpanProcessor,
ConsoleSpanExporter,
)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

resource = Resource({SERVICE_NAME: "my.service.name"})
provider = TracerProvider(resource=resource)
processor = BatchSpanProcessor(OTLPSpanExporter(endpoint="http://0.0.0.0:4318/v1/traces"))
provider.add_span_processor(processor)
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)

# Sets the global default tracer provider
trace.set_tracer_provider(provider)

# Creates a tracer from the global tracer provider
tracer = trace.get_tracer("my.tracer.name")

View your traces in the base14 Scout observability platform.

Note: Ensure your OpenTelemetry Collector is properly configured to receive and process the trace data.

Reference

Official Traces Documentation

Span

A span represents a unit of work or operation. Spans are the building blocks of Traces. In OpenTelemetry, they include some necessary information.

Creating a Span

def do_work():
with tracer.start_as_current_span("span.name") as span:
# do some work that 'span' tracks
print("doing some work...")

Creating nested Spans

def do_work():
with tracer.start_as_current_span("parent") as parent:
# do some work that 'parent' tracks
print("doing some work...")
# Create a nested span to track nested work
with tracer.start_as_current_span("child") as child:
# do some work that 'child' tracks
print("doing some nested work...")

Creating Spans with decorators

@tracer.start_as_current_span("span")
def do_work():
print("doing some work...")

View these spans in base14 Scout observability backend.

Reference

Official Span Documentation

Attributes

Attributes let you attach key/value pairs to a span so it carries more information about the current operation that it’s tracking.

Adding Attributes to a Span

def do_work():
with tracer.start_as_current_span("span.name") as span:
span.set_attribute("operation.value", 1)
span.set_attribute("operation.name", "Saying hello!")
span.set_attribute("operation.other-stuff", [1, 2, 3])

print("doing some work...")

Adding Semantic Attributes to a Span

Semantic Attributes are pre-defined Attributes that are well-known naming conventions for common kinds of data. Using Semantic Attributes lets you normalize this kind of information across your systems.

Ensure that you have installed opentelemetry-semantic-conventions package for using Semantic Attributes

from opentelemetry.semconv.trace import SpanAttributes

def do_work():
with tracer.start_as_current_span("span.name") as span:
span.set_attribute(SpanAttributes.HTTP_METHOD, "GET")
span.set_attribute(SpanAttributes.HTTP_URL, "https://base14.io/")

print("doing some work...")

View these spans in the base14 Scout observability platform.

Note: Ensure your OpenTelemetry Collector is properly configured to receive and process the span data.

Reference

Official Attributes Documentation

Events

An event is a human-readable message on a span that represents “something happening” during its lifetime.

You can think of it as a primitive log.

Adding an event to a span

def do_work():
with tracer.start_as_current_span("span.name") as span:
span.add_event("Starting some work")
print("doing some work...")
span.add_event("Finished working")

Reference

Official Event Documentation

Span Status

A Status can be set on a Span, typically used to specify that a Span has not completed successfully - Error. By default, all spans are Unset, which means a span completed without error. The Ok status is reserved for when you need to explicitly mark a span as successful rather than stick with the default of Unset (i.e., “without error”).

We also look at how to record an exception in the Span.

Setting a Span Status

from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode

def do_work():
with tracer.start_as_current_span("span.name") as span:
try:
# something that might fail
except Exception as exception:
span.set_status(Status(StatusCode.ERROR))
span.record_exception(exception)

View these spans in the base14 Scout observability platform.

Note: Ensure your OpenTelemetry Collector is properly configured to receive and process the span data.

Metrics

Initialization

To start collecting metrics, you’ll need to initialize a MeterProvider and optionally set it as the global default.

Sample Reference code for Metrics Initialization

from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import (
ConsoleMetricExporter,
PeriodicExportingMetricReader,
)
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

metric_reader = PeriodicExportingMetricReader(OTLPMetricExporter(endpoint="http://0.0.0.0:4317/v1/metrics"))
metric_provider = MeterProvider(metric_readers=[metric_reader])
metrics.set_meter_provider(metric_provider)

# Creates a meter from the global meter provider
meter = metrics.get_meter("my.meter.name")

View these metrics in base14 Scout observability backend.

Note: Ensure your OpenTelemetry Collector is properly configured to receive and process the trace data.

Counter

Counter is a synchronous Instrument which supports non-negative increments.

Creating a Synchronous Counter

work_counter = meter.create_counter(
"work.counter", unit="1", description="Counts the amount of work done"
)

def do_work(work_type: string):
work_counter.add(1, {"work.type": work_type})
print("doing some work...")

View these metrics in base14 Scout observability backend.

Creating Asynchronous Counter

from opentelemetry.metrics import Observation

def pf_callback(callback_options):
return [
Observation(8, attributes={"pid": 0, "bitness": 64}),
Observation(37741921, attributes={"pid": 4, "bitness": 64}),
Observation(10465, attributes={"pid": 880, "bitness": 32}),
]


meter.create_observable_counter(name="PF", description="process page faults", callbacks=[pf_callback])

View these metrics in base14 Scout observability backend.

Reference

Official Counter Documentation

Histogram

Histogram is a synchronous Instrument which can be used to report arbitrary values that are likely to be statistically meaningful. It is intended for statistics such as histograms, summaries, and percentile.

Creating a Histogram

http_server_duration = meter.create_histogram(
name="http.server.duration",
description="measures the duration of the inbound HTTP request",
unit="ms",
value_type=float)

http_server_duration.Record(50, {"http.request.method": "POST", "url.scheme": "https"})
http_server_duration.Record(100, http_method="GET", http_scheme="http")

View these metrics in base14 Scout observability backend.

Reference

Official Histogram Documentation