Flask Application Manual Instrumentation for Distributed Traces

Flask Application Manual Instrumentation for Distributed Traces

In this blog series, we share the application instrumentation steps for distributed tracing with OpenTelemetry standards across multiple languages. Earlier, we covered Java Application Manual Instrumentation for Distributed Traces, Golang Application Instrumentation for Distributed Traces, Node JS Application for Distributed Traces, and DotNet Application Instrumentation for Distributed Traces.  

In this blog post, we are going to cover:

OpenTelemetry is a project by the Cloud Native Computing Foundation aimed to standardize the way that application telemetry data is recorded and utilized by platforms downstream. This application trace data can be valuable for application owners to understand the relationship between the components and services in their code, the request volume and latency introduced in each step, and ultimately where the bottlenecks are that are resulting in poor user experience. Python is one of the many languages that OpenTelemetry supports, and Flask is a popular lightweight framework that is used to create web applications. Below we will cover the steps involved to instrument a basic Flask application.

You can read more about OpenTelemetry here

Installation

Prerequisites:

  • Python installed (3.7+).
  • Your selected IDE (like PyCharm)
  • Once you have the IDE, choose your project (or create a new one if you’re just following along). I named mine “instrument-flask-app”.
  • virtualenv — if you’re using PyCharm you don’t have to worry about this.

Initialize the Project:

Install the following libraries:

pip install flask
pip install opentelemetry-api
pip install opentelemetry-sdk
pip install opentelemetry-opentelemetry-instrumentation-flask
pip install opentelemetry-exporter-otlp

Create a file “app.py” under the root project directory “instrument-flask-app”:

Instrument-flask-app
	|___ app.py

Import the following libraries:

from flask import Flask  			
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider		
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.sdk.trace.export import BatchSpanProcessor,          ConsoleSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import   OTLPSpanExporter

Create a Flask app instance:

app = Flask(__name__)

Construct trace provider:

trace.set_tracer_provider(TracerProvider())

Init span exporter:

The exporter is the component in SDK responsible for exporting the telemetry signal (trace) out of the application to a remote backend, log to a file, stream to stdout. etc. In this example, we are creating a gRPC exporter to send out traces to an OpenTelemetry receiver backend running on localhost. 

trace.get_tracer_provider().add_span_processor(BatchSpanProcessor
(OTLPSpanExporter(endpoint=os.environ.get("LM_OTEL_ENDPOINT"),insecure=True)))

Note: Optionally, you can also print the traces emitted from the application on the console by doing the following:

trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

Set up the OTEL collector endpoint:

You will have to set the environment variable LMOTEL_ENDPOINT which is the endpoint of the OTEL collector. The traces would be emitted by following:

export  LM_OTEL_ENDPOINT=http://<HOSTNAME>:<PORT>

Create resource detector:

The resource describes the object that generated the Telemetry signals. Essentially, it must be the name of the service or application. In LogicMonitor, you can use these attributes to map to a device or resource that you’re already monitoring within LogicMonitor.

  • Service.namespace: Used to group the services. For example, you can use this to distinguish services across environments like QA, UAT, PROD.
  • Service.name: The logical name of the service.
  • Host.name: Name of the host where the service is running.

Set the environment variable as the following:

export OTEL_RESOURCE_ATTRIBUTES=service.namespace=opentelemetry,service.name=instrument-flask-app,host.name=localhost

Auto-instrumenting the Flask app object:

FlaskInstrumentor().instrument_app(app)

Define an endpoint to test the instrumentation:

@app.route("/hello") 
def hello():  
   return "Hello World!" 

Running the Flask app:

Flask apps run on port 5000 by default.

if __name__ == "__main__":  	# on running python app.py
        app.run()  			# run the flask app

Putting it together:

# app.py

from flask import Flask
from opentelemetry import trace
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter importOTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor,ConsoleSpanExporter

app = Flask(__name__) 
trace.set_tracer_provider(TracerProvider())

# To print the traces on the console
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))

# To send the traces to the configured OTEL collector
trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(
OTLPSpanExporter(endpoint=os.environ.get("LM_OTEL_ENDPOINT"), insecure=True)))

@app.route("/hello")  
def hello():  
       return "Hello World!"  


if __name__ == "__main__":  
   app.run()

Test our application

Let’s test our application by running the Python app. You should the server started as below:

python app.py
Testing Python application

Success! 

You should be able to see the traces on the LogicMonitor portal after hitting the endpoint (“http://127.0.0.1:5000/hello”) from your browser: 

Traces showing success in LogicMonitor

You can view additional information by clicking on the trace:

Additional information of a trace in LogicMonitor.

The traces on the console would look like this:

Traces within console example.

Next Steps

Congratulations, you have instrumented a flask-based Python application emitting traces using the OpenTelemetry Protocol (OTLP) Specification! Now, you’ll have visibility into your application and be able to address any potential bottlenecks present in your application code. If you’re already using LogicMonitor to monitor your infrastructure or collect logs, you’ll be able to associate these traces for faster troubleshooting. 

If you’re not already sending in logs, try it out here to further enrich how much context you can bring into LogicMonitor. 

External Resources

We recommend the following resources to learn more about OpenTelemetry and CNCF:

  • Join their Slack community here.
  • Check out their OTel documentation here.
  • Read more or leave feedback in the OTel Python repo here.

Check back for more blogs covering steps for distributed tracing with OpenTelemetry standards across other application languages.