手把手把应用链路追踪接入炬鲸 OBSERVE:部署 Collector、配置 OTLP 导出、应用侧埋点,再到验证与常见坑,覆盖 Java 与 Python 两种示例,附完整配置文件。
直接在应用里把 trace 发到后端,是最省事也最脆弱的做法:后端地址、鉴权信息硬编码进每个服务,换一次就要重新发布;应用重启时在途数据丢失;高峰期没有缓冲,直接把下游压垮。OpenTelemetry Collector 把这些问题挡在业务代码外面:它独立部署,负责接收、缓冲、批处理、重试,应用只把数据发到本机或内网的固定端口,后端怎么换都不影响业务代码。
还有一个额外好处:采样率、脱敏规则、统一加环境名/集群名这类横切策略,改 Collector 配置就行,不用动几十个服务。你可以先上基于头部的采样控制成本,之后再切到尾采样,全程不用改一行应用代码。
先准备配置文件 otel-collector.yaml:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
memory_limiter:
check_interval: 1s
limit_mib: 512
batch:
timeout: 5s
send_batch_size: 512
exporters:
otlphttp:
endpoint: https://ob.jjhub.cn/otel/v1/traces
headers:
Authorization: Bearer ${env:JJHUB_TOKEN}
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlphttp]
用 Docker 启动:
docker run -d --name otel-collector \
-p 4317:4317 -p 4318:4318 \
-e JJHUB_TOKEN=你的token \
-v $PWD/otel-collector.yaml:/etc/otelcol/config.yaml \
otel/opentelemetry-collector-contrib:0.115.0
两个注意点:一是导出走 otlphttp(HTTP 协议),比 gRPC 更容易过网关和防火墙;二是 token 用环境变量注入,别写死在配置文件里提交进仓库。生产环境建议用 Kubernetes DaemonSet 或 systemd 服务来跑 Collector,而不是裸 Docker,让每个应用都指向本机就近的 Collector。
Java(Spring Boot)最省事的方式是挂 Java Agent,不改业务代码:
java -javaagent:opentelemetry-javaagent.jar \
-Dotel.traces.exporter=otlp \
-Dotel.exporter.otlp.endpoint=http://localhost:4317 \
-Dotel.service.name=order-service \
-jar app.jar
Python 手动埋点,只给关键函数加 span:
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
trace.set_tracer_provider(TracerProvider(
resource=Resource.create({SERVICE_NAME: 'order-service'})))
trace.get_tracer_provider().add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint='localhost:4317', insecure=True)))
tracer = trace.get_tracer(__name__)
with tracer.start_as_current_span('checkout') as span:
span.set_attribute('order.id', order_id)
# 业务逻辑
不管什么语言,有三项默认值建议处处统一:稳定的 service.name、开启跨进程传播让 trace_id 在 HTTP 调用间传递、给关键 span 加至少一两个业务属性(订单号、用户 ID)。这三项决定了一条链路是「知道慢在哪个函数」,还是「知道慢在谁的请求上」。
同一个 Collector 可以同时把三路信号都送到炬鲸 OBSERVE,不止链路。加一个 filelog receiver 采集应用日志、一个 prometheus receiver 抓指标,各自经过 batch 处理后导出到对应的 OTLP 出口:
receivers:
filelog:
include: [/var/log/app/*.log]
operators:
- type: json_parser
prometheus:
config:
scrape_configs:
- job_name: app
static_configs:
- targets: ['localhost:8080']
service:
pipelines:
logs:
receivers: [filelog]
processors: [memory_limiter, batch]
exporters: [otlphttp/logs]
metrics:
receivers: [prometheus]
processors: [memory_limiter, batch]
exporters: [otlphttp/metrics]
otlphttp/logs 和 otlphttp/metrics 指向同一个主机、不同路径,配置和 traces 出口镜像。好处是一台主机一个 Agent 管全部,token、采样、脱敏都在一处管理。记住一条:三路信号的 service.name 要一致,如果链路里叫 order-service、日志里叫 host-03,trace 和日志的关联就会悄悄断掉。
验证三件套:炬鲸 OBSERVE 链路追踪页面能查到你的 service.name;点开一条 trace 能看到完整的 span 层级和耗时;拿 trace_id 去日志检索能串起同一次请求的全部日志。如果 span 有但日志串不起来,多半是日志行里没带 trace_id——很多日志框架需要显式往 MDC 里塞这个字段。
常见坑:第一,4317 端口只在 Collector 本机可连,容器化应用要用宿主机 IP 或服务名;第二,batch processor 的 timeout 别设太长,否则调试时半天看不到数据;第三,采样会丢掉一部分 trace,个别请求查不到时先查采样器,别急着怀疑导出链路;第四,Collector 版本要和 SDK 版本对齐,避免 OTLP 协议不兼容。