很多 Python 服务的可观测性还停留在 print 和自研日志。本文用 FastAPI 为例,演示不改业务代码接入 OpenTelemetry 自动埋点,把 trace、指标和带 traceId 的日志一起送进炬鲸 OBSERVE,并列出常见的三个坑。
Python 服务的可观测性常常停留在 print 和自研日志:出了问题只能靠 grep 日志文件。用 OpenTelemetry 的自动埋点,不改业务代码就能拿到 trace 和指标,再把日志的 traceId 关联上,排障路径会顺很多。下面以 FastAPI 为例走一遍完整流程。
pip install opentelemetry-distro opentelemetry-exporter-otlp-proto-http \
opentelemetry-instrumentation-fastapi \
opentelemetry-instrumentation-requests \
opentelemetry-instrumentation-psycopg2
opentelemetry-distro 会装好 SDK 和默认的全局配置,各 instrumentation 包负责给对应框架自动生成 span,不需要在业务代码里 import。
export OTEL_SERVICE_NAME=order-service
export OTEL_EXPORTER_OTLP_ENDPOINT=https://ob.jjhub.cn/otel
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1
export OTEL_LOGS_EXPORTER=otlp
export OTEL_METRICS_EXPORTER=otlp
服务名一定要唯一,否则多个实例会在链路图里混成一条。采样先给 10%,等链路跑通、确认数据量后再调。
不改代码,用 opentelemetry-instrument 启动 uvicorn 即可:
opentelemetry-instrument uvicorn app.main:app --host 0.0.0.0 --port 8000
这会让 FastAPI 的每个请求自动生成一个 span,requests 发出的下游 HTTP 调用、psycopg2 的数据库查询也会自动挂成子 span。
自动埋点只覆盖 trace 和指标,日志要和 trace 关联需要手动注入。业务里用标准 logging 输出时,把当前 span 的上下文带进去:
import logging
from opentelemetry import trace
logger = logging.getLogger("order-service")
def get_order(order_id):
ctx = trace.get_current_span().get_span_context()
logger.info("fetching order %s", order_id,
extra={"trace_id": ctx.trace_id, "span_id": ctx.span_id})
炬鲸 OBSERVE 识别日志里的 trace_id、span_id 字段后,就能在 trace 详情页直接看到这条请求产生的所有日志行,反之也能从日志反查调用链。
发一个真实请求,去控制台确认三件事:链路图里能看到 FastAPI、下游 HTTP、数据库三层 span;指标页有请求数、耗时和错误率;日志页能用 traceId 反查出对应日志行。
常见的三个坑:OTLP endpoint 要带 /otel 路径,少了会 404;exporter 协议要和后端一致,http/protobuf 与 grpc 不要混用;OTEL_SERVICE_NAME 改了之后要重启进程才生效,别在运行中热改。