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.

Setup

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.

1
pip install opentelemetry-api opentelemetry-sdk

Creating a span

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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())
tracer.add_span_processor(span_processor)
with tracer.start_as_current_span('foo'):
    with tracer.start_as_current_span('bar'):
        with tracer.start_as_current_span('baz'):
            print(Context)
span_processor.shutdown()

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

1
2
3
4
5
python console.py
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 Jaeger. The following command will launch the “handy in dev” but “don’t use it in PROD” all-in-one Jaeger Docker container:

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

You should be able to visit http://localhost:16686 and see the Jaeger UI. Next we’ll install the Jaeger exporter:

1
pip install opentelemetry-ext-jaeger

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

1
2
3
4
5
6
7
8
9
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: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:

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

For the server, we’ll use the OpenTelemetryMiddleware to instrument the Flask app. The / handler sends a request using the requests module which is instrumented as well, using the OpenTelemetry requests integration.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import flask
import requests

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

http_requests.enable(tracer)
app = flask.Flask(__name__)
app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app)

@app.route("/")
def hello():
    with tracer.start_as_current_span("parent"):
        requests.get("https://www.wikipedia.org/wiki/Rabbit")
    return "hello"

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

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

1
2
3
4
5
6
7
8
9
import requests

from opentelemetry import trace
from opentelemetry.ext import http_requests
...

http_requests.enable(tracer)
response = requests.get(url="http://127.0.0.1:5000/")
span_processor.shutdown()

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 Contributing, there’s still a ton of work to do! Find us on gitter or visit the OpenTelemetry Python repo if you’d like to get involved!