opentelemetry-python 运行机制

iBit程序猿 2022年03月13日 2,232次浏览

概述

OpenTelemetry 是一组标准和工具的集合,旨在管理观测类数据,如 trace、metrics、logs 等 (未来可能有新的观测类数据类型出现)。

OpenTelemetry 提供与 vendor 无关的实现,根据用户的需要将观测类数据导出到不同的后端,如开源的 Prometheus、Jaeger 或云厂商的服务中。

本文介绍 opentelemetry 在 python 应用中的使用和运行机制。

示例

文档:https://opentelemetry-python.readthedocs.io/en/latest/examples/auto-instrumentation/README.html

安装好相关的依赖:

$ pip install opentelemetry-sdk
$ pip install opentelemetry-instrumentation
$ pip install opentelemetry-instrumentation-flask
$ pip install flask
$ pip install requests

启动应用程序:opentelemetry-instrument python server_uninstrumented.py

应用程序仅需要进行简单的trace初始化,不需任何的手工instrument,或者创建span的代码。opentelemetry-instrumentation-flask库会自动插入处理trace的相关逻辑。

from flask import Flask, request

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

app = Flask(__name__)

trace.set_tracer_provider(TracerProvider())

trace.get_tracer_provider().add_span_processor(
    BatchSpanProcessor(ConsoleSpanExporter())
)


@app.route("/server_request")
def server_request():
    print(request.args.get("param"))
    return "served"


if __name__ == "__main__":
    app.run(port=8082)

opentelemetry-instrument命令

上面示例中使用了 opentelemetry-instrument 命令进行应用程序启用,那么 opentelemetry-instrument 的作用是什么呢?

opentelemetry-instrument核心功能是将 sitecustomize 模块的路径添加到PYTHONPATH头部,以便sitecustomize可以再程序启动时被执行。

这个脚本的代码:

import re
import sys
from opentelemetry.instrumentation.auto_instrumentation import run
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(run())

它主要是调用auto_instrumentation的run函数,在run函数中主要是解析参数,设置环境变量,最重要的环境变量为PYTHONPATH,他将opentelemetry.instrumentation.auto_instrumentation添加为 PYTHONPATH 的头部。

sitecustomize

sitecustomize机制用于解决python启动时自动执行instrument的问题。

在opentelemetry.instrumentation.auto_instrumentation 目录下有一个 sitecustomize.py文件,这个文件中的内容就是auto_instrumentation的处理逻辑。那么sitecustomize是何时调用的呢?site模块会在初始化阶段自动导入,详细请查看:https://docs.python.org/3/library/site.html

在sitecustomize中会通过pkg_resources.iter_entry_points扫描 opentelemetry_instrumentor 加载那些已经安装的instrumentor,比如:django、flask等。

entry_points

entry_points机制用于解决如何自动找到依赖库的instrumentation库

例如:

opentelemetry-instrumentation-django

[options.entry_points]
opentelemetry_instrumentor =
    django = opentelemetry.instrumentation.django:DjangoInstrumentor

文档:

distro

上面的示例中,应用程序还需要自己进行 TracerProvider 的初始化,否则不会输出任何span记录。那么是否可以由 opentelemetry-instrument 完成这部分工作呢?

可以的,distro就是用于解决初始化TracerProvider和配置SpanExporter的问题的。

在sitecustomize 中加载的entry_points除了opentelemetry_instrumentor还有 opentelemetry_distro和opentelemetry_configurator。

opentelemetry-distro

[options.entry_points]
opentelemetry_distro =
    distro = opentelemetry.distro:OpenTelemetryDistro
opentelemetry_configurator =
    configurator = opentelemetry.distro:OpenTelemetryConfigurator

从上面的setup.cfg可以知道opentelemetry-distro 会增加opentelemetry_distro 和 opentelemetry_configurator 两个entry_points。

代码如下:

import os

from opentelemetry.environment_variables import OTEL_TRACES_EXPORTER
from opentelemetry.instrumentation.distro import BaseDistro
from opentelemetry.sdk._configuration import _OTelSDKConfigurator


class OpenTelemetryConfigurator(_OTelSDKConfigurator):
    pass


class OpenTelemetryDistro(BaseDistro):
    """
    The OpenTelemetry provided Distro configures a default set of
    configuration out of the box.
    """

    # pylint: disable=no-self-use
    def _configure(self, **kwargs):
        os.environ.setdefault(OTEL_TRACES_EXPORTER, "otlp_proto_grpc_span")

OpenTelemetryDistro 区别于 DefaultDistro 仅仅是设置了默认的 “OTEL_TRACES_EXPORTER”环境变量。

而 OpenTelemetryConfigurator 的作用就是进行trace初始化(完成 TracerProvider的设置),代码如下:

def _init_tracing(
    exporters: Sequence[SpanExporter], id_generator: IdGenerator
):
    # if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
    # from the env variable else defaults to "unknown_service"
    provider = TracerProvider(
        id_generator=id_generator(),
    )
    trace.set_tracer_provider(provider)

    for _, exporter_class in exporters.items():
        exporter_args = {}
        provider.add_span_processor(
            BatchSpanProcessor(exporter_class(**exporter_args))
        )

综上

安装以下库:

$ pip install opentelemetry-sdk
$ pip install opentelemetry-instrumentation
$ pip install opentelemetry-instrumentation-flask
$ pip install opentelemetry-distro
$ pip install opentelemetry-exporter-otlp

应用程序代码如下:

from flask import Flask, request

app = Flask(__name__)

@app.route("/server_request")
def server_request():
    print(request.args.get("param"))
    return "served"


if __name__ == "__main__":
    app.run(port=8082)

启动opentelemetry-collector:

docker run -p 4317:4317 \
    -v /tmp/otel-collector-config.yaml:/etc/otel-collector-config.yaml \
    otel/opentelemetry-collector:latest \
    --config=/etc/otel-collector-config.yaml

运行应用程序:

opentelemetry-instrument  python server.py

运行客户端程序:

python client.py testing

测试结果:opentelemetry-collector会输出收到的span

这样整个应用程序就完全不需要写任何代码了。

版权申明

本文转载于【Timmy Yuan 伏特加空间】5分钟实现用docker搭建Redis集群模式和哨兵模式,仅用于学习,版权归作者所有,如有侵权烦请告知,我会立即删除并表示歉意,联系邮箱