gin_trace.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package trace
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "strconv"
  7. "time"
  8. "git.shuncheng.lu/bigthing/gocommon/pkg/internal/util"
  9. "git.shuncheng.lu/bigthing/gocommon/pkg/net/engines"
  10. "github.com/SkyAPM/go2sky"
  11. "github.com/SkyAPM/go2sky/propagation"
  12. v3 "github.com/SkyAPM/go2sky/reporter/grpc/language-agent"
  13. )
  14. const (
  15. ginRequestContextKey = "_gin_request_context_key"
  16. ginLocalSpan = "_gin_local_span_key"
  17. )
  18. var (
  19. tracerNotFoundError = errors.New("tracer not found err")
  20. hostName = util.CurrentServerHostName()
  21. )
  22. /**
  23. ctx拿到的是 request 中 绑定的ctx
  24. */
  25. /**
  26. 这里设计的主要目的:
  27. 1、gin的ctx和context.Context无法转换
  28. 2、所以所有的span信息都存入在 requestContext中
  29. 3、查找原则,如果发现ginRequestContextKey,则返回,没有发现则返回 ctx,那么链路会丢失
  30. */
  31. func getGinRequestContext(ctx context.Context) context.Context {
  32. result, isOk := ctx.Value(ginRequestContextKey).(context.Context)
  33. if isOk {
  34. return result
  35. }
  36. return ctx
  37. }
  38. /**
  39. 设置 ctx
  40. */
  41. func setGinRequestContext(ctx *engines.Context, reqCtx context.Context) {
  42. ctx.Set(ginRequestContextKey, reqCtx)
  43. }
  44. /**
  45. 设置LocalSpan 上下文
  46. */
  47. func setGinLocalSpan(ctx context.Context, span go2sky.Span) context.Context {
  48. if span == nil || ctx == nil {
  49. return ctx
  50. }
  51. return context.WithValue(ctx, ginLocalSpan, span)
  52. }
  53. /**
  54. 存在 request的ctx中 span
  55. */
  56. func GetGinLocalSpan(ctx context.Context) go2sky.Span {
  57. ctx = getGinRequestContext(ctx)
  58. value, isOk := ctx.Value(ginLocalSpan).(go2sky.Span)
  59. if isOk {
  60. return value
  61. }
  62. return nil
  63. }
  64. func GinLogInfo(ctx context.Context, log string) {
  65. if ctx == nil || log == "" {
  66. return
  67. }
  68. span := GetGinLocalSpan(ctx)
  69. if span != nil {
  70. span.Log(time.Now(), GetCallLine(3), log)
  71. }
  72. }
  73. // 这里只会记录error
  74. func GinLogError(ctx context.Context, log string) {
  75. if ctx == nil || log == "" {
  76. return
  77. }
  78. span := GetGinLocalSpan(ctx)
  79. if span != nil {
  80. span.Error(time.Now(), GetCallLine(3), log)
  81. }
  82. }
  83. /**
  84. 这里需要注意,默认生成的一个空的trace_id,所以需要判断,是否未空trace_id
  85. */
  86. func GinGetTraceId(ctx *engines.Context) string {
  87. traceId := go2sky.TraceID(ctx.Request.Context())
  88. if traceId == go2sky.EmptyTraceID {
  89. return ""
  90. }
  91. return traceId
  92. }
  93. func GetTraceId(ctx context.Context) string {
  94. traceId := go2sky.TraceID(ctx)
  95. if traceId == go2sky.EmptyTraceID {
  96. return ""
  97. }
  98. return traceId
  99. }
  100. /**
  101. */
  102. func GinMiddleware(engine *engines.Engine, tracer *go2sky.Tracer) engines.HandlerFunc {
  103. if engine == nil || tracer == nil {
  104. return func(c *engines.Context) {
  105. c.Next()
  106. }
  107. }
  108. return func(c *engines.Context) {
  109. operationName := "/" + c.Request.Method + c.Request.URL.Path
  110. // 创建一个 entry span,这里需要解析header
  111. span, ctx, err := tracer.CreateEntrySpan(c.Request.Context(), operationName, func() (string, error) {
  112. return c.Request.Header.Get(propagation.Header), nil
  113. })
  114. // 如果异常则直接继续
  115. if span == nil || err != nil {
  116. c.Next()
  117. return
  118. }
  119. span.SetComponent(HttpServerComponentID)
  120. span.SetSpanLayer(v3.SpanLayer_Http)
  121. // 记录日志
  122. startTime := time.Now()
  123. defer func() {
  124. span.Tag("http.status", strconv.Itoa(c.Writer.Status()))
  125. span.Tag("spend", util.TimeToSeconds(time.Now().Sub(startTime).Seconds()))
  126. if c.Errors != nil && len(c.Errors) > 0 {
  127. span.Error(time.Now(), "Error", fmt.Sprintf("%v", c.Errors))
  128. }
  129. span.End()
  130. }()
  131. span.Tag("server.host", hostName)
  132. span.Tag("client.ip", c.ClientIP())
  133. span.Tag("http.method", c.Request.Method)
  134. span.Tag("http.url", c.Request.URL.String())
  135. // 继续创建
  136. localSpan, ctx, err := tracer.CreateLocalSpan(ctx)
  137. if localSpan != nil && err == nil {
  138. localSpan.SetOperationName(c.HandlerName())
  139. defer localSpan.End()
  140. // 将localSpan绑定到上下文
  141. ctx = setGinLocalSpan(ctx, localSpan)
  142. }
  143. // 绑定request
  144. c.Request = c.Request.WithContext(ctx)
  145. setGinRequestContext(c, c.Request.Context())
  146. // 继续调用
  147. c.Next()
  148. }
  149. }
  150. func GinNewExitSpan(ctx context.Context, opName, peerName string, handlerHeader propagation.Injector) (go2sky.Span, error) {
  151. tracer := GetSkyWalkingTracer()
  152. if tracer == nil {
  153. return nil, tracerNotFoundError
  154. }
  155. return tracer.CreateExitSpan(getGinRequestContext(ctx), opName, peerName, handlerHeader)
  156. }
  157. func GinNewLocalSpan(ctx context.Context) (go2sky.Span, context.Context, error) {
  158. tracer := GetSkyWalkingTracer()
  159. if tracer == nil {
  160. return nil, ctx, tracerNotFoundError
  161. }
  162. return tracer.CreateLocalSpan(getGinRequestContext(ctx))
  163. }
  164. func GinNewEntrySpan(ctx context.Context, opName string, handlerHeader propagation.Extractor) (go2sky.Span, context.Context, error) {
  165. tracer := GetSkyWalkingTracer()
  166. if tracer == nil {
  167. return nil, nil, tracerNotFoundError
  168. }
  169. return tracer.CreateEntrySpan(getGinRequestContext(ctx), opName, handlerHeader)
  170. }