All Products
Search
Document Center

Application Real-Time Monitoring Service:Report trace data from Python applications by using OpenTelemetry

Last Updated:Mar 11, 2026

Instrument your Python application with OpenTelemetry to report trace data to Managed Service for OpenTelemetry. Once connected, Application Real-Time Monitoring Service (ARMS) displays application topology, traces, abnormal transactions, slow transactions, and SQL analysis.

This topic covers three instrumentation approaches:

  • Automatic instrumentation -- No code changes. An OpenTelemetry agent instruments supported frameworks at runtime.

  • Manual instrumentation -- Full control. Add spans, attributes, and baggage through the OpenTelemetry SDK.

  • Combined instrumentation -- Layer custom spans on top of automatic instrumentation for targeted visibility.

Important Tip: Alibaba Cloud provides a dedicated Python agent with built-in observability for LLM frameworks including LlamaIndex, Dify, LangChain, OpenAI, and Qwen. It produces richer metrics, traces, and continuous profiling data than the open-source agent. For details, see Monitor Python applications.

Prerequisites

Supported frameworks

OpenTelemetry provides automatic instrumentation plug-ins for common Python frameworks. The following table lists the supported frameworks and their version requirements. For the full list, see opentelemetry-python-contrib.

Note In the table, ~= V.N means >= V.N and == V.*. For example, aiohttp ~= 3.0 means version 3.0 or later within the 3.x range.

Expand to view supported Python frameworks

Plug-inSupported package and version
opentelemetry-instrumentation-aio-pikaaio_pika - [7.2.0, 10.0.0)
opentelemetry-instrumentation-aiohttp-clientaiohttp ~= 3.0
opentelemetry-instrumentation-aiohttp-serveraiohttp ~= 3.0
opentelemetry-instrumentation-aiopgaiopg - [0.13.0, 2.0.0)
opentelemetry-instrumentation-asgiasgiref ~= 3.0
opentelemetry-instrumentation-asyncpgasyncpg >= 0.12.0
opentelemetry-instrumentation-aws-lambdaaws_lambda
opentelemetry-instrumentation-botoboto ~= 2.0
opentelemetry-instrumentation-boto3sqsboto3 ~= 1.0
opentelemetry-instrumentation-botocorebotocore ~= 1.0
opentelemetry-instrumentation-cassandracassandra-driver ~= 3.25, scylla-driver ~= 3.25
opentelemetry-instrumentation-celerycelery - [4.0, 6.0)
opentelemetry-instrumentation-confluent-kafkaconfluent-kafka - [1.8.2, 2.2.0]
opentelemetry-instrumentation-dbapidbapi
opentelemetry-instrumentation-djangodjango >= 1.10
opentelemetry-instrumentation-elasticsearchelasticsearch >= 2.0
opentelemetry-instrumentation-falconfalcon - [1.4.1, 4.0.0)
opentelemetry-instrumentation-fastapifastapi ~= 0.58
opentelemetry-instrumentation-flaskflask - [1.0, 3.0)
opentelemetry-instrumentation-grpcgrpcio ~= 1.27
opentelemetry-instrumentation-httpxhttpx >= 0.18.0
opentelemetry-instrumentation-jinja2jinja2 - [2.7, 4.0)
opentelemetry-instrumentation-kafka-pythonkafka-python >= 2.0
opentelemetry-instrumentation-logginglogging
opentelemetry-instrumentation-mysqlmysql-connector-python ~= 8.0
opentelemetry-instrumentation-mysqlclientmysqlclient < 3
opentelemetry-instrumentation-pikapika >= 0.12.0
opentelemetry-instrumentation-psycopg2psycopg2 >= 2.7.3.1
opentelemetry-instrumentation-pymemcachepymemcache - [1.3.5, 5)
opentelemetry-instrumentation-pymongopymongo - [3.1, 5.0)
opentelemetry-instrumentation-pymysqlPyMySQL < 2
opentelemetry-instrumentation-pyramidpyramid >= 1.7
opentelemetry-instrumentation-redisredis >= 2.6
opentelemetry-instrumentation-remouladeremoulade >= 0.50
opentelemetry-instrumentation-requestsrequests ~= 2.0
opentelemetry-instrumentation-sqlalchemysqlalchemy
opentelemetry-instrumentation-sqlite3sqlite3
opentelemetry-instrumentation-starlettestarlette ~= 0.13.0
opentelemetry-instrumentation-system-metricspsutil >= 5
opentelemetry-instrumentation-tornadotornado >= 5.1.1
opentelemetry-instrumentation-tortoiseormtortoise-orm >= 0.17.0
opentelemetry-instrumentation-urlliburllib
opentelemetry-instrumentation-urllib3urllib3 - [1.0.0, 3.0.0)
opentelemetry-instrumentation-wsgiwsgi

