通过OpenTelemetry上报Python应用数据

通过OpenTelemetry为应用埋点并上报链路数据至可观测链路 OpenTelemetry 版后,可观测链路 OpenTelemetry 版即可开始监控应用,您可以查看应用拓扑、调用链路、异常事务、慢事务和SQL分析等一系列监控数据。本文介绍如何使用OpenTelemetry Python Agent/SDK进行自动或手动埋点并上报数据。

前提条件

获取接入点信息

  1. 登录ARMS控制台,在左侧导航栏单击接入中心

  2. 服务端应用区域单击OpenTelemetry卡片。

  3. 在弹出的OpenTelemetry面板中选择数据需要上报的地域。

    说明

    初次接入的地域将会自动进行资源初始化。

  4. 选择连接方式上报方式,然后复制接入点信息。

    • 连接方式:若您的服务部署在阿里云上,且所属地域与选择的接入地域一致,推荐使用阿里云内网方式,否则选择公网方式。

    • 上报方式:根据客户端支持的协议类型选择HTTPgRPC协议上报数据。

    image.png

背景信息

OpenTelemetry提供了若干自动埋点插件,支持为常见框架自动创建Span,支持的框架列表如下,完整信息请参见OpenTelemetry官方文档

说明

版本~= V.N表示≥ V.N== V.*,例如,aiohttp ~= 3.0表示aiohttp版本要求aiohttp ≥ 3.0aiohttp == 3.*

展开查看支持的Python框架

OpenTelemetry插件

支持的Package以及版本

opentelemetry-instrumentation-aio-pika

aio_pika - [7.2.0, 10.0.0)

opentelemetry-instrumentation-aiohttp-client

aiohttp ~= 3.0

opentelemetry-instrumentation-aiohttp-server

aiohttp ~= 3.0

opentelemetry-instrumentation-aiopg

aiopg - [0.13.0, 2.0.0)

opentelemetry-instrumentation-asgi

asgiref ~= 3.0

opentelemetry-instrumentation-asyncpg

asyncpg ≥ 0.12.0

opentelemetry-instrumentation-aws-lambda

aws_lambda

opentelemetry-instrumentation-boto

boto ~= 2.0

opentelemetry-instrumentation-boto3sqs

boto3 ~= 1.0

opentelemetry-instrumentation-botocore

botocore ~= 1.0

opentelemetry-instrumentation-cassandra

  • cassandra-driver ~= 3.25

  • scylla-driver ~= 3.25

opentelemetry-instrumentation-celery

celery - [4.0, 6.0)

opentelemetry-instrumentation-confluent-kafka

confluent-kafka - [1.8.2, 2.2.0]

opentelemetry-instrumentation-dbapi

dbapi

opentelemetry-instrumentation-django

django ≥ 1.10

opentelemetry-instrumentation-elasticsearch

elasticsearch ≥ 2.0

opentelemetry-instrumentation-falcon

falcon - [1.4.1, 4.0.0)

opentelemetry-instrumentation-fastapi

fastapi ~= 0.58

opentelemetry-instrumentation-flask

flask - [1.0, 3.0)

opentelemetry-instrumentation-grpc

grpcio ~= 1.27

opentelemetry-instrumentation-httpx

httpx ≥ 0.18.0

opentelemetry-instrumentation-jinja2

jinja2 - [2.7, 4.0)

opentelemetry-instrumentation-kafka-python

kafka-python ≥ 2.0

opentelemetry-instrumentation-logging

logging

opentelemetry-instrumentation-mysql

mysql-connector-python ~= 8.0

opentelemetry-instrumentation-mysqlclient

mysqlclient < 3

opentelemetry-instrumentation-pika

pika≥ 0.12.0

opentelemetry-instrumentation-psycopg2

psycopg2 ≥ 2.7.3.1

opentelemetry-instrumentation-pymemcache

pymemcache - [1.3.5, 5)

opentelemetry-instrumentation-pymongo

pymongo - [3.1, 5.0)

opentelemetry-instrumentation-pymysql

PyMySQL < 2

opentelemetry-instrumentation-pyramid

pyramid ≥ 1.7

opentelemetry-instrumentation-redis

redis ≥ 2.6

opentelemetry-instrumentation-remoulade

remoulade ≥ 0.50

opentelemetry-instrumentation-requests

requests ~= 2.0

opentelemetry-instrumentation-sqlalchemy

sqlalchemy

opentelemetry-instrumentation-sqlite3

sqlite3

opentelemetry-instrumentation-starlette

starlette ~= 0.13.0

opentelemetry-instrumentation-system-metrics

psutil ≥ 5

opentelemetry-instrumentation-tornado

tornado ≥ 5.1.1

