仪表化

OpenTelemetry Python 手动插装

Instrumention 是指您自己向应用程序添加可观察性代码的过程。

如果您正在 instrumenting 应用程序,您需要为您使用的语言使用 OpenTelemetry SDK。然后,您将使用 SDK 初始化 OpenTelemetry,并使用 API instrument 您的代码。这将从您的应用程序和任何也带有 instrumention 的已安装库中发出遥测数据。

如果您正在 instrumenting 库,请仅安装您语言的 OpenTelemetry API 包。您的库不会自行发出遥测数据。只有当它作为使用 OpenTelemetry SDK 的应用程序的一部分时,它才会发出遥测数据。有关 instrumenting 库的更多信息,请参阅 Libraries

有关 OpenTelemetry API 和 SDK 的更多信息,请参阅 specification

设置

首先,确保您已安装 API 和 SDK 包

pip install opentelemetry-api
pip install opentelemetry-sdk

追踪

获取 Tracer

要开始追踪,您需要初始化一个 TracerProvider 并可选地将其设置为全局默认值。

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import (
    BatchSpanProcessor,
    ConsoleSpanExporter,
)

provider = TracerProvider()
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)

# Sets the global default tracer provider
trace.set_tracer_provider(provider)

# Creates a tracer from the global tracer provider
tracer = trace.get_tracer("my.tracer.name")

创建 Span

要创建一个 Span,通常希望将其作为当前 Span 启动。

def do_work():
    with tracer.start_as_current_span("span-name") as span:
        # do some work that 'span' will track
        print("doing some work...")
        # When the 'with' block goes out of scope, 'span' is closed for you

您也可以使用 start_span 创建一个 Span,而不将其设为当前 Span。这通常用于跟踪并发或异步操作。

创建嵌套 Span

如果您有一个需要作为另一个 Span 的一部分来跟踪的明确的子操作,则可以创建 Span 来表示这种关系。

def do_work():
    with tracer.start_as_current_span("parent") as parent:
        # do some work that 'parent' tracks
        print("doing some work...")
        # Create a nested span to track nested work
        with tracer.start_as_current_span("child") as child:
            # do some work that 'child' tracks
            print("doing some nested work...")
            # the nested span is closed when it's out of scope

        # This span is also closed when it goes out of scope

当您在追踪可视化工具中查看 Span 时,child 将被跟踪为 parent 下的嵌套 Span。

使用装饰器创建 Span

通常需要一个单独的 Span 来跟踪整个函数的执行。在这种情况下,有一个装饰器可以用于减少代码量。

@tracer.start_as_current_span("do_work")
def do_work():
    print("doing some work...")

使用此装饰器等同于在 do_work() 内部创建 Span,并在 do_work() 完成时结束它。

要使用此装饰器,您必须在函数声明的全局范围内有一个 tracer 实例。

获取当前 Span

有时,访问当前 Span 的状态会很有帮助,以便您可以为其添加更多信息。

from opentelemetry import trace

current_span = trace.get_current_span()
# enrich 'current_span' with some information

向 Span 添加属性

Attributes 允许您将键/值对附加到 Span,以便它携带有关正在跟踪的当前操作的更多信息。

from opentelemetry import trace

current_span = trace.get_current_span()

current_span.set_attribute("operation.value", 1)
current_span.set_attribute("operation.name", "Saying hello!")
current_span.set_attribute("operation.other-stuff", [1, 2, 3])

添加语义属性

Semantic Attributes 是预定义的 Attributes,它们是常见数据类型的知名命名约定。使用语义属性可以跨系统统一此类信息。

要在 Python 中使用语义属性,请确保您已安装语义约定包。

pip install opentelemetry-semantic-conventions

然后,您可以在代码中使用它。

from opentelemetry import trace
from opentelemetry.semconv.trace import SpanAttributes

// ...

current_span = trace.get_current_span()
current_span.set_attribute(SpanAttributes.HTTP_METHOD, "GET")
current_span.set_attribute(SpanAttributes.HTTP_URL, "https://opentelemetry.org.cn/")

添加事件

An event is a human-readable message on a span that represents “something happening” during its lifetime. You can think of it as a primitive log.

from opentelemetry import trace

current_span = trace.get_current_span()

current_span.add_event("Gonna try it!")

# Do the thing

current_span.add_event("Did it!")

A span can be created with zero or more span links that causally link it to another span. A link needs a span context to be created.

from opentelemetry import trace

tracer = trace.get_tracer(__name__)

with tracer.start_as_current_span("span-1"):
    # Do something that 'span-1' tracks.
    ctx = trace.get_current_span().get_span_context()
    link_from_span_1 = trace.Link(ctx)

with tracer.start_as_current_span("span-2", links=[link_from_span_1]):
    # Do something that 'span-2' tracks.
    # The link in 'span-2' is causally associated it with the 'span-1',
    # but it is not a child span.
    pass

设置跨度状态

可以在 Span 上设置 Status,通常用于指定 Span 未成功完成 - Error。默认情况下,所有 spans 的状态都是 Unset,这意味着 span 完成时没有错误。Ok 状态保留给您需要明确将 span 标记为成功而不是坚持默认的 Unset(即,“无错误”)的情况。

可以在 span 完成之前的任何时间设置状态。