Sample code

Download the complete sample code from python-opentelemetry-demo.

Choose a transport protocol

Managed Service for OpenTelemetry accepts trace data over HTTP and gRPC. Choose based on your network environment.

ProtocolBest forNotes
HTTPMost environmentsTraverses proxies and firewalls easily. No authentication header required.
gRPCLow-latency, high-throughput environmentsRequires the grpcio package. Uses an Authentication header.

Both options appear in the examples below. Choose one based on your network requirements.

Placeholder reference

Replace these placeholders in the examples with your actual values:

PlaceholderDescriptionWhere to find it
<your-service-name>Identifies your application in the ARMS console.You define this. Example: my-python-app
<host-name>Hostname of the machine running your application.Run hostname in your terminal.
<endpoint>Endpoint for reporting trace data (HTTP or gRPC).Managed Service for OpenTelemetry console. See Obtain access point information.
<token>Authentication token (gRPC only).Managed Service for OpenTelemetry console. See Obtain access point information.

Automatic instrumentation

Automatic instrumentation is the quickest path to distributed tracing. The OpenTelemetry agent instruments supported frameworks at runtime without code changes.

Step 1: Install dependencies

pip install django
pip install requests
pip install opentelemetry-distro \
	opentelemetry-exporter-otlp

opentelemetry-bootstrap -a install

opentelemetry-bootstrap detects installed libraries and installs the matching instrumentation plug-ins automatically.

Step 2: Create a Django project and application

  1. Create an AutoAndManualDemo project:

       django-admin startproject AutoAndManualDemo
  2. Create a HelloWorld application in the project:

       cd AutoAndManualDemo
    
       # Create a HelloWorld application in the project.
       python manage.py startapp helloworld

Step 3: Add application code

  1. Add the following code to the AutoAndManualDemo/helloworld/views.py file:

       from django.http import HttpResponse
       from datetime import datetime
    
       # Create your views here.
       def hello_world_view(request):
           result = "Hello World!  Current Time =" + str(get_time())
           return HttpResponse(result)
    
       def get_time():
           now = datetime.now()
           return now.strftime("%H:%M:%S")
  2. Create a AutoAndManualDemo/helloworld/urls.py file and add the following code:

       from django.urls import path
    
       from . import views
    
       urlpatterns = [
           path('', views.hello_world_view, name='helloworld')
       ]
  3. Add the URL pattern to the AutoAndManualDemo/AutoAndManualDemo/urls.py file:

       from django.contrib import admin
       from django.urls import path, include
    
       urlpatterns = [
        path('admin/', admin.site.urls),
        path('helloworld/', include('helloworld.urls')),
       ]

Step 4: Run your application

Prefix your start command with opentelemetry-instrument and pass the exporter configuration.

Report over HTTP

opentelemetry-instrument \
    --traces_exporter console,otlp_proto_http \
    --metrics_exporter none \
    --service_name <your-service-name> \
    --exporter_otlp_traces_endpoint <endpoint> \
    python manage.py runserver --noreload

If you do not want the console to display the trace data, set the --traces_exporter parameter to --traces_exporter otlp_proto_http.

Note
  • The --noreload flag prevents Django from running the manage.main method twice.
  • If you see CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False, set the environment variable before running: This error occurs because opentelemetry-instrument loads the default Django settings file (django/conf/global_settings.py) instead of your project settings.
  export DJANGO_SETTINGS_MODULE=AutoAndManualDemo.settings

Step 5: Verify trace reporting

  1. Visit http://127.0.0.1:8000/helloworld/ in a browser. The console displays the trace data and reports the trace data to Managed Service for OpenTelemetry.

  2. Log on to the ARMS console.

  3. In the left-side navigation pane, choose Application Monitoring > Applications.

  4. On the Applications page, find your application by the service name you configured and click it.

  5. On the application details page, verify that traces appear. You should see span data corresponding to the requests you sent.

Note If the icon icon appears in the Language column, the application is connected to Application Monitoring. A hyphen (-) indicates a connection to Managed Service for OpenTelemetry.

Manual instrumentation

Use manual instrumentation for fine-grained control over spans, attributes, and context propagation. This approach adds OpenTelemetry SDK calls directly to your code.

Step 1: Install dependencies

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

Step 2: Initialize OpenTelemetry

