| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- package trace
- import (
- "context"
- "errors"
- "fmt"
- "strconv"
- "time"
- "git.shuncheng.lu/bigthing/gocommon/pkg/internal/util"
- "git.shuncheng.lu/bigthing/gocommon/pkg/net/engines"
- "github.com/SkyAPM/go2sky"
- "github.com/SkyAPM/go2sky/propagation"
- v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent"
- )
- const (
- ginRequestContextKey = "_gin_request_context_key"
- ginLocalSpan = "_gin_local_span_key"
- )
- var (
- tracerNotFoundError = errors.New("tracer not found err")
- hostName = util.CurrentServerHostName()
- )
- /**
- ctx拿到的是 request 中 绑定的ctx
- */
- /**
- 这里设计的主要目的:
- 1、gin的ctx和context.Context无法转换
- 2、所以所有的span信息都存入在 requestContext中
- 3、查找原则,如果发现ginRequestContextKey,则返回,没有发现则返回 ctx,那么链路会丢失
- */
- func getGinRequestContext(ctx context.Context) context.Context {
- result, isOk := ctx.Value(ginRequestContextKey).(context.Context)
- if isOk {
- return result
- }
- return ctx
- }
- /**
- 设置 ctx
- */
- func setGinRequestContext(ctx *engines.Context, reqCtx context.Context) {
- ctx.Set(ginRequestContextKey, reqCtx)
- }
- /**
- 设置LocalSpan 上下文
- */
- func setGinLocalSpan(ctx context.Context, span go2sky.Span) context.Context {
- if span == nil || ctx == nil {
- return ctx
- }
- return context.WithValue(ctx, ginLocalSpan, span)
- }
- /**
- 存在 request的ctx中 span
- */
- func GetGinLocalSpan(ctx context.Context) go2sky.Span {
- ctx = getGinRequestContext(ctx)
- value, isOk := ctx.Value(ginLocalSpan).(go2sky.Span)
- if isOk {
- return value
- }
- return nil
- }
- func GinLogInfo(ctx context.Context, log string) {
- if ctx == nil || log == "" {
- return
- }
- span := GetGinLocalSpan(ctx)
- if span != nil {
- span.Log(time.Now(), GetCallLine(3), log)
- }
- }
- // 这里只会记录error
- func GinLogError(ctx context.Context, log string) {
- if ctx == nil || log == "" {
- return
- }
- span := GetGinLocalSpan(ctx)
- if span != nil {
- span.Error(time.Now(), GetCallLine(3), log)
- }
- }
- /**
- 这里需要注意,默认生成的一个空的trace_id,所以需要判断,是否未空trace_id
- */
- func GinGetTraceId(ctx *engines.Context) string {
- traceId := go2sky.TraceID(ctx.Request.Context())
- if traceId == go2sky.EmptyTraceID {
- return ""
- }
- return traceId
- }
- func GetTraceId(ctx context.Context) string {
- traceId := go2sky.TraceID(ctx)
- if traceId == go2sky.EmptyTraceID {
- return ""
- }
- return traceId
- }
- /**
- */
- func GinMiddleware(engine *engines.Engine, tracer *go2sky.Tracer) engines.HandlerFunc {
- if engine == nil || tracer == nil {
- return func(c *engines.Context) {
- c.Next()
- }
- }
- return func(c *engines.Context) {
- operationName := "/" + c.Request.Method + c.Request.URL.Path
- // 创建一个 entry span,这里需要解析header
- span, ctx, err := tracer.CreateEntrySpan(c.Request.Context(), operationName, func() (string, error) {
- return c.Request.Header.Get(propagation.Header), nil
- })
- // 如果异常则直接继续
- if span == nil || err != nil {
- c.Next()
- return
- }
- span.SetComponent(HttpServerComponentID)
- span.SetSpanLayer(v3.SpanLayer_Http)
- // 记录日志
- startTime := time.Now()
- defer func() {
- span.Tag("http.status", strconv.Itoa(c.Writer.Status()))
- span.Tag("spend", util.TimeToSeconds(time.Now().Sub(startTime).Seconds()))
- if c.Errors != nil && len(c.Errors) > 0 {
- span.Error(time.Now(), "Error", fmt.Sprintf("%v", c.Errors))
- }
- span.End()
- }()
- span.Tag("server.host", hostName)
- span.Tag("client.ip", c.ClientIP())
- span.Tag("http.method", c.Request.Method)
- span.Tag("http.url", c.Request.URL.String())
- // 继续创建
- localSpan, ctx, err := tracer.CreateLocalSpan(ctx)
- if localSpan != nil && err == nil {
- localSpan.SetOperationName(c.HandlerName())
- defer localSpan.End()
- // 将localSpan绑定到上下文
- ctx = setGinLocalSpan(ctx, localSpan)
- }
- // 绑定request
- c.Request = c.Request.WithContext(ctx)
- setGinRequestContext(c, c.Request.Context())
- // 继续调用
- c.Next()
- }
- }
- func GinNewExitSpan(ctx context.Context, opName, peerName string, handlerHeader propagation.Injector) (go2sky.Span, error) {
- tracer := GetSkyWalkingTracer()
- if tracer == nil {
- return nil, tracerNotFoundError
- }
- return tracer.CreateExitSpan(getGinRequestContext(ctx), opName, peerName, handlerHeader)
- }
- func GinNewLocalSpan(ctx context.Context) (go2sky.Span, context.Context, error) {
- tracer := GetSkyWalkingTracer()
- if tracer == nil {
- return nil, ctx, tracerNotFoundError
- }
- return tracer.CreateLocalSpan(getGinRequestContext(ctx))
- }
- func GinNewEntrySpan(ctx context.Context, opName string, handlerHeader propagation.Extractor) (go2sky.Span, context.Context, error) {
- tracer := GetSkyWalkingTracer()
- if tracer == nil {
- return nil, nil, tracerNotFoundError
- }
- return tracer.CreateEntrySpan(getGinRequestContext(ctx), opName, handlerHeader)
- }
|