全部產品
Search
文件中心

Simple Log Service:通過OpenTelemetry接入Python Trace資料

更新時間:Oct 25, 2024

本文介紹通過OpenTelemetry Python SDK將Python應用的Trace資料接入到Log Service的操作步驟。

前提條件

  • 已建立Trace執行個體。更多資訊,請參見建立Trace執行個體
  • 已安裝Python 3.7及以上版本。

  • 已在Python上安裝OpenTelemetry Python SDK。

    如果未安裝,可使用以下命令進行安裝。

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

接入流程

  1. 初始化OpenTelemetry Provider。

  2. 判斷是否符合半自動接入條件。

    • 如果符合,則您可以使用半自動方式接入Trace資料。

      當半自動方式無法覆蓋您的所有情境時,餘下情境您需要使用手動方式接入Trace資料。

    • 如果不符合,則您可以使用手動方式接入Trace資料。

步驟1:初始化OpenTelemetry Provider

您可以通過如下程式碼完成OpenTelemetry Provider的初始化。其中,代碼中的變數需根據實際情況替換。關於變數的詳細說明,請參見變數說明

# For Opentelemetry
import socket
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.trace.export import ConsoleSpanExporter
from opentelemetry.sdk.trace.export import SimpleSpanProcessor


class OpenTelemetrySLSProvider(object):

    def __init__(self, namespace="", service="", version="", endpoint='stdout',
                 project=None, instance=None, access_key_id=None, access_key_secret=None):
        '''
        :param namespace: Your service namespace
        :param service: Your Application Service Name
        :param version: Your Application Version
        :param endpoint: console or https://sls_endpoint:10010
        :param project: SLS project
        :param instance: SLS OTEL InstanceId
        :param access_key_id: Aliyun AccesskeyId
        :param access_key_secret:  Aliyun AccesskeySecret
        '''
        self.sls_otel_endpoint = endpoint
        self.sls_otel_project = project
        self.sls_otel_akid = access_key_id
        self.sls_otel_aksecret = access_key_secret
        self.sls_otel_instanceid = instance
        self.local_mode = False
        if endpoint == "stdout":
            self.local_mode = True
            self.resource = Resource(attributes={
                "host.name": socket.gethostname(),
                "service.name": service,
                "service.namespace": namespace,
                "service.version": version})
        else:
            self.resource = Resource(attributes={
                "host.name": socket.gethostname(),
                "service.namespace": namespace,
                "service.name": service,
                "service.version": version,
                "sls.otel.project": self.sls_otel_project,
                "sls.otel.akid": self.sls_otel_akid,
                "sls.otel.aksecret": self.sls_otel_aksecret,
                "sls.otel.instanceid": self.sls_otel_instanceid
            })

    def initTracer(self):
        trace.set_tracer_provider(TracerProvider(resource=self.resource))
        if self.local_mode:
            trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
        else:
            otlp_exporter = OTLPSpanExporter(endpoint=self.sls_otel_endpoint)
            trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))

# debug mode
#sls_ot_provider = OpenTelemetrySLSProvider(service="example", version="v0.1.0")

# write to sls
sls_ot_provider = OpenTelemetrySLSProvider(namespace="${service.namespace}", service="${service}", version="${version}",
                                         endpoint='${endpoint}',
                                         project="${project}",
                                         instance="${instance}",
                                         access_key_id="${access-key-id}",
                                         access_key_secret="${access-key-secret}"
                                         )

表 1. 變數說明

變數

說明

樣本

${service.namespace}

服務歸屬的命名空間。

order

${service}

服務名。根據您的實際情境取值即可。

payment

${version}

服務版本號碼。建議按照va.b.c格式定義。

v0.1.2

${endpoint}

Log ServiceProject的接入地址,格式為https://${project}.${region-endpoint}:Port,其中:

  • ${project}:Log ServiceProject名稱。

  • ${region-endpoint}:Log ServiceProject所在地區的訪問網域名稱,支援公網和阿里雲內網(傳統網路、VPC)。更多資訊,請參見服務入口

  • Port:網路連接埠,固定為10010。

說明
  • 如果配置為stdout,即endpoint='stdout',表示將資料列印到標準輸出。

  • 如果配置為空白值,表示不上傳Trace資料到Log Service。

https://test-project.cn-hangzhou.log.aliyuncs.com:10010

