115 lines
3.0 KiB
Go
115 lines
3.0 KiB
Go
// Package otel 提供 OpenTelemetry 链路追踪功能
|
|
package otel
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"go.opentelemetry.io/otel"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
|
"go.opentelemetry.io/otel/propagation"
|
|
"go.opentelemetry.io/otel/sdk/resource"
|
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
|
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
|
|
"go.opentelemetry.io/otel/trace"
|
|
)
|
|
|
|
var tracer trace.Tracer
|
|
|
|
// Config OpenTelemetry 配置
|
|
type Config struct {
|
|
ServiceName string
|
|
ServiceVersion string
|
|
Environment string
|
|
Endpoint string // Tempo OTLP HTTP endpoint, e.g., "tempo:4318"
|
|
Enabled bool
|
|
}
|
|
|
|
// Init 初始化 OpenTelemetry
|
|
// 返回 shutdown 函数,在程序退出时调用
|
|
func Init(cfg Config) (func(context.Context) error, error) {
|
|
if !cfg.Enabled {
|
|
// 如果未启用,返回空的 shutdown 函数
|
|
tracer = otel.Tracer(cfg.ServiceName)
|
|
return func(ctx context.Context) error { return nil }, nil
|
|
}
|
|
|
|
ctx := context.Background()
|
|
|
|
// 创建 OTLP HTTP exporter
|
|
client := otlptracehttp.NewClient(
|
|
otlptracehttp.WithEndpoint(cfg.Endpoint),
|
|
otlptracehttp.WithInsecure(), // 内网使用,不需要 TLS
|
|
)
|
|
exporter, err := otlptrace.New(ctx, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 创建 resource
|
|
res, err := resource.Merge(
|
|
resource.Default(),
|
|
resource.NewWithAttributes(
|
|
semconv.SchemaURL,
|
|
semconv.ServiceName(cfg.ServiceName),
|
|
semconv.ServiceVersion(cfg.ServiceVersion),
|
|
attribute.String("environment", cfg.Environment),
|
|
),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 创建 TracerProvider
|
|
tp := sdktrace.NewTracerProvider(
|
|
sdktrace.WithBatcher(exporter,
|
|
sdktrace.WithBatchTimeout(5*time.Second),
|
|
),
|
|
sdktrace.WithResource(res),
|
|
sdktrace.WithSampler(sdktrace.AlwaysSample()), // 生产环境可改为采样
|
|
)
|
|
|
|
// 设置全局 TracerProvider 和 Propagator
|
|
otel.SetTracerProvider(tp)
|
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
|
propagation.TraceContext{},
|
|
propagation.Baggage{},
|
|
))
|
|
|
|
tracer = tp.Tracer(cfg.ServiceName)
|
|
|
|
return tp.Shutdown, nil
|
|
}
|
|
|
|
// Tracer 获取全局 Tracer
|
|
func Tracer() trace.Tracer {
|
|
if tracer == nil {
|
|
tracer = otel.Tracer("bindbox-game")
|
|
}
|
|
return tracer
|
|
}
|
|
|
|
// StartSpan 开始一个新的 span
|
|
func StartSpan(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
|
return Tracer().Start(ctx, name, opts...)
|
|
}
|
|
|
|
// SpanFromContext 从 context 获取当前 span
|
|
func SpanFromContext(ctx context.Context) trace.Span {
|
|
return trace.SpanFromContext(ctx)
|
|
}
|
|
|
|
// AddEvent 为当前 span 添加事件
|
|
func AddEvent(ctx context.Context, name string, attrs ...attribute.KeyValue) {
|
|
span := trace.SpanFromContext(ctx)
|
|
span.AddEvent(name, trace.WithAttributes(attrs...))
|
|
}
|
|
|
|
// SetError 设置 span 错误状态
|
|
func SetError(ctx context.Context, err error) {
|
|
span := trace.SpanFromContext(ctx)
|
|
span.RecordError(err)
|
|
}
|