Instrumenting Python Apps with OpenTelemetry
Auto Instrumentation
This section shows a working example of a Python application auto-instrumented with OpenTelemetry.
Prerequisite
- A VMware Aria Operations for Applications (formerly known as Tanzu Observability by Wavefront) account, which gives you access to a cluster.
- Clone the OpenTelemetry Examples repository.
- Install the Wavefront proxy.
Note: When running the Wavefront proxy:- Make sure that the
WAVEFRONT_PROXY_ARGS
environment variable contains--otlpGrpcListenerPorts 4317
. - And expose the OpenTelemetry port via
-p 4317:4317
.
- Make sure that the
- Set up an OpenTelemetry Collector:
- Download the
otelcol-contrib
binary from the latest release of the OpenTelemetry Collector project. - In the same directory, create a file named
otel_collector_config.yaml
. - Copy the configurations in the preconfigured YAML file to the file you just created. For details on OpenTelemetry configurations, see OpenTelemetry Collector Configuration.
- On your console, navigate to the directory you downloaded in the step above and run the following command to start OpenTelemetry Collector:
./otelcol-contrib --config otel_collector_config.yaml
- Download the
Tip: We recommend trying virtualenv
to create an
isolated Python environment.
Step 1: Create or Download a Sample Application
Any application can be easily instrumented, for this walk through we will refer to a locally hosted application that responds with “Hello, World!“ each time it is accessed.
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
app.run(host='0.0.0.0', port=80)
Let’s save this file as server.py
.
Step 2: Install OpenTelemetry Packages
The following OpenTelemetry packages are required to auto-instrument an application:
Flask
opentelemetry-distro
opentelemetry-instrumentation
opentelemetry-bootstrap
opentelemetry-exporter-otlp
To install these packages, run the following commands from the application directory:
pip3 install Flask
pip3 install opentelemetry-distro
pip3 install opentelemetry-instrumentation
opentelemetry-bootstrap --action=install
pip3 install opentelemetry-exporter-otlp
Step 3: Configuring the OpenTelemetry Exporter
Now, configure the OpenTelemetry Exporter to send traces from our application to the required endpoint on our local machine:
export OTEL_TRACES_EXPORTER=otlp
export OTEL_EXPORTER_OTLP_ENDPOINT="http://localhost:4317"
Note: Change the value of my_service_name
and my_application_name
attribute as per application’s requirements.
export OTEL_RESOURCE_ATTRIBUTES="service.name=<my_service_name>,application=<my_application_name>"
In the above configuration, we export our trace data using OpenTelemetry Protocol (OTLP). In addition, we set the export
endpoint as localhost:4317
and assign the name my_application_name
to our tracing service as a resource
attribute.
Step 4: Run the Application and Generate a Trace
The collector is now running and listening to incoming traces on port 4317. Now we’re ready to generate traces.
-
Start the application
opentelemetry-instrument python3 server.py
-
Visit
http://localhost
and refresh the page. The application generates and emits a trace of that transaction. When the trace data collected from the OpenTelemetry collector are ingested, we can examine them in our user interface.
Manual-Instrumentation
Automation is great, but eventually we want to add detail. This section shows a working example of a Python application manually-instrumented with OpenTelemetry.
Prerequisite
- An Aria Operations for Applications account, which gives you access to a cluster.
- Clone the OpenTelemetry Examples repository.
- Install the Wavefront proxy.
Note: When running the Wavefront proxy:- Make sure that the
WAVEFRONT_PROXY_ARGS
environment variable contains--otlpGrpcListenerPorts 4317
. - And expose the OpenTelemetry port via
-p 4317:4317
.
- Make sure that the
- Set up an OpenTelemetry Collector:
- Download the
otelcol-contrib
binary from the latest release of the OpenTelemetry Collector project. - In the same directory, create a file named
otel_collector_config.yaml
. - Copy the configurations in the preconfigured YAML file to the file you just created. For details on OpenTelemetry configurations, see OpenTelemetry Collector Configuration.
- On your console, navigate to the directory you downloaded in the step above and run the following command to start OpenTelemetry Collector:
./otelcol-contrib --config otel_collector_config.yaml
- Download the
Tip: We recommend trying virtualenv
to create an
isolated Python environment. Please do not use the same virtual environment if it is already used in
auto-instrumentation.
Step 1: Install OpenTelemetry Packages
To install OpenTelemetry packages for Python, run this command:
pip3 install -r requirements.txt
Tip: We’ve put all the dependencies in the requirements.txt file.
Step 2: Instrument the Application
To keep things simple, this example creates a basic “Hello World” application using Flask.
-
Activate Flask Instrumentation
- First, install the instrumentation package. already taken care of.
- To activate flask instrumentation, run following code in the application:
app = Flask(__name__) FlaskInstrumentor().instrument_app(app)
-
Resource Attributes
Note: Change the value of
my_service_name
andmy_application_name
attribute as per application’s requirements.resource = Resource(attributes={ "service.name": "<my_service_name>", "application": "<my_application_name>" })
-
OTLPSpanExporter Configuration
Note: These are default values, changing this is optional.
span_exporter = OTLPSpanExporter( # endpoint="localhost:4317", # credentials=ChannelCredentials(credentials), # headers=(("metadata", "metadata")), )
-
Setup Tracer
Tracer, an object that tracks the currently active span and allows us to create (or activate) new spans.
trace.set_tracer_provider(tracer_provider) span_processor = BatchSpanProcessor(span_exporter) tracer_provider.add_span_processor(span_processor) tracer = trace.get_tracer_provider().get_tracer(__name__)
-
Creating a Child Span
A span represents a distinct operation - not an individual function, but an entire operation, such as a database query. Generally, this means we shouldn’t be creating spans in our application code, they should be managed as part of the framework or library we are using. But, that said, here is how we do it. Span management has two parts - the span lifetime and the span context. The lifetime is managed by starting the span with a tracer, and adding it to a trace by assigning it a parent.
@app.route('/') def index(): # add latency to the parent span sleep(20 / 1000) # always create a new context when starting a span with tracer.start_as_current_span("child_span") as span: # add an event to the child span span.add_event("event message", {"event_attributes": 1}) # get_current_span will now return the same span trace.get_current_span().set_attribute("http.route", "some route") # add latency to the child span sleep(30 / 1000) return "Hello World"
-
Recording Errors
Exceptions are reported as events, but they should be properly formatted. As a convenience, OpenTelemetry provides a record_exception method for capturing them correctly.
@app.route("/exception") def exception(): try: 1 / 0 except ZeroDivisionError as error: span = trace.get_current_span() # record an exception span.record_exception(error) # fail the operation span.set_status(Status(StatusCode.ERROR, "error happened")) return "Some Exception"
Step 3: Run the Application
-
Start the application
opentelemetry-instrument python3 server.py
-
Visit
http://localhost:8080
orhttp://localhost:8080/exception
and refresh the page. The application generates and emits a trace of that transaction. When the trace data collected from the OpenTelemetry collector are ingested, we can examine them in our user interface.