Lightstep from ServiceNow Logo





Lightstep from ServiceNow Logo
< all blogs

Getting Started with OpenTelemetry Alphas: Python

New Alpha versions of the Python OpenTelemetry API and SDK packages have just been released, and it's time to take them for a test drive! This guide walks through installing the required packages, generating tracing information and propagating context across processes via HTTP.


In order to get started with OpenTelemetry in Python, we'll need to install the API and SDK packages. This can be done via pip. Note: this requires Python 3.4 or newer.

pip install opentelemetry-api opentelemetry-sdk

Creating a span

In this first example, we'll use the ConsoleSpanExporter to get us going:

from opentelemetry import trace
from opentelemetry.context import Context
from opentelemetry.sdk.trace import Tracer
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor

trace.set_preferred_tracer_implementation(lambda T: Tracer())
tracer = trace.tracer()
# configure the ConsoleSpanExporter as our exporter
span_processor = BatchExportSpanProcessor(ConsoleSpanExporter())
with tracer.start_as_current_span('foo'):
    with tracer.start_as_current_span('bar'):
        with tracer.start_as_current_span('baz'):

After running the code, the tracing information will appear in the console:

AsyncRuntimeContext({'current_span': Span(name="baz", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0x2202e5e00e02a985, trace_state={}))})
Span(name="baz", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0x2202e5e00e02a985, trace_state={}), kind=SpanKind.INTERNAL, parent=Span(name="bar", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0x10a1a831a5210e6a, trace_state={})), start_time=2019-11-05T23:50:06.569282Z, end_time=2019-11-05T23:50:06.569359Z)
Span(name="bar", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0x10a1a831a5210e6a, trace_state={}), kind=SpanKind.INTERNAL, parent=Span(name="foo", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0xc4beccc7fbb46181, trace_state={})), start_time=2019-11-05T23:50:06.569255Z, end_time=2019-11-05T23:50:06.569482Z)
Span(name="foo", context=SpanContext(trace_id=0xddf0ac0d009a33320781ccac56018ead, span_id=0xc4beccc7fbb46181, trace_state={}), kind=SpanKind.INTERNAL, parent=None, start_time=2019-11-05T23:50:06.569221Z, end_time=2019-11-05T23:50:06.569561Z)

Console output is a great start. Now, let's try sending our traces into JaegerJaeger. The following command will launch the "handy in dev" but "don't use it in PROD" all-in-one Jaeger Docker container:

docker run -d -p 6831:6831/udp -p 16686:16686 jaegertracing/all-in-one:latest

You should be able to visit http://localhost:16686http://localhost:16686:

pip install opentelemetry-ext-jaeger

With those in place, we can now configure the JaegerSpanExporter to send traces to the container we just launched.

from opentelemetry.ext import jaeger
# create a JaegerSpanExporter
jaeger_exporter = jaeger.JaegerSpanExporter(
    service_name="getting-started-jaeger", agent_host_name="localhost", agent_port=6831,

span_processor = BatchExportSpanProcessor(jaeger_exporter)

Navigating through the Jaeger interface using a browser http://localhost:16686http://localhost:16686, we should now be able to find the trace that was just created.


Propagating the Context

The next example will walk through making a request from a client to a server, which allows us to test context propagation across process boundaries. Let's start with the dependencies:

pip install flask opentelemetry-ext-wsgi opentelemetry-ext-http-requests

For the server, we'll use the OpenTelemetryMiddlewareOpenTelemetryMiddleware.

import flask
import requests

from opentelemetry import trace
from opentelemetry.ext import http_requests
from opentelemetry.ext.wsgi import OpenTelemetryMiddleware

app = flask.Flask(__name__)
app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)

def hello():
    with tracer.start_as_current_span("parent"):
    return "hello"

if __name__ == "__main__":

The client code will make a request to the server using the Requests module, which we'll instrument via the http_request OpenTelemetry extension.

import requests

from opentelemetry import trace
from opentelemetry.ext import http_requests

response = requests.get(url="")

That's it! You're now ready to instrument your Python code with OpenTelemetry! If you're interested in contributing but aren't sure how to, be sure to check out the recent blog post on How to Start ContributingHow to Start Contributing repo if you'd like to get involved!

Interested in joining our team? See our open positions herehere.

November 14, 2019
2 min read

Share this article

About the author

Alex Boten

From Day 0 to Day 2: Reducing the anxiety of scaling up cloud-native deployments

Jason English | Mar 7, 2023

The global cloud-native development community is facing a reckoning. There are too many tools, too much telemetry data, and not enough skilled people to make sense of it all.  See how you can.

Learn moreLearn more

OpenTelemetry Collector in Kubernetes: Get started with autoscaling

Moh Osman | Jan 6, 2023

Learn how to leverage a Horizontal Pod Autoscaler alongside the OpenTelemetry Collector in Kubernetes. This will enable a cluster to handle varying telemetry workloads as the collector pool aligns to demand.

Learn moreLearn more

Observability-Landscape-as-Code in Practice

Adriana Villela, Ana Margarita Medina | Oct 25, 2022

Learn how to put Observability-Landscape-as-Code in this hands-on tutorial. In it, you'll use Terraform to create a Kubernetes cluster, configure and deploy the OTel Demo App to send Traces and Metrics to Lightstep, and create dashboards in Lightstep.

Learn moreLearn more

Lightstep sounds like a lovely idea

Monitoring and observability for the world’s most reliable systems