本文介绍Python运行环境的链路追踪相关内容。

背景信息

阿里云链路追踪服务(Tracing Analysis)基于OpenTracing标准,兼容开源社区,为分布式应用的开发者提供了完整地分布式调用链查询和诊断、分布式拓扑动态发现、应用性能实时汇总等功能。

函数计算与链路追踪集成后,支持使用Jaeger SDKOpenTelemetry上传链路信息,使您能够跟踪函数的执行,帮助您快速分析和诊断Serverless架构下的性能瓶颈,提高Serverless场景的开发诊断效率。

功能简介

您可以在函数计算控制台配置链路追踪。具体操作,请参见配置链路追踪

为服务开启链路追踪后,函数计算会自动记录请求在系统侧的耗时,包含冷启动耗时、Initializer函数的耗时和函数的执行时间等。关于下图中系统Span的说明,请参见Span名称说明链路追踪

如您还需查看函数内业务侧的耗时,例如,在函数内访问RDS,NAS等服务的耗时,可以通过创建自定义Span来实现。

示例代码

函数计算的链路分析基于OpenTracing协议的Jaeger实现,Python运行时提供使用OpenTelemetry的方式自定义Span。

在Python语言的代码中,您可以通过OpenTelemetry SDK手动埋点将数据上报到链路追踪服务端。完整的示例代码,请参见python-tracing-openTelemetry

示例代码解析如下。
  • 在工程目录中配置依赖文件requirements.txt
    opentelemetry-api==1.12.0
    opentelemetry-sdk==1.12.0
    opentelemetry-exporter-jaeger==1.12.0
  • 上报数据到链路追踪服务端。
    trace.set_tracer_provider(
        TracerProvider(
            resource=Resource.create({SERVICE_NAME: "my-helloworld-service"})
        )
    )
    tracer = trace.get_tracer(__name__)
    def handler(event, context):
        init_tracer(context.tracing.jaeger_endpoint)
        span_context = get_fc_span(context.tracing.span_context)
        start_my_span(trace.set_span_in_context(NonRecordingSpan(span_context)))
        return 'hello world'
  • 初始化一个tracer对象,提供对Tracers的访问。
    def init_tracer(endpoint):
        jaeger_exporter = JaegerExporter(
            collector_endpoint=endpoint
        )
    
        span_processor = SimpleSpanProcessor(jaeger_exporter)
    
        trace.get_tracer_provider().add_span_processor(span_processor)
  • 获取上下文的Tracing信息,转换为SpanContext对象。
    def get_fc_span(jaeger_span_context):
        jaeger_span_context_arr = jaeger_span_context.split(":")
        tid = int(jaeger_span_context_arr[0], 16)
        sid = int(jaeger_span_context_arr[1], 16)
    
        span_context = trace.SpanContext(
            trace_id=tid,
            span_id=sid,
            is_remote=True,
            trace_flags=trace.TraceFlags(0x01),
        )
        return span_context
  • 创建tracer并通过转换的Context创建子Span。每一个Span代表调用链中被命名并计时的连续性执行片段,您也可以基于该Span继续创建子Span。
    def start_my_span(context):
        with tracer.start_as_current_span(name="fc-operation", context=context):
            time.sleep(0.15)
            with tracer.start_as_current_span("child"):
                time.sleep(0.1)