Create an initialization module (for example, manual.py) that configures the tracer provider and exporter.

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
    OTLPSpanExporter as OTLPSpanGrpcExporter,
)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
    OTLPSpanExporter as OTLPSpanHttpExporter,
)
from opentelemetry.sdk.resources import SERVICE_NAME, HOST_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def init_opentelemetry():
    resource = Resource(attributes={
        SERVICE_NAME: "<your-service-name>",
        HOST_NAME: "<host-name>",
    })

    # Option A: Export over gRPC
    span_processor = BatchSpanProcessor(OTLPSpanGrpcExporter(
        endpoint="<endpoint>",
        headers=("Authentication=<token>"),
    ))

    # Option B: Export over HTTP (uncomment to use)
    # span_processor = BatchSpanProcessor(OTLPSpanHttpExporter(
    #     endpoint="<endpoint>",
    # ))

    trace_provider = TracerProvider(
        resource=resource,
        active_span_processor=span_processor,
    )
    trace.set_tracer_provider(trace_provider)

Step 3: Create spans

After initialization, obtain a tracer and create spans to track operations.

tracer = trace.get_tracer(__name__)

# Context manager pattern
with tracer.start_as_current_span("child_span") as child_span:
    print("hello world")

Nest spans to represent parent-child relationships:

def outer_method():
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span("parent_span") as parent_span:
        inner_method()

def inner_method():
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span("child_span") as child_span:
        print("hello world")

Step 4: Retrieve the trace ID and span ID

Extract the trace ID and span ID from the current span context. These IDs are useful for correlating logs with traces.

ctx = trace.get_current_span().get_span_context()
trace_id = '{trace:032x}'.format(trace=ctx.trace_id)
span_id = '{span:016x}'.format(span=ctx.span_id)
print(trace_id)
print(span_id)

Step 5: Propagate custom metadata with the Baggage API

The OpenTelemetry Baggage API propagates key-value pairs across spans within a trace. Use baggage to pass metadata -- such as user IDs, tenant IDs, or request context -- between services.

from opentelemetry import trace, baggage

def baggage_and_attribute_usage():
    tracer = trace.get_tracer(__name__)

    # Set baggage at the global level
    global_ctx = baggage.set_baggage("key", "value_from_global_ctx")

    # Create a span with custom attributes
    with tracer.start_as_current_span(
        name='baggage_parent_span',
        attributes={'attribute_key': 'value'},
    ) as baggage_parent_span:

        # Set baggage in the parent context
        parent_ctx = baggage.set_baggage("key", "value_from_parent_ctx")

        with tracer.start_as_current_span(
            name='baggage_child_span',
            context=parent_ctx,
        ) as baggage_child_span:

            # Set baggage in the child context
            child_ctx = baggage.set_baggage("key", "value_from_child_ctx")

    # Read baggage values from each context
    print(baggage.get_baggage("key", global_ctx))   # value_from_global_ctx
    print(baggage.get_baggage("key", parent_ctx))    # value_from_parent_ctx
    print(baggage.get_baggage("key", child_ctx))     # value_from_child_ctx

Step 6: Run and verify

python manual.py

After the application runs, verify trace data in the ARMS console. Follow the steps in the Verify trace reporting section under Automatic instrumentation.

Expand to view full sample code

from opentelemetry import trace, baggage
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
    OTLPSpanExporter as OTLPSpanGrpcExporter,
)
from opentelemetry.exporter.otlp.proto.http.trace_exporter import (
    OTLPSpanExporter as OTLPSpanHttpExporter,
)
from opentelemetry.sdk.resources import SERVICE_NAME, HOST_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def init_opentelemetry():
    resource = Resource(attributes={
        SERVICE_NAME: "<your-service-name>",
        HOST_NAME: "<host-name>",
    })

    # Export over gRPC
    span_processor = BatchSpanProcessor(OTLPSpanGrpcExporter(
        endpoint="<endpoint>",
        headers=("Authentication=<token>"),
    ))

    # Export over HTTP (uncomment to use)
    # span_processor = BatchSpanProcessor(OTLPSpanHttpExporter(
    #     endpoint="<endpoint>",
    # ))

    trace_provider = TracerProvider(
        resource=resource,
        active_span_processor=span_processor,
    )
    trace.set_tracer_provider(trace_provider)


def inner_method():
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span("child_span") as child_span:
        print("hello world")


def outer_method():
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span("parent_span") as parent_span:
        inner_method()