opentelemetry-instrumentation-tortoiseorm

tortoise-orm ≥ 0.17.0

opentelemetry-instrumentation-urllib

urllib

opentelemetry-instrumentation-urllib3

urllib3 - [1.0.0, 3.0.0)

opentelemetry-instrumentation-wsgi

wsgi

示例Demo

示例代码仓库地址:python-opentelemetry-demo

方法一:通过Agent自动埋点并上报

  1. 下载所需包。

    pip install django
    pip install requests
    pip install opentelemetry-distro \
    	opentelemetry-exporter-otlp
     
    opentelemetry-bootstrap -a install
  2. 创建AutoAndManualDemo项目并创建HelloWorld应用。

    1. 创建AutoAndManualDemo项目。

      django-admin startproject AutoAndManualDemo
    2. 在项目中创建HelloWorld应用。

      cd AutoAndManualDemo
      
      # 在项目中创建helloworld app
      python manage.py startapp helloworld
  3. 修改HelloWorld应用代码。

    1. AutoAndManualDemo/helloworld/views.py文件中添加以下代码。

      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. 创建AutoAndManualDemo/helloworld/urls.py文件,并在urls.py文件中添加以下代码。

      from django.urls import path
      
      from . import views
      
      urlpatterns = [
          path('', views.hello_world_view, name='helloworld')
      ]
    3. 修改AutoAndManualDemo/AutoAndManualDemo/urls.py文件,添加helloworldURL。

      from django.contrib import admin
      from django.urls import path, include
      
      urlpatterns = [
       path('admin/', admin.site.urls),
       path('helloworld/', include('helloworld.urls')),
      ]
  4. 运行项目。

    通过HTTP上报,请将<your-service-name>替换为您的应用名,将<http-endpoint>替换为HTTP接入点。

    opentelemetry-instrument \
        --traces_exporter console,otlp_proto_http \
        --metrics_exporter none \
        --service_name <your-service-name> \
        --exporter_otlp_traces_endpoint <http-endpoint> \
        python manage.py runserver --noreload
    说明
    • --noreload:避免manage.main方法执行两次。

    • 如果运行报错CommandError: You must set settings.ALLOWED_HOSTS if DEBUG is False.,但AutoAndManualDemo/AutoAndManualDemo/settings.py中的DEBUGALLOWED_HOSTS均已配置正确,这是因为使用opentelemetry-instrument启动时使用了Django框架的默认配置文件(django/conf/global_settings.py), 因此需要添加export DJANGO_SETTINGS_MODULE=AutoAndManualDemo.settings环境变量。

  5. 在浏览器中访问http://127.0.0.1:8000/helloworld/,控制台会打印Trace,同时也会将Trace上报至阿里云可观测链路 OpenTelemetry 版

    如需关闭控制台打印Trace,只需将--traces_exporter参数配置为--traces_exporter otlp_proto_http

方法二:手动埋点并上报

  1. 下载所需包。

    pip install opentelemetry-api
    pip install opentelemetry-sdk
    pip install opentelemetry-exporter-otlp
  2. manual.py文件中设置OpenTelemetry初始化代码。

    • 请将代码中的<token><endpoint>替换成前提条件中获取的接入点信息。

    • 请根据实际情况替换代码中的<service-name>(服务名)和<host-name>(主机名)。

    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, Resource, HOST_NAME
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    
    def init_opentelemetry():
        # 设置服务名、主机名
        resource = Resource(attributes={
            SERVICE_NAME: "<service-name>",
            HOST_NAME: "<host-name>"
        })
        # 使用GRPC协议上报
        span_processor = BatchSpanProcessor(OTLPSpanGrpcExporter(
            endpoint="<endpoint>",
            headers=("Authentication=<token>")
        ))
        # 使用HTTP协议上报
        # span_processor = BatchSpanProcessor(OTLPSpanHttpExporter(
        #     endpoint="<endpoint>",
        # ))
        trace_provider = TracerProvider(resource=resource, active_span_processor=span_processor)
        trace.set_tracer_provider(trace_provider)
                            
  3. 创建Span。

    tracer = trace.get_tracer(__name__)  # 获取tracer
    with tracer.start_as_current_span("child_span") as child_span:  # 创建名为child_spanspan
        print("hello world")
  4. 获取TraceIDSpanID。

    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)
  5. 使用OpenTelemetry Baggage API透传业务自定义标签。

    创建baggage_parent_span时通过指定attributes参数来设置属性。

    def baggage_and_attribute_usage():
        tracer = trace.get_tracer(__name__)
        global_ctx = baggage.set_baggage("key", "value_from_global_ctx")  # 使用baggage api,在不同span之间传递数据
        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")
    
        # 获取不同contexkey对应的值
        print(baggage.get_baggage("key", global_ctx))
        print(baggage.get_baggage("key", parent_ctx))
        print(baggage.get_baggage("key", child_ctx))
        

    展开查看完整示例代码

    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, Resource, HOST_NAME
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    
    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")  # 使用baggage api,在不同span之间传递数据
        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))
    
    
    
    def init_opentelemetry():
        # 设置服务名、主机名
        resource = Resource(attributes={
            SERVICE_NAME: "<service-name>",
            HOST_NAME: "<host-name>"
        })
        # 使用GRPC协议上报
        span_processor = BatchSpanProcessor(OTLPSpanGrpcExporter(
            endpoint="<endpoint>",
            headers=("Authentication=<token>")
        ))
        # 使用HTTP协议上报
        # span_processor = BatchSpanProcessor(OTLPSpanHttpExporter(
        #     endpoint="<endpoint>",
        # ))
        trace_provider = TracerProvider(resource=resource, active_span_processor=span_processor)
        trace.set_tracer_provider(trace_provider)
    
    
    if __name__ == '__main__':
        init_opentelemetry()
        outer_method()
        baggage_and_attribute_usage()
  6. 运行程序。

    python manual.py

方法三:在自动埋点基础上手动埋点

如果您需要在使用OpenTelemetry获得自动埋点能力的同时,添加自定义业务埋点,请在方法一的基础上完成以下操作。

  1. 下载包。

    pip install django
    pip install requests
    pip install opentelemetry-sdk
    pip install opentelemetry-instrumentation-django
    pip install opentelemetry-exporter-otlp 
  2. 创建AutoAndManualDemo项目并创建HelloWorld应用。

    1. 创建AutoAndManualDemo项目。

      django-admin startproject AutoAndManualDemo
    2. 在项目中创建HelloWorld应用。

      cd AutoAndManualDemo
      
      # 在项目中创建helloworld app
      python manage.py startapp helloworld
  3. 修改helloworld/views.py文件代码。

    获取tracer并手动创建span,同时设置span名称。

    from django.http import HttpResponse
    from opentelemetry import trace
    from datetime import datetime
    
    
    # Create your views here.
    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__)
        # 创建新的span
        with tracer.start_as_current_span("time_span") as time_span:
            return now.strftime("%H:%M:%S")
  4. 修改urls.py文件。

    1. 创建helloworld/urls.py文件,在urls.py中添加以下代码。

      from django.urls import path
      
      from . import views
      
      urlpatterns = [
          path('', views.hello_world_view, name='helloworld')
      ]
    2. 修改AutoAndManualDemo/AutoAndManualDemo/urls.py文件,添加helloworldURL。

      from django.contrib import admin
      from django.urls import path, include
      
      urlpatterns = [
       path('admin/', admin.site.urls),
       path('helloworld/', include('helloworld.urls')),
      ]
  5. 修改manage.py文件代码,在应用初始化的代码中添加以下内容。

    from opentelemetry import trace
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter  # 通过gRPC接入
    # from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter # 通过HTTP接入
    from opentelemetry.sdk.resources import SERVICE_NAME, Resource, HOST_NAME
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
    
    
    
    resource = Resource(attributes={
            SERVICE_NAME: "<service-name>",
            HOST_NAME: "<host-name>"
    })
    trace.set_tracer_provider(TracerProvider(resource=resource))
    trace.get_tracer_provider().add_span_processor(
        BatchSpanProcessor(OTLPSpanExporter(
            endpoint="<endpoint>",
            headers="Authentication=<token>" # 通过gRPC接入时需要headers参数,通过HTTP接入时不需要此参数
    )))  # 通过 OTLPSpanExporter 上报Trace
    trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter()))  # 在控制台输出Trace

    请替换以下内容,并根据接入方式(gRPC或者HTTP)修改代码:

    • <service-name>: 需要上报的服务名

    • <host-name>:主机名

    • <endpoint>:通过HTTP/gRPC上报数据的接入点

    • <token>:通过gRPC上报数据的鉴权Token

  6. 运行项目。

    python manage.py runserver --noreload
    • --noreload防止manage.main方法执行两次。

    • 如果运行时出现ImportError(symbol not found in flat namespace '_CFRelease'),请下载并安装grpcio包。

      pip install grpcio
  7. 在浏览器中访问127.0.0.1:8000/helloworld,链路数据便会上报至可观测链路 OpenTelemetry 版控制台。

查看监控数据

登录ARMS控制台后,在应用监控 > 应用列表页面选择目标应用,查看链路数据。

说明

语言列显示image图标的应用为接入应用监控的应用,显示-图标的应用为接入可观测链路 OpenTelemetry 版的应用。