应用集成 Jaeger SDK
应用集成 Jaeger SDK
多语言 SDK 支持
Jaeger 支持主流编程语言的客户端库:
| 语言 | 客户端库 | 成熟度 |
|---|---|---|
| Go | jaeger-client-go | ⭐⭐⭐⭐⭐ |
| Java | jaeger-client-java | ⭐⭐⭐⭐⭐ |
| Python | jaeger-client-python | ⭐⭐⭐⭐ |
| Node.js | jaeger-client-node | ⭐⭐⭐⭐ |
| C++ | jaeger-client-cpp | ⭐⭐⭐ |
| .NET | jaeger-client-csharp | ⭐⭐⭐⭐ |
Go 应用集成
1. 安装依赖
go get github.com/uber/jaeger-client-go
go get github.com/opentracing/opentracing-go
2. 初始化 Tracer
// tracing.go
package tracing
import (
"io"
"time"
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"github.com/uber/jaeger-client-go/config"
)
// InitTracer 初始化 Jaeger Tracer
func InitTracer(serviceName string) (opentracing.Tracer, io.Closer, error) {
cfg := &config.Configuration{
ServiceName: serviceName,
// 采样配置
Sampler: &config.SamplerConfig{
Type: "const", // 常量采样
Param: 1, // 1 = 100% 采样
},
// Reporter 配置
Reporter: &config.ReporterConfig{
LogSpans: true,
BufferFlushInterval: 1 * time.Second,
LocalAgentHostPort: "jaeger-agent:6831", // Agent 地址
},
}
tracer, closer, err := cfg.NewTracer(
config.Logger(jaeger.StdLogger),
)
if err != nil {
return nil, nil, err
}
// 设置为全局 Tracer
opentracing.SetGlobalTracer(tracer)
return tracer, closer, nil
}
3. HTTP Server 集成
// main.go
package main
import (
"context"
"fmt"
"log"
"net/http"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
func main() {
// 初始化 Tracer
tracer, closer, err := InitTracer("my-service")
if err != nil {
log.Fatal(err)
}
defer closer.Close()
// 注册 Handler
http.HandleFunc("/api/users", TracingMiddleware(GetUsersHandler))
log.Println("Server starting on :8080")
http.ListenAndServe(":8080", nil)
}
// TracingMiddleware HTTP 追踪中间件
func TracingMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 从 HTTP Header 提取 Span Context
spanCtx, _ := opentracing.GlobalTracer().Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(r.Header),
)
// 创建新的 Span
span := opentracing.GlobalTracer().StartSpan(
fmt.Sprintf("%s %s", r.Method, r.URL.Path),
ext.RPCServerOption(spanCtx),
)
defer span.Finish()
// 设置标准 Tags
ext.HTTPMethod.Set(span, r.Method)
ext.HTTPUrl.Set(span, r.URL.String())
ext.Component.Set(span, "net/http")
// 将 Span 放入 Context
ctx := opentracing.ContextWithSpan(r.Context(), span)
// 创建自定义 ResponseWriter 来捕获状态码
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
// 调用下一个 Handler
next.ServeHTTP(rw, r.WithContext(ctx))
// 设置响应状态码
ext.HTTPStatusCode.Set(span, uint16(rw.statusCode))
if rw.statusCode >= 400 {
ext.Error.Set(span, true)
}
}
}
// responseWriter 包装 http.ResponseWriter 以捕获状态码
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
// GetUsersHandler 业务处理函数
func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
span, ctx := opentracing.StartSpanFromContext(r.Context(), "GetUsers")
defer span.Finish()
// 设置自定义 Tags
span.SetTag("user.type", "admin")
// 调用数据库
users, err := GetUsersFromDB(ctx)
if err != nil {
span.SetTag("error", true)
span.LogKV("event", "error", "message", err.Error())
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 记录日志
span.LogKV("event", "users_fetched", "count", len(users))
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(users)
}
// GetUsersFromDB 数据库查询(带追踪)
func GetUsersFromDB(ctx context.Context) ([]User, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "db.query")
defer span.Finish()
// 设置数据库 Tags
span.SetTag("db.type", "mysql")
span.SetTag("db.instance", "users_db")
span.SetTag("db.statement", "SELECT * FROM users")
// 模拟数据库查询
time.Sleep(50 * time.Millisecond)
return []User{{ID: 1, Name: "Alice"}}, nil
}
4. HTTP Client 集成
// client.go
func CallExternalAPI(ctx context.Context, url string) (*http.Response, error) {
// 创建子 Span
span, ctx := opentracing.StartSpanFromContext(ctx, "http.client")
defer span.Finish()
// 设置 Tags
ext.SpanKindRPCClient.Set(span)
ext.HTTPUrl.Set(span, url)
ext.HTTPMethod.Set(span, "GET")
// 创建 HTTP 请求
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return nil, err
}
// 注入 Span Context 到 HTTP Header
err = opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header),
)
if err != nil {
span.LogKV("event", "inject_error", "error", err.Error())
}
// 发送请求
resp, err := http.DefaultClient.Do(req)
if err != nil {
ext.Error.Set(span, true)
span.LogKV("event", "error", "message", err.Error())
return nil, err
}
// 记录响应状态
ext.HTTPStatusCode.Set(span, uint16(resp.StatusCode))
if resp.StatusCode >= 400 {
ext.Error.Set(span, true)
}
return resp, nil
}
5. gRPC 集成
// grpc_client.go
import (
"github.com/grpc-ecosystem/go-grpc-middleware/tracing/opentracing"
"google.golang.org/grpc"
)
func NewGRPCClient(addr string) (*grpc.ClientConn, error) {
return grpc.Dial(
addr,
grpc.WithUnaryInterceptor(
grpc_opentracing.UnaryClientInterceptor(
grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
),
),
grpc.WithStreamInterceptor(
grpc_opentracing.StreamClientInterceptor(
grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
),
),
)
}
// grpc_server.go
func NewGRPCServer() *grpc.Server {
return grpc.NewServer(
grpc.UnaryInterceptor(
grpc_opentracing.UnaryServerInterceptor(
grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
),
),
grpc.StreamInterceptor(
grpc_opentracing.StreamServerInterceptor(
grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
),
),
)
}
Java/Spring Boot 集成
1. 添加依赖
<!-- pom.xml -->
<dependencies>
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Jaeger Client -->
<dependency>
<groupId>io.jaegertracing</groupId>
<artifactId>jaeger-client</artifactId>
<version>1.8.1</version>
</dependency>
<!-- OpenTracing Spring Web -->
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-web-starter</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>
2. 配置 Tracer
// JaegerConfig.java
@Configuration
public class JaegerConfig {
@Bean
public Tracer jaegerTracer() {
// 采样配置
SamplerConfiguration samplerConfig = SamplerConfiguration.fromEnv()
.withType("const")
.withParam(1);
// Reporter 配置
ReporterConfiguration reporterConfig = ReporterConfiguration.fromEnv()
.withLogSpans(true)
.withFlushInterval(1000)
.withMaxQueueSize(10000)
.withSender(
SenderConfiguration.fromEnv()
.withAgentHost("jaeger-agent")
.withAgentPort(6831)
);
// Tracer 配置
return Configuration.fromEnv("my-service")
.withSampler(samplerConfig)
.withReporter(reporterConfig)
.getTracer();
}
}
3. Controller 示例
// UserController.java
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private Tracer tracer;
@Autowired
private UserService userService;
@GetMapping("/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
// 获取当前 Span
Span span = tracer.activeSpan();
if (span != null) {
span.setTag("user.id", id.toString());
span.log("Fetching user");
}
try {
User user = userService.getUserById(id);
return ResponseEntity.ok(user);
} catch (Exception e) {
if (span != null) {
Tags.ERROR.set(span, true);
span.log(Map.of(
"event", "error",
"error.object", e,
"message", e.getMessage()
));
}
throw e;
}
}
}
4. Service 层追踪
// UserService.java
@Service
public class UserService {
@Autowired
private Tracer tracer;
@Autowired
private UserRepository userRepository;
public User getUserById(Long id) {
// 创建子 Span
Span span = tracer.buildSpan("getUserById")
.asChildOf(tracer.activeSpan())
.start();
try (Scope scope = tracer.scopeManager().activate(span)) {
span.setTag("service.method", "getUserById");
span.setTag("user.id", id.toString());
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
span.log("User found");
return user;
} catch (Exception e) {
Tags.ERROR.set(span, true);
span.log(Map.of("event", "error", "message", e.getMessage()));
throw e;
} finally {
span.finish();
}
}
}
5. RestTemplate 集成
// RestTemplateConfig.java
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(Tracer tracer) {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(
Collections.singletonList(
new TracingClientHttpRequestInterceptor(tracer)
)
);
return restTemplate;
}
}
Python/Flask 集成
1. 安装依赖
pip install jaeger-client flask-opentracing
2. 初始化 Tracer
# tracing.py
from jaeger_client import Config
from flask_opentracing import FlaskTracing
def init_tracer(service_name='my-service'):
config = Config(
config={
'sampler': {
'type': 'const',
'param': 1,
},
'logging': True,
'local_agent': {
'reporting_host': 'jaeger-agent',
'reporting_port': 6831,
},
},
service_name=service_name,
validate=True,
)
return config.initialize_tracer()
3. Flask 应用集成
# app.py
from flask import Flask, jsonify, request
from flask_opentracing import FlaskTracing
from opentracing_instrumentation.client_hooks import install_all_patches
import requests
app = Flask(__name__)
# 初始化 Tracer
tracer = init_tracer('flask-service')
tracing = FlaskTracing(tracer, True, app)
# 自动追踪所有 HTTP 请求
install_all_patches()
@app.route('/api/users/<int:user_id>')
def get_user(user_id):
# 获取当前 Span
span = tracer.active_span
if span:
span.set_tag('user.id', user_id)
span.log_kv({'event': 'get_user', 'user_id': user_id})
# 调用数据库
user = get_user_from_db(user_id)
# 调用外部服务
orders = get_user_orders(user_id)
return jsonify({
'user': user,
'orders': orders
})
def get_user_from_db(user_id):
# 创建子 Span
with tracer.start_active_span('db.query') as scope:
scope.span.set_tag('db.type', 'postgresql')
scope.span.set_tag('db.statement', 'SELECT * FROM users WHERE id=?')
# 模拟数据库查询
import time
time.sleep(0.05)
return {'id': user_id, 'name': 'Alice'}
def get_user_orders(user_id):
# 创建子 Span
with tracer.start_active_span('http.client') as scope:
scope.span.set_tag('http.url', f'http://order-service/api/orders')
scope.span.set_tag('http.method', 'GET')
# 发送 HTTP 请求(自动传播 Context)
response = requests.get(
f'http://order-service/api/orders?user_id={user_id}'
)
scope.span.set_tag('http.status_code', response.status_code)
return response.json()
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
Node.js/Express 集成
1. 安装依赖
npm install jaeger-client express-opentracing opentracing
2. 初始化 Tracer
// tracing.js
const initTracer = require('jaeger-client').initTracer;
function initJaegerTracer(serviceName) {
const config = {
serviceName: serviceName,
sampler: {
type: 'const',
param: 1,
},
reporter: {
logSpans: true,
agentHost: 'jaeger-agent',
agentPort: 6831,
},
};
const options = {
logger: {
info: msg => console.log('INFO', msg),
error: msg => console.log('ERROR', msg),
},
};
return initTracer(config, options);
}
module.exports = initJaegerTracer;
3. Express 应用集成
// app.js
const express = require('express');
const initTracer = require('./tracing');
const { middleware } = require('express-opentracing');
const app = express();
const tracer = initTracer('nodejs-service');
// 应用追踪中间件
app.use(middleware({ tracer }));
// API 路由
app.get('/api/users/:id', async (req, res) => {
const span = req.span;
span.setTag('user.id', req.params.id);
span.log({ event: 'get_user', user_id: req.params.id });
try {
// 调用数据库
const user = await getUserFromDB(req.params.id, span);
// 调用外部服务
const orders = await getUserOrders(req.params.id, span);
res.json({ user, orders });
} catch (error) {
span.setTag('error', true);
span.log({ event: 'error', message: error.message });
res.status(500).json({ error: error.message });
}
});
async function getUserFromDB(userId, parentSpan) {
// 创建子 Span
const span = tracer.startSpan('db.query', { childOf: parentSpan });
span.setTag('db.type', 'mongodb');
span.setTag('db.statement', 'db.users.findOne({ _id: userId })');
try {
// 模拟数据库查询
await new Promise(resolve => setTimeout(resolve, 50));
return { id: userId, name: 'Alice' };
} finally {
span.finish();
}
}
async function getUserOrders(userId, parentSpan) {
const span = tracer.startSpan('http.client', { childOf: parentSpan });
span.setTag('http.url', 'http://order-service/api/orders');
span.setTag('http.method', 'GET');
try {
const axios = require('axios');
// 注入 Trace Context 到 Headers
const headers = {};
tracer.inject(span, 'http_headers', headers);
const response = await axios.get(
`http://order-service/api/orders?user_id=${userId}`,
{ headers }
);
span.setTag('http.status_code', response.status);
return response.data;
} catch (error) {
span.setTag('error', true);
span.log({ event: 'error', message: error.message });
throw error;
} finally {
span.finish();
}
}
app.listen(8080, () => {
console.log('Server running on port 8080');
});
Kubernetes 配置
环境变量注入
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
- name: myapp
image: myapp:latest
env:
# Jaeger Agent 地址
- name: JAEGER_AGENT_HOST
value: "jaeger-agent.observability.svc.cluster.local"
- name: JAEGER_AGENT_PORT
value: "6831"
# 服务名称
- name: JAEGER_SERVICE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.labels['app']
# 采样配置
- name: JAEGER_SAMPLER_TYPE
value: "probabilistic"
- name: JAEGER_SAMPLER_PARAM
value: "0.1" # 10% 采样
# 日志配置
- name: JAEGER_REPORTER_LOG_SPANS
value: "true"
- name: JAEGER_REPORTER_MAX_QUEUE_SIZE
value: "100"
Sidecar 模式
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
containers:
# 应用容器
- name: myapp
image: myapp:latest
env:
- name: JAEGER_AGENT_HOST
value: "localhost" # Sidecar 在同一 Pod
- name: JAEGER_AGENT_PORT
value: "6831"
# Jaeger Agent Sidecar
- name: jaeger-agent
image: jaegertracing/jaeger-agent:1.51
ports:
- containerPort: 6831
protocol: UDP
- containerPort: 6832
protocol: UDP
args:
- "--reporter.grpc.host-port=jaeger-collector.observability:14250"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
总结
SDK 选择建议
| 场景 | 推荐方案 |
|---|---|
| 新项目 | OpenTelemetry SDK |
| 已有 Jaeger | Jaeger Client SDK |
| 需要切换后端 | OpenTelemetry |
| Spring Boot | OpenTracing Spring |
| 微服务 | Service Mesh (Istio) |
集成检查清单
✅ 初始化 Tracer
- 配置服务名称
- 设置采样策略
- 配置 Reporter
✅ HTTP 追踪
- 请求入口埋点
- 客户端调用埋点
- Context 传播
✅ 数据库追踪
- SQL 查询埋点
- 连接池监控
✅ 异步任务
- 消息队列埋点
- 后台任务追踪
✅ 错误处理
- 异常捕获和记录
- 错误标记
下一节将介绍分布式追踪的最佳实践和性能优化。