from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode

current_span = trace.get_current_span()

try:
    # something that might fail
except:
    current_span.set_status(Status(StatusCode.ERROR))

在 Span 中记录异常

记录发生的异常是一个好主意。建议将其与设置 Span 状态结合使用。

from opentelemetry import trace
from opentelemetry.trace import Status, StatusCode

current_span = trace.get_current_span()

try:
    # something that might fail

# Consider catching a more specific exception in your code
except Exception as ex:
    current_span.set_status(Status(StatusCode.ERROR))
    current_span.record_exception(ex)

更改默认传播格式

默认情况下,OpenTelemetry Python 将使用以下传播格式:

  • W3C Trace Context
  • W3C Baggage

如果您需要更改默认设置,可以通过环境变量或代码进行更改。

使用环境变量

您可以使用逗号分隔的列表设置 OTEL_PROPAGATORS 环境变量。可接受的值为:

  • "tracecontext":W3C Trace Context
  • "baggage":W3C Baggage
  • "b3":B3 Single
  • "b3multi":B3 Multi
  • "jaeger":Jaeger
  • "xray":AWS X-Ray(第三方)
  • "ottrace":OT Trace(第三方)
  • "none":无自动配置的传播器。

默认配置等同于 OTEL_PROPAGATORS="tracecontext,baggage"

使用 SDK API

或者,您也可以在代码中更改格式。

例如,如果您需要改用 Zipkin 的 B3 传播格式,可以安装 B3 包。

pip install opentelemetry-propagator-b3

然后在您的追踪初始化代码中设置 B3 传播器。

from opentelemetry.propagate import set_global_textmap
from opentelemetry.propagators.b3 import B3Format

set_global_textmap(B3Format())

请注意,环境变量将覆盖代码中配置的内容。

深入阅读

指标

要开始收集指标,您需要初始化一个 MeterProvider 并可选地将其设置为全局默认值。

from opentelemetry import metrics
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import (
    ConsoleMetricExporter,
    PeriodicExportingMetricReader,
)

metric_reader = PeriodicExportingMetricReader(ConsoleMetricExporter())
provider = MeterProvider(metric_readers=[metric_reader])

# Sets the global default meter provider
metrics.set_meter_provider(provider)

# Creates a meter from the global meter provider
meter = metrics.get_meter("my.meter.name")

创建和使用同步 Instruments

Instruments 用于测量您的应用程序。Synchronous instruments 用于与应用程序/业务处理逻辑内联,例如在处理请求或调用其他服务时。

首先,创建您的 instrument。Instruments 通常在模块或类级别创建一次,然后与业务逻辑内联使用。此示例使用 Counter instrument 来计算已完成工作项的数量。

work_counter = meter.create_counter(
    "work.counter", unit="1", description="Counts the amount of work done"
)

使用 Counter 的 add operation,下面的代码将计数器加一,使用工作项的类型作为属性。

def do_work(work_item):
    # count the work being doing
    work_counter.add(1, {"work.type": work_item.work_type})
    print("doing some work...")

创建和使用异步 Instruments

Asynchronous instruments 为用户提供了一种注册回调函数的方式,这些函数会在需要时被调用以进行测量。这对于定期测量无法直接插装的值非常有用。Async instruments 在创建时带有零个或多个回调,这些回调将在指标收集期间被调用。每个回调都接受 SDK 提供的选项并返回其观测值。

此示例使用 Asynchronous Gauge instrument 来报告配置服务器通过抓取 HTTP 端点提供的当前配置版本。首先,编写一个回调函数来执行观测。

from typing import Iterable
from opentelemetry.metrics import CallbackOptions, Observation


def scrape_config_versions(options: CallbackOptions) -> Iterable[Observation]:
    r = requests.get(
        "http://configserver/version_metadata", timeout=options.timeout_millis / 10**3
    )
    for metadata in r.json():
        yield Observation(
            metadata["version_num"], {"config.name": metadata["version_num"]}
        )

请注意,OpenTelemetry 会将包含超时信息的选项传递给您的回调。回调函数应遵守此超时设置,以避免无限期阻塞。最后,使用回调函数创建 instrument 并注册它。

meter.create_observable_gauge(
    "config.version",
    callbacks=[scrape_config_versions],
    description="The active config version for each configuration",
)

进一步阅读

日志

日志 API & SDK 目前正在开发中。要开始收集日志,您需要初始化一个 LoggerProvider 并可选地将其设置为全局默认值。然后使用 Python 内置的 logging 模块创建 OpenTelemetry 可以处理的日志记录。

import logging
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter # ConsoleLogExporter on versions earlier than 1.39.0
from opentelemetry._logs import set_logger_provider

provider = LoggerProvider()
processor = BatchLogRecordProcessor(ConsoleLogRecordExporter())
provider.add_log_record_processor(processor)
# Sets the global default logger provider
set_logger_provider(provider)

handler = LoggingHandler(level=logging.INFO, logger_provider=provider)
logging.basicConfig(handlers=[handler], level=logging.INFO)

logging.getLogger(__name__).info("This is an OpenTelemetry log record!")

进一步阅读

下一步

您还需要配置适当的 exporter 以将您的遥测数据 导出到一个或多个遥测后端。