def baggage_and_attribute_usage():
    tracer = trace.get_tracer(__name__)
    global_ctx = baggage.set_baggage("key", "value_from_global_ctx")
    with tracer.start_as_current_span(
        name='baggage_parent_span',
        attributes={'attribute_key': 'value'},
    ) as baggage_parent_span:
        parent_ctx = baggage.set_baggage("key", "value_from_parent_ctx")
        with tracer.start_as_current_span(
            name='baggage_child_span',
            context=parent_ctx,
        ) as baggage_child_span:
            child_ctx = baggage.set_baggage("key", "value_from_child_ctx")

    print(baggage.get_baggage("key", global_ctx))
    print(baggage.get_baggage("key", parent_ctx))
    print(baggage.get_baggage("key", child_ctx))


if __name__ == '__main__':
    init_opentelemetry()
    outer_method()
    baggage_and_attribute_usage()

Combined instrumentation

Combine automatic and manual instrumentation to get broad framework coverage from the agent while adding custom spans for business-critical operations. This approach uses the auto-instrumentation agent for framework-level tracing, and adds SDK calls for application-specific spans.

Step 1: Install dependencies

pip install django
pip install requests
pip install opentelemetry-sdk
pip install opentelemetry-instrumentation-django
pip install opentelemetry-exporter-otlp

Step 2: Create a Django project and application

  1. Create an AutoAndManualDemo project:

       django-admin startproject AutoAndManualDemo
  2. Create a HelloWorld application in the project:

       cd AutoAndManualDemo
    
       # Create a HelloWorld application in the project.
       python manage.py startapp helloworld

Step 3: Add custom spans to your code

Import the OpenTelemetry trace API and create spans around the operations you want to track.

The following Django view example adds two custom spans (hello_world_span and time_span) on top of the spans created automatically by the Django instrumentation plug-in. Modify the helloworld/views.py file:

from django.http import HttpResponse
from opentelemetry import trace
from datetime import datetime


def hello_world_view(request):
    tracer = trace.get_tracer(__name__)

    with tracer.start_as_current_span("hello_world_span") as hello_world_span:
        result = "Hello World!  Current Time =" + str(get_time())
        return HttpResponse(result)


def get_time():
    now = datetime.now()
    tracer = trace.get_tracer(__name__)
    with tracer.start_as_current_span("time_span") as time_span:
        return now.strftime("%H:%M:%S")

Step 4: Modify the urls.py file

  1. Create a helloworld/urls.py file and add the following code:

       from django.urls import path
    
       from . import views
    
       urlpatterns = [
           path('', views.hello_world_view, name='helloworld')
       ]
  2. Add the URL pattern to the AutoAndManualDemo/AutoAndManualDemo/urls.py file:

       from django.contrib import admin
       from django.urls import path, include
    
       urlpatterns = [
        path('admin/', admin.site.urls),
        path('helloworld/', include('helloworld.urls')),
       ]

Step 5: Configure the tracer provider

Create an initialization module that sets up the exporter and tracer provider. Add the following code to the OpenTelemetry initialization code in the manual.py file. Import this module early in your application startup (for example, in manage.py or your WSGI/ASGI entry point).

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
# To use HTTP instead, uncomment the following line and comment out the gRPC import:
# from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import SERVICE_NAME, HOST_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter


resource = Resource(attributes={
    SERVICE_NAME: "<your-service-name>",
    HOST_NAME: "<host-name>",
})

trace.set_tracer_provider(TracerProvider(resource=resource))

# Export traces over gRPC
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(OTLPSpanExporter(
        endpoint="<endpoint>",
        headers="Authentication=<token>",  # Required for gRPC only
    ))
)

# Also print traces to the console (useful for debugging)
trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(ConsoleSpanExporter())
)

Step 6: Run and verify

python manage.py runserver --noreload
Note
  • The --noreload flag prevents Django from running the manage.main method twice.
  • If an ImportError(symbol not found in flat namespace '_CFRelease') error occurs, install the gRPC package:
  pip install grpcio

Send a request to http://127.0.0.1:8000/helloworld/ and verify trace data in the ARMS console. Follow the steps in the Verify trace reporting section under Automatic instrumentation.

View monitoring data

  1. Log on to the ARMS console.

  2. In the left-side navigation pane, choose Application Monitoring > Applications.

  3. On the Applications page, click the name of your application.

  4. On the application details page, view traces, the application topology, abnormal transactions, slow transactions, and SQL analysis data.

Note If the icon icon appears in the Language column, the application is connected to Application Monitoring. A hyphen (-) indicates a connection to Managed Service for OpenTelemetry.