In this article, we discuss how to instruct a Python application to send traces to Oracle Cloud Infrastructure (OCI) Application Performance Monitoring (APM) as a simple starter example.

OCI APM service provides deep visibility into the performance of applications and enables you to diagnose issues quickly to deliver a consistent level of service. APM supports OpenTracing, an API- and vendor-neutral instrumentation framework, allows application developers to instrument their application code and analyze the trace data in OCI APM.

In this example, we use zipkin tracers to instrument Python Flask-based web application. Various communities support zipkin libraries, and in this example, we use py_zipkin.

The Python application looks like the following service operations map. DemoAPMApp is the trace for the entire transaction, and the storefront, catalog, orders, and payment are individual spans

A graphic depicting the service operations map workflow for the example.

Prerequisites and dependencies

Use Python 3.0 and above, installing with the following command:

python3 --version

Install the following dependencies:

pip3 install flask 
pip3 install requests 
pip3 install py_zipkin

Instrumenting the application

Before beginning, determine the APM endpoint URL. The documentation shows how to extract the APM data upload endpoint URL with pictures. Use the following steps to determine the endpoint:

  • Log in to Oracle Cloud Console. Under Observability and Management, choose Application Performance Monitoring, and Administration.

  • Create your APM domain.

  • Save the domain values for the data upload endpoint and public key.

Create a file named transport.py with the following code. Read the comments to substitute the values.

import requests
 
# Change this APM Endpoint url
apm_upload_endpoint_url=https://{APM_ENDPOINT_URL}/20200101/observations/public-span?dataFormat=zipkin&dataFormatVersion=2&dataKey={APM_PUBLIC_KEY};
 
def http_transport(encoded_span):
    result = requests.post(
      #Construct a URL that communicate with Application Performance Monitoring
      apm_upload_endpoint_url,
      data=encoded_span,
      headers={'Content-Type': 'application/json'},
      )
    return result

Create a file named oci-apm.py with the following code:

from flask import Flask  # include the flask library 
app = Flask(__name__) 
from py_zipkin import Encoding #import Zipkin package 
from py_zipkin.zipkin import zipkin_span #import Zipkin package 
from transport import http_transport #import Zipkin transport 
import requests, time
 
@zipkin_span(service_name='storefront', span_name='service1-storefront')
def service1():
    time.sleep(2)
    service2()
 
@zipkin_span(service_name='catalogue', span_name='service2-catalogue')
def service2():
    time.sleep(5)
    
@zipkin_span(service_name='orders', span_name='service3-orders')
def service3():
    service4()
 
@zipkin_span(service_name='payment', span_name='service4-payment')
def service4():
    pass
 
@app.route("/tracing") 
def apm_tracing():
 
    with zipkin_span(  
        service_name="DemoAPMApp", #You can change it as you need
        span_name="Demo OCI APM App Spans", #You can change it as you need
        transport_handler=http_transport, #zipkin transport, will use it to upload trace data to OCI APM
        encoding = Encoding.V2_JSON,
        binary_annotations = {"oci-apm":"This is a Test APM tracing web application"}, #Custom tag
        sample_rate=100 # this is optional and can be used to set custom sample rates
    ):
        service1()
        service3()
    return 'This is a Test APM tracing web application'
if __name__ == '__main__': 
   app.run(port=5000, debug=True) # application will start listening for web request on port 5000

This installation is technically one service because everything runs on one webserver. However, it tracks the service operations because we annotated every function.

The code includes the following steps:

  • Import the py_zipkin library.

  • Initialize the zipkin tracer with zipkin_span.

  • Annotated functions with service and span names.

  • The transport handler (http_transport) does the POST calls to APM service.

The following snippet of the encoded data for the storefront function is sent to APM:

{"traceId": "7c9de84a7xxxx", "id": "a32cae6exxxx", "name":
"Service1 storefront", "parentId": "7d11d0ede00de125", "timestamp":
1648082561061494, "duration": 2005185, "localEndpoint": {"serviceName":
"service1", "ipv4": "127.0.0.1"}}

Run and test

To run the code, input the following command:

$ python3 oci-apm.py

Then, test the code with the following command:

$ curl http://127.0.0.1:5000/tracing

Viewing the traces from the Console

Log in to Oracle Cloud Console. Under Observability and Management, choose Application Performance Monitoring and Trace explorer.

A screenshot of the Trace Details page for the example deployment in APM.

APM shows the service map and the entire trace plus the spans. According to the sleep statements, service1 took seven seconds, service2 took five seconds, and other functions took close to milliseconds.

Conclusion

You can use APM to analyze application performance by instrumenting the application using open source tracers like zipkin. I hope that this simple example helps you get started with your own deployments.

You can find code and instructions on GitHub. For advanced use cases, follow Application Performance Monitoring: Instrumenting Python Applications.