trace.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2020 getensh.com. All rights reserved.
  2. // Use of this source code is governed by getensh.com.
  3. /**
  4. * @Author: mac
  5. * @Description:
  6. * @Date: 2020/3/25 9:46
  7. */
  8. package trace
  9. import (
  10. "context"
  11. "github.com/gin-gonic/gin"
  12. "github.com/opentracing/opentracing-go/ext"
  13. "github.com/uber/jaeger-client-go"
  14. "github.com/uber/jaeger-client-go/config"
  15. "google.golang.org/grpc/metadata"
  16. "io"
  17. "log"
  18. "net/http"
  19. "net/url"
  20. "regexp"
  21. "strings"
  22. "github.com/opentracing-contrib/go-stdlib/nethttp"
  23. "github.com/opentracing/opentracing-go"
  24. )
  25. type JaegerConf struct {
  26. Open bool
  27. AgentHostPort string
  28. SamplerType string
  29. SamplerParam float64
  30. LogSpans bool
  31. }
  32. /* url path 过滤名单,加入的url path不会进行调用链追踪*/
  33. // 不确定全路径,需要正则的加到此处
  34. var urlPathFiltersRegexp = []string{
  35. "/swagger/*",
  36. "/*.cfg",
  37. }
  38. // 可以确定全路径的加到此处
  39. var urlPathFiltersMap = map[string]bool{
  40. "/": true,
  41. "/metrics": true,
  42. "/favicon.ico": true,
  43. "/version": true,
  44. }
  45. // 增加要跳过的全路径
  46. func FilterPathAdd(filters ...string) {
  47. for _, path := range filters {
  48. urlPathFiltersMap[path] = true
  49. }
  50. }
  51. // 增加要跳过的路径正则
  52. func FilterPathRegexpAdd(filters ...string) {
  53. urlPathFiltersRegexp = append(urlPathFiltersRegexp, filters...)
  54. }
  55. type MDReaderWriter struct {
  56. metadata.MD
  57. }
  58. // ForeachKey 实现 ForeachKey of opentracing.TextMapReader
  59. func (c MDReaderWriter) ForeachKey(handler func(key, val string) error) error {
  60. for k, vs := range c.MD {
  61. for _, v := range vs {
  62. if err := handler(k, v); err != nil {
  63. return err
  64. }
  65. }
  66. }
  67. return nil
  68. }
  69. // Set 实现 Set() of opentracing.TextMapWriter
  70. func (c MDReaderWriter) Set(key, val string) {
  71. key = strings.ToLower(key)
  72. c.MD[key] = append(c.MD[key], val)
  73. }
  74. // NewJaegerTracer jaeger初始化
  75. func NewJaegerTracer(service string, jaegerConf JaegerConf) (opentracing.Tracer, io.Closer) {
  76. cfg := &config.Configuration{
  77. Sampler: &config.SamplerConfig{
  78. Type: jaegerConf.SamplerType,
  79. Param: jaegerConf.SamplerParam,
  80. },
  81. Reporter: &config.ReporterConfig{
  82. LogSpans: jaegerConf.LogSpans, // 关闭打印span上报日志
  83. LocalAgentHostPort: jaegerConf.AgentHostPort,
  84. },
  85. }
  86. tracer, closer, err := cfg.New(service, config.Logger(jaeger.StdLogger))
  87. if err != nil {
  88. log.Fatalf("ERROR: cannot init tracer: %v\n", err)
  89. }
  90. opentracing.SetGlobalTracer(tracer)
  91. return tracer, closer
  92. }
  93. // JaegerPluginsForGin gin jaeger 中间件,在每次http调用前拦截,进行trace注入
  94. func JaegerPluginsForGin() gin.HandlerFunc {
  95. return func(c *gin.Context) {
  96. // 如果不需要链路的,直接跳过
  97. urlPath := c.Request.URL.Path
  98. if _, ok := urlPathFiltersMap[urlPath]; ok {
  99. c.Next()
  100. return
  101. }
  102. for _, pattern := range urlPathFiltersRegexp {
  103. if mathed, _ := regexp.MatchString(pattern, urlPath); mathed {
  104. c.Next()
  105. return
  106. }
  107. }
  108. var parentSpan opentracing.Span
  109. tracer := opentracing.GlobalTracer()
  110. spCtx, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
  111. opName := "HTTP " + c.Request.Method + " " + urlPath
  112. if err != nil {
  113. parentSpan = tracer.StartSpan(opName)
  114. defer parentSpan.Finish()
  115. } else {
  116. parentSpan = opentracing.StartSpan(
  117. opName,
  118. opentracing.ChildOf(spCtx),
  119. opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
  120. ext.SpanKindRPCServer,
  121. )
  122. defer parentSpan.Finish()
  123. }
  124. ctx := opentracing.ContextWithSpan(c.Request.Context(), parentSpan)
  125. // 设置 request ID,可用于日志记录, 使用ctx.Value("RequestId")可以取出
  126. if sc, ok := parentSpan.Context().(jaeger.SpanContext); ok {
  127. traceId := sc.TraceID().String()
  128. ctx = context.WithValue(ctx, "RequestId", traceId)
  129. c.Header("X-Request-Id", traceId)
  130. }
  131. c.Request = c.Request.WithContext(ctx)
  132. c.Next()
  133. }
  134. }
  135. // JaegerPluginsForBeego beego jaeger中间件,在每次http调用前拦截,进行trace注入
  136. func JaegerPluginsForBeego(componentName string) func(http.Handler) http.Handler {
  137. return func(h http.Handler) http.Handler {
  138. opts := []nethttp.MWOption{
  139. nethttp.MWComponentName(componentName),
  140. nethttp.OperationNameFunc(func(r *http.Request) string {
  141. return "HTTP " + r.Method + " " + r.URL.Path
  142. }),
  143. nethttp.MWURLTagFunc(func(u *url.URL) string {
  144. return u.String()
  145. }),
  146. nethttp.MWSpanFilter(func(r *http.Request) bool {
  147. // 如果不需要链路的,直接跳过
  148. if _, ok := urlPathFiltersMap[r.URL.Path]; ok {
  149. return false
  150. }
  151. for _, pattern := range urlPathFiltersRegexp {
  152. if mathed, _ := regexp.MatchString(pattern, r.URL.Path); mathed {
  153. return false
  154. }
  155. }
  156. return true
  157. }),
  158. nethttp.MWSpanObserver(func(span opentracing.Span, r *http.Request) {
  159. ctx := opentracing.ContextWithSpan(r.Context(), span)
  160. // 设置 request ID,可用于日志记录, 使用ctx.Value("RequestId")可以取出
  161. if sc, ok := span.Context().(jaeger.SpanContext); ok {
  162. traceId := sc.TraceID().String()
  163. ctx = context.WithValue(ctx, "RequestId", traceId)
  164. r.Header.Set("X-Request-Id", traceId)
  165. }
  166. }),
  167. }
  168. return nethttp.Middleware(opentracing.GlobalTracer(), h, opts...)
  169. }
  170. }
  171. // 继承 ctx,放置断链
  172. func CtxFromParent(parentCtx context.Context) context.Context {
  173. span := opentracing.SpanFromContext(parentCtx)
  174. return opentracing.ContextWithSpan(context.Background(), span)
  175. }
  176. // 根据父级ctx生成子级span和ctx,同服务调用时使用(如mysql、redis或者服务内方法)
  177. func StartChildSpan(parentCtx context.Context, operationName string) (opentracing.Span, context.Context) {
  178. return opentracing.StartSpanFromContext(parentCtx, operationName)
  179. }