${project}

Log ServiceProject名稱。

test-project

${instance}

Trace服務執行個體ID。更多資訊,請參見建立Trace執行個體

test-traces

${access-key-id}

阿里雲帳號AccessKey ID。

建議您使用只具備Log ServiceProject寫入許可權的RAM使用者的AccessKey(包括AccessKey ID和AccessKey Secret)。授予RAM使用者向指定Project寫入資料許可權的具體操作,請參見授權。如何擷取AccessKey的具體操作,請參見存取金鑰

${access-key-secret}

阿里雲帳號AccessKey Secret。

建議您使用只具備Log ServiceProject寫入許可權的RAM使用者的AccessKey。

步驟2:接入資料

您可以通過半自動或手動方式接入Trace資料到Log Service。OpenTelemetry Python SDK提供多種類型的instrumentation包,支援常用架構的半自動埋點,如果您使用的是指定的instrumentation包,則支援通過半自動方式接入資料。

  • 半自動埋點

    此處以flask和requests為例。

    1. 安裝instrumentation包。

      pip install requests
      pip install flask
      pip install opentelemetry-instrumentation-flask
      pip install opentelemetry-instrumentation-requests
    2. 運行代碼。

      如下代碼中的變數需根據實際情況替換。更多資訊,請參見變數說明

      # for flask
      import flask
      import requests
      
      # for Opentelemetry instrumentation
      import socket
      from opentelemetry.instrumentation.flask import FlaskInstrumentor
      from opentelemetry.instrumentation.requests import RequestsInstrumentor
      
      # For Opentelemetry pip install opentelemetry-instrumentation-requests
      from opentelemetry import trace
      from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
          OTLPSpanExporter,
      )
      from opentelemetry.sdk.resources import Resource
      from opentelemetry.sdk.trace import TracerProvider
      from opentelemetry.sdk.trace.export import BatchSpanProcessor
      from opentelemetry.sdk.trace.export import ConsoleSpanExporter
      from opentelemetry.sdk.trace.export import SimpleSpanProcessor
      
      
      class OpenTelemetrySLSProvider(object):
      
          def __init__(self, namespace="", service="", version="", endpoint='stdout',
                       project=None, instance=None, access_key_id=None, access_key_secret=None):
              '''
              :param namespace: Your service namespace
              :param service: Your Application Service Name
              :param version: Your Application Version
              :param endpoint: console or https://sls_endpoint:10010
              :param project: SLS project
              :param instance: SLS OTEL InstanceId
              :param access_key_id: Aliyun AccesskeyId
              :param access_key_secret:  Aliyun AccesskeySecret
              '''
              self.sls_otel_endpoint = endpoint
              self.sls_otel_project = project
              self.sls_otel_akid = access_key_id
              self.sls_otel_aksecret = access_key_secret
              self.sls_otel_instanceid = instance
              self.local_mode = False
              if endpoint == "stdout":
                  self.local_mode = True
                  self.resource = Resource(attributes={
                      "host.name": socket.gethostname(),
                      "service.name": service,
                      "service.namespace": namespace,
                      "service.version": version})
              else:
                  self.resource = Resource(attributes={
                      "host.name": socket.gethostname(),
                      "service.name": service,
                      "service.version": version,
                      "service.namespace": namespace,
                      "sls.otel.project": self.sls_otel_project,
                      "sls.otel.akid": self.sls_otel_akid,
                      "sls.otel.aksecret": self.sls_otel_aksecret,
                      "sls.otel.instanceid": self.sls_otel_instanceid
                  })
      
          def initTracer(self):
              trace.set_tracer_provider(TracerProvider(resource=self.resource))
              if self.local_mode:
                  trace.get_tracer_provider().add_span_processor(
                      SimpleSpanProcessor(ConsoleSpanExporter()))
              else:
                  otlp_exporter = OTLPSpanExporter(endpoint=self.sls_otel_endpoint)
                  trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))
      
      
      # write to sls
      sls_ot_provider = OpenTelemetrySLSProvider(namespace="${service.namespace}", service="${service}", version="${version}",
                                                 endpoint='${endpoint}',
                                                 project="${project}",
                                                 instance="${instance}",
                                                 access_key_id="${access-key-id}",
                                                 access_key_secret="${access-key-secret}"
                                                 )
      # for console debug
      #sls_ot_provider = OpenTelemetrySLSProvider(service="example", version="v0.1.0")
      
      sls_ot_provider.initTracer()
      
      # flask init
      app = flask.Flask(__name__)
      
      # instrumentation init
      FlaskInstrumentor().instrument_app(app)
      RequestsInstrumentor().instrument()
      
      
      @app.route("/")
      def hello():
          tracer = trace.get_tracer(__name__)
          with tracer.start_as_current_span("request_server"):
              requests.get("http://www.taobao.com")
          return "hello"
      
      
      app.run(debug=True, port=5000)
    3. 訪問服務,觸發Trace資料產生並發送。

      127.0.0.1:5000/hello
  • 手動埋點

    運行如下代碼,其中代碼中的變數需根據實際情況替換。關於代碼的詳細說明,請參見變數說明

    # ot-manual-example.py
    import time
    
    # For Opentelemetry
    import socket
    from opentelemetry import trace
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    from opentelemetry.sdk.trace.export import ConsoleSpanExporter
    from opentelemetry.sdk.trace.export import SimpleSpanProcessor
    
    
    class OpenTelemetrySLSProvider(object):
    
        def __init__(self, namespace="", service="", version="", endpoint='stdout',
                     project=None, instance=None, access_key_id=None, access_key_secret=None):
            '''
            :param service: Your service namespace
            :param service: Your Application Service Name
            :param version: Your Application Version
            :param endpoint: console or https://sls_endpoint:10010
            :param project: SLS project
            :param instance: SLS OTEL InstanceId
            :param access_key_id: Aliyun AccesskeyId
            :param access_key_secret:  Aliyun AccesskeySecret
            '''
            self.sls_otel_endpoint = endpoint
            self.sls_otel_project = project
            self.sls_otel_akid = access_key_id
            self.sls_otel_aksecret = access_key_secret
            self.sls_otel_instanceid = instance
            self.local_mode = False
            if endpoint == "stdout":
                self.local_mode = True
                self.resource = Resource(attributes={
                    "host.name": socket.gethostname(),
                    "service.name": service,
                    "service.namespace": namespace,
                    "service.version": version})
            else:
                self.resource = Resource(attributes={
                    "host.name": socket.gethostname(),
                    "service.name": service,
                    "service.version": version,
                    "service.namespace": namespace,
                    "sls.otel.project": self.sls_otel_project,
                    "sls.otel.akid": self.sls_otel_akid,
                    "sls.otel.aksecret": self.sls_otel_aksecret,
                    "sls.otel.instanceid": self.sls_otel_instanceid
                })
    
        def initTracer(self):
            trace.set_tracer_provider(TracerProvider(resource=self.resource))
            if self.local_mode:
                trace.get_tracer_provider().add_span_processor(SimpleSpanProcessor(ConsoleSpanExporter()))
            else:
                otlp_exporter = OTLPSpanExporter(endpoint=self.sls_otel_endpoint)
                trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(otlp_exporter))
    
    # write to sls
    sls_ot_provider = OpenTelemetrySLSProvider(namespace="${service.namespace}", service="${service}", version="${version}",
                                               endpoint='${endpoint}',
                                               project="${project}",
                                               instance="${instance}",
                                               access_key_id="${access-key-id}",
                                               access_key_secret="${access-key-secret}"
                                               )
    
    # for console debug
    #sls_ot_provider = OpenTelemetrySLSProvider(service="example", version="v0.1.0")
    
    # Trace Example
    sls_ot_provider.initTracer()
    tracer = trace.get_tracer(__name__)
    
    with tracer.start_as_current_span("foo"):
        print("Hello world!")
    
    
    labels = {"environment": "staging"}
    
    time.sleep(60)

常見問題

如何檢查是否正確安裝OpenTelemetry SDK?

以如下代碼為例,將如下代碼儲存為tracing.py,執行tracing.py命令,如果正常輸出則表示Python OpenTelemetry相關的基礎依賴安裝正確。

# tracing-example-1.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    ConsoleSpanExporter,
    SimpleSpanProcessor,
)

trace.set_tracer_provider(TracerProvider())
trace.get_tracer_provider().add_span_processor(
    SimpleSpanProcessor(ConsoleSpanExporter())
)

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("foo"):
    print("Hello world!")

python trace

後續步驟