排查 Python 自动检测问题

安装问题

Python 包安装失败

Python 包的安装需要 gccgcc-c++,如果您运行的是精简版的 Linux,例如 CentOS,则可能需要安装它们。

yum -y install python3-devel
yum -y install gcc-c++
apt install -y python3-dev
apt install -y build-essential
apk add python3-dev
apk add build-base

使用 uv 进行引导

当使用 uv 包管理器时,运行 opentelemetry-bootstrap -a install 可能会导致依赖项设置出错或出现意外情况。

相反,您可以动态生成 OpenTelemetry 的依赖项,并使用 uv 进行安装。

首先,安装适当的包(或将其添加到项目文件中并运行 uv sync

uv pip install opentelemetry-distro opentelemetry-exporter-otlp

现在,您可以安装自动检测

uv run opentelemetry-bootstrap -a requirements | uv pip install --requirement -

最后,使用 uv run 启动应用程序(请参阅 配置代理

uv run opentelemetry-instrument python myapp.py

请注意,每次运行 uv sync 或更新现有包时,都必须重新安装自动检测。因此,建议将安装作为构建管道的一部分。

检测问题

Flask 调试模式下的重新加载器会破坏检测

可以在 Flask 应用中这样启用调试模式

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

调试模式会通过启用重新加载器来破坏检测的进行。要运行检测而调试模式又启用,请将 use_reloader 选项设置为 False

if __name__ == "__main__":
    app.run(port=8082, debug=True, use_reloader=False)

预分叉服务器问题

像 Gunicorn 带有多个工作进程的预分叉服务器可以这样运行:

gunicorn myapp.main:app --workers 4

但是,指定超过一个 --workers 可能会破坏自动检测应用时的指标生成。这是因为分叉(创建工作进程/子进程)会在每个子进程的后台线程之间产生不一致,并会锁定一些关键的 OpenTelemetry SDK 组件。特别是,PeriodicExportingMetricReader 会生成自己的线程来定期将数据刷新到导出器。另请参阅问题 #2767#3307。分叉后,每个子进程都会尝试在内存中查找一个实际上并未运行的线程对象,并且任何原始锁都可能不会为每个子进程解锁。另请参阅 Python issue 6721 中描述的分叉和死锁。

解决方法

对于带有 OpenTelemetry 的预分叉服务器,存在一些解决方法。下表总结了不同自动检测的 Web 服务器网关堆栈(带有多个工作进程且已预分叉)的当前信号导出支持情况。更多详细信息和选项请参阅下文。

带有多个工作进程的堆栈追踪指标日志
Uvicornxx
Gunicornxx
Gunicorn + UvicornWorkerxxx
使用 Gunicorn 和 UvicornWorker 进行部署

要自动检测带有多个工作进程的服务器,如果它是异步服务器网关接口 (ASGI) 应用(FastAPI、Starlette 等),建议使用 uvicorn.workers.UvicornWorker 进行部署。UvicornWorker 类专为处理分叉而设计,可保留后台进程和线程。例如:

opentelemetry-instrument gunicorn \
  --workers 4 \
  --worker-class uvicorn.workers.UvicornWorker \
  --bind 0.0.0.0:8000 \
  myapp.main:app
使用编程方式自动检测

在服务器分叉后,在工作进程内使用 编程方式自动检测 进行初始化,而不是使用 opentelemetry-instrument。例如:

from opentelemetry.instrumentation.auto_instrumentation import initialize
initialize()

from your_app import app

如果使用 FastAPI,请注意,由于检测的补丁方式,initialize() 必须在导入 FastAPI 之前调用。例如:

from opentelemetry.instrumentation.auto_instrumentation import initialize
initialize()

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
    return {"message": "Hello World"}

然后,使用以下命令运行服务器:

uvicorn main:app --workers 2
使用 Prometheus 和直接 OTLP

考虑使用最新版本的 Prometheus 直接接收 OTLP 指标。设置一个 PeriodicExportingMetricReader 和每个进程一个 OTLP 工作进程来推送到 Prometheus 服务器。我们建议不要在分叉时使用 PrometheusMetricReader,请参阅问题 #3747

使用单个工作进程

或者,在预分叉中使用单个工作进程进行零代码检测:

opentelemetry-instrument gunicorn your_app:app --workers 1

连接问题

gRPC 连接

要调试 Python gRPC 连接问题,请设置以下 gRPC 调试环境变量:

export GRPC_VERBOSITY=debug
export GRPC_TRACE=http,call_error,connectivity_state
opentelemetry-instrument python YOUR_APP.py