github.com/zly-app/zapp@v1.3.3/pkg/utils/otel.go (about)

     1  package utils
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"time"
     7  
     8  	"github.com/spf13/cast"
     9  	"go.opentelemetry.io/otel"
    10  	"go.opentelemetry.io/otel/attribute"
    11  	"go.opentelemetry.io/otel/codes"
    12  	"go.opentelemetry.io/otel/propagation"
    13  	"go.opentelemetry.io/otel/trace"
    14  )
    15  
    16  var Otel = &otelCli{}
    17  
    18  type otelCli struct{}
    19  
    20  // key, 示例 OtelSpanKey("foo").String("bar")
    21  type OtelSpanKey = attribute.Key
    22  type OtelSpanKV = attribute.KeyValue
    23  
    24  func (*otelCli) GlobalTrace(name string) trace.Tracer {
    25  	return otel.Tracer(name)
    26  }
    27  
    28  // 将span存入ctx中
    29  func (*otelCli) SaveSpan(ctx context.Context, span trace.Span) context.Context {
    30  	return trace.ContextWithSpan(ctx, span)
    31  }
    32  
    33  // 将span存入ctx中
    34  // Deprecated: use SaveSpan
    35  func (*otelCli) SaveToContext(ctx context.Context, span trace.Span) context.Context {
    36  	return trace.ContextWithSpan(ctx, span)
    37  }
    38  
    39  // 从ctx中获取span
    40  func (*otelCli) GetSpan(ctx context.Context) trace.Span {
    41  	return trace.SpanFromContext(ctx)
    42  }
    43  
    44  func (*otelCli) SaveToHeaders(ctx context.Context, headers http.Header) {
    45  	v := propagation.HeaderCarrier(headers)
    46  	otel.GetTextMapPropagator().Inject(ctx, v)
    47  }
    48  
    49  func (c *otelCli) GetSpanWithHeaders(ctx context.Context, headers http.Header) (context.Context, trace.Span) {
    50  	v := propagation.HeaderCarrier(headers)
    51  	ctx = otel.GetTextMapPropagator().Extract(ctx, v)
    52  	return ctx, c.GetSpan(ctx)
    53  }
    54  
    55  func (*otelCli) SaveToMap(ctx context.Context, mapping map[string]string) {
    56  	v := propagation.MapCarrier(mapping)
    57  	otel.GetTextMapPropagator().Inject(ctx, v)
    58  }
    59  
    60  func (c *otelCli) GetSpanWithMap(ctx context.Context, mapping map[string]string) (context.Context, trace.Span) {
    61  	v := propagation.MapCarrier(mapping)
    62  	ctx = otel.GetTextMapPropagator().Extract(ctx, v)
    63  	return ctx, c.GetSpan(ctx)
    64  }
    65  
    66  func (*otelCli) SaveToTextMapCarrier(ctx context.Context, carrier propagation.TextMapCarrier) {
    67  	otel.GetTextMapPropagator().Inject(ctx, carrier)
    68  }
    69  
    70  func (c *otelCli) GetSpanWithTextMapCarrier(ctx context.Context, carrier propagation.TextMapCarrier) (context.Context, trace.Span) {
    71  	ctx = otel.GetTextMapPropagator().Extract(ctx, carrier)
    72  	return ctx, c.GetSpan(ctx)
    73  }
    74  
    75  // 开始一个span
    76  func (*otelCli) StartSpan(ctx context.Context, spanName string, attributes ...OtelSpanKV) (
    77  	context.Context, trace.Span) {
    78  	return otel.Tracer("").Start(ctx, spanName, trace.WithAttributes(attributes...))
    79  }
    80  
    81  // 设置span属性. 属性是作为元数据应用于跨度的键和值,可用于聚合、过滤和分组跟踪
    82  func (*otelCli) SetSpanAttributes(span trace.Span, attributes ...OtelSpanKV) {
    83  	span.SetAttributes(attributes...)
    84  }
    85  
    86  // 添加事件, 事件的属性不会用于聚合、过滤和分组跟踪
    87  func (*otelCli) AddSpanEvent(span trace.Span, eventName string, attributes ...OtelSpanKV) {
    88  	span.AddEvent(eventName, trace.WithAttributes(attributes...))
    89  }
    90  
    91  // 将span标记为错误
    92  func (*otelCli) MarkSpanAnError(span trace.Span, isErr bool) {
    93  	span.SetStatus(codes.Error, cast.ToString(isErr))
    94  }
    95  
    96  // 结束一个span
    97  func (*otelCli) EndSpan(span trace.Span) {
    98  	span.End()
    99  }
   100  
   101  // 获取 traceID
   102  func (*otelCli) GetOTELTraceID(ctx context.Context) (traceID string, spanID string) {
   103  	sc := trace.SpanContextFromContext(ctx)
   104  	return sc.TraceID().String(), sc.SpanID().String()
   105  }
   106  
   107  // 根据超时ctx获取一个OtelSpanKV描述
   108  func (*otelCli) GetSpanKVWithDeadline(ctx context.Context) OtelSpanKV {
   109  	deadline, deadlineOK := ctx.Deadline()
   110  	if !deadlineOK {
   111  		return OtelSpanKey("ctx.deadline").Bool(false)
   112  	}
   113  	d := deadline.Sub(time.Now()) // 剩余时间
   114  	return OtelSpanKey("ctx.deadline").String(d.String())
   115  }
   116  
   117  // 创建一个OtelSpanKV描述
   118  func (*otelCli) AttrKey(key string) OtelSpanKey {
   119  	return OtelSpanKey(key)
   120  }
   121  
   122  func (c *otelCli) CtxStart(ctx context.Context, name string, attributes ...OtelSpanKV) context.Context {
   123  	// 生成新的 span
   124  	ctx, _ = c.StartSpan(ctx, name, attributes...)
   125  	return ctx
   126  }
   127  
   128  func (c *otelCli) CtxEvent(ctx context.Context, name string, attributes ...OtelSpanKV) {
   129  	span := c.GetSpan(ctx)
   130  	attr := []OtelSpanKV{
   131  		c.GetSpanKVWithDeadline(ctx),
   132  	}
   133  	attr = append(attr, attributes...)
   134  	c.AddSpanEvent(span, name, attr...)
   135  }
   136  
   137  func (c *otelCli) CtxErrEvent(ctx context.Context, name string, err error, attributes ...OtelSpanKV) {
   138  	span := c.GetSpan(ctx)
   139  	attr := []OtelSpanKV{
   140  		c.GetSpanKVWithDeadline(ctx),
   141  		OtelSpanKey("err.detail").String(err.Error()),
   142  	}
   143  	if Recover.IsRecoverError(err) {
   144  		c.SetSpanAttributes(span, OtelSpanKey("panic").Bool(true))
   145  		panicErrs := Recover.GetRecoverErrors(err)
   146  		attr = append(attr, OtelSpanKey("detail").StringSlice(panicErrs))
   147  	}
   148  
   149  	attr = append(attr, attributes...)
   150  	c.AddSpanEvent(span, name+" err", attr...)
   151  	c.MarkSpanAnError(span, true)
   152  }
   153  
   154  func (c *otelCli) CtxEnd(ctx context.Context) {
   155  	span := c.GetSpan(ctx)
   156  	c.EndSpan(span)
   157  }