fix(otelTrace): 增强span创建和结束的安全性
- 在Span和SpanWithMetrics函数中添加nil context检查,避免panic - 优化CreateLinkContext函数,区分父span有效和无效时的处理逻辑 - 新增SafeEndSpan函数,安全结束span并捕获可能的panic - 添加日志记录panic信息,防止程序崩溃 - 确保所有返回的context不为nil,提升稳定性
This commit is contained in:
@@ -7,11 +7,17 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// Span 创建并返回一个新的span,优化了性能和指标收集
|
||||
// 支持自动添加通用属性和错误处理
|
||||
func Span(ctx context.Context, traceName, spanName string, attr ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
// 安全检查:如果context为空,创建一个默认的context以避免panic
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
// 添加通用属性以优化指标收集
|
||||
attrs := append([]trace.SpanStartOption{
|
||||
trace.WithAttributes(
|
||||
@@ -22,7 +28,7 @@ func Span(ctx context.Context, traceName, spanName string, attr ...trace.SpanSta
|
||||
|
||||
ctx, span := otel.Tracer(traceName).Start(ctx, spanName, attrs...)
|
||||
|
||||
// 如果context为空,创建一个默认的context以避免panic
|
||||
// 二次检查:确保返回的context不为nil
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
@@ -41,6 +47,11 @@ func Span(ctx context.Context, traceName, spanName string, attr ...trace.SpanSta
|
||||
// - 异步任务需要独立完成,不应被父请求取消影响
|
||||
// - 后台处理任务需要保持与原始请求的追踪关联
|
||||
func CreateLinkContext(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
// 安全检查:确保context不为nil
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
// 获取父span的上下文
|
||||
parentSpanCtx := trace.SpanContextFromContext(ctx)
|
||||
|
||||
@@ -48,16 +59,27 @@ func CreateLinkContext(ctx context.Context, spanName string, opts ...trace.SpanS
|
||||
// 使用Background()确保子ctx独立于父ctx的生命周期
|
||||
linkCtx := trace.ContextWithSpanContext(context.Background(), parentSpanCtx)
|
||||
|
||||
// 创建新的span,链接到父span
|
||||
linkOpts := append(opts,
|
||||
trace.WithLinks(trace.Link{SpanContext: parentSpanCtx}),
|
||||
trace.WithSpanKind(trace.SpanKindConsumer),
|
||||
trace.WithAttributes(
|
||||
attribute.String("parent.trace_id", parentSpanCtx.TraceID().String()),
|
||||
attribute.String("parent.span_id", parentSpanCtx.SpanID().String()),
|
||||
attribute.String("context_type", "traceable"),
|
||||
),
|
||||
)
|
||||
// 创建新的span,链接到父span(只在父span有效时添加链接)
|
||||
var linkOpts []trace.SpanStartOption
|
||||
if parentSpanCtx.IsValid() {
|
||||
linkOpts = append(opts,
|
||||
trace.WithLinks(trace.Link{SpanContext: parentSpanCtx}),
|
||||
trace.WithSpanKind(trace.SpanKindConsumer),
|
||||
trace.WithAttributes(
|
||||
attribute.String("parent.trace_id", parentSpanCtx.TraceID().String()),
|
||||
attribute.String("parent.span_id", parentSpanCtx.SpanID().String()),
|
||||
attribute.String("context_type", "traceable"),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
// 父span无效时,仍然创建span但不添加链接
|
||||
linkOpts = append(opts,
|
||||
trace.WithSpanKind(trace.SpanKindConsumer),
|
||||
trace.WithAttributes(
|
||||
attribute.String("context_type", "standalone"),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return otel.Tracer("link").Start(linkCtx, spanName, linkOpts...)
|
||||
}
|
||||
@@ -110,8 +132,36 @@ func AddSpanAttributes(span trace.Span, attrs ...attribute.KeyValue) {
|
||||
span.SetAttributes(attrs...)
|
||||
}
|
||||
|
||||
// SafeEndSpan 安全地结束span,防止panic传播
|
||||
// 建议在defer中使用:defer SafeEndSpan(span)
|
||||
func SafeEndSpan(span trace.Span) {
|
||||
if span == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 使用defer+recover捕获可能的panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// 记录panic信息,但不重新抛出
|
||||
if Logger.logger != nil {
|
||||
Logger.logger.Error("panic in span.End()",
|
||||
zap.Any("panic", r),
|
||||
zap.Stack("stack"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
span.End()
|
||||
}
|
||||
|
||||
// SpanWithMetrics 创建带有性能指标收集的span,用于高频操作的性能监控
|
||||
func SpanWithMetrics(ctx context.Context, traceName, spanName string, operationType string, attr ...trace.SpanStartOption) (context.Context, trace.Span) {
|
||||
// 安全检查:如果context为空,创建一个默认的context以避免panic
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
// 添加性能相关的属性
|
||||
perfAttrs := append([]trace.SpanStartOption{
|
||||
trace.WithAttributes(
|
||||
@@ -124,6 +174,7 @@ func SpanWithMetrics(ctx context.Context, traceName, spanName string, operationT
|
||||
|
||||
ctx, span := otel.Tracer(traceName).Start(ctx, spanName, perfAttrs...)
|
||||
|
||||
// 二次检查:确保返回的context不为nil
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user