gclient_tracing.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package gclient
  7. import (
  8. "context"
  9. "fmt"
  10. "io/ioutil"
  11. "net/http"
  12. "net/http/httptrace"
  13. "go.opentelemetry.io/otel"
  14. "go.opentelemetry.io/otel/attribute"
  15. "go.opentelemetry.io/otel/codes"
  16. "go.opentelemetry.io/otel/propagation"
  17. "go.opentelemetry.io/otel/trace"
  18. "github.com/gogf/gf/v2"
  19. "github.com/gogf/gf/v2/internal/httputil"
  20. "github.com/gogf/gf/v2/internal/utils"
  21. "github.com/gogf/gf/v2/net/gtrace"
  22. "github.com/gogf/gf/v2/os/gctx"
  23. "github.com/gogf/gf/v2/text/gstr"
  24. "github.com/gogf/gf/v2/util/gconv"
  25. )
  26. const (
  27. tracingInstrumentName = "github.com/gogf/gf/v2/net/gclient.Client"
  28. tracingAttrHttpAddressRemote = "http.address.remote"
  29. tracingAttrHttpAddressLocal = "http.address.local"
  30. tracingAttrHttpDnsStart = "http.dns.start"
  31. tracingAttrHttpDnsDone = "http.dns.done"
  32. tracingAttrHttpConnectStart = "http.connect.start"
  33. tracingAttrHttpConnectDone = "http.connect.done"
  34. tracingEventHttpRequest = "http.request"
  35. tracingEventHttpRequestHeaders = "http.request.headers"
  36. tracingEventHttpRequestBaggage = "http.request.baggage"
  37. tracingEventHttpRequestBody = "http.request.body"
  38. tracingEventHttpResponse = "http.response"
  39. tracingEventHttpResponseHeaders = "http.response.headers"
  40. tracingEventHttpResponseBody = "http.response.body"
  41. tracingMiddlewareHandled gctx.StrKey = `MiddlewareClientTracingHandled`
  42. )
  43. // internalMiddlewareTracing is a client middleware that enables tracing feature using standards of OpenTelemetry.
  44. func internalMiddlewareTracing(c *Client, r *http.Request) (response *Response, err error) {
  45. var ctx = r.Context()
  46. // Mark this request is handled by server tracing middleware,
  47. // to avoid repeated handling by the same middleware.
  48. if ctx.Value(tracingMiddlewareHandled) != nil {
  49. return c.Next(r)
  50. }
  51. ctx = context.WithValue(ctx, tracingMiddlewareHandled, 1)
  52. tr := otel.GetTracerProvider().Tracer(
  53. tracingInstrumentName,
  54. trace.WithInstrumentationVersion(gf.VERSION),
  55. )
  56. ctx, span := tr.Start(ctx, r.URL.String(), trace.WithSpanKind(trace.SpanKindClient))
  57. defer span.End()
  58. span.SetAttributes(gtrace.CommonLabels()...)
  59. // Inject tracing content into http header.
  60. otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(r.Header))
  61. // If it is now using default trace provider, it then does no complex tracing jobs.
  62. if gtrace.IsUsingDefaultProvider() {
  63. response, err = c.Next(r)
  64. return
  65. }
  66. // Continue client handler executing.
  67. response, err = c.Next(
  68. r.WithContext(
  69. httptrace.WithClientTrace(
  70. ctx, newClientTrace(ctx, span, r),
  71. ),
  72. ),
  73. )
  74. if err != nil {
  75. span.SetStatus(codes.Error, fmt.Sprintf(`%+v`, err))
  76. }
  77. if response == nil || response.Response == nil {
  78. return
  79. }
  80. reqBodyContentBytes, _ := ioutil.ReadAll(response.Body)
  81. response.Body = utils.NewReadCloser(reqBodyContentBytes, false)
  82. span.AddEvent(tracingEventHttpResponse, trace.WithAttributes(
  83. attribute.String(tracingEventHttpResponseHeaders, gconv.String(httputil.HeaderToMap(response.Header))),
  84. attribute.String(tracingEventHttpResponseBody, gstr.StrLimit(
  85. string(reqBodyContentBytes),
  86. gtrace.MaxContentLogSize(),
  87. "...",
  88. )),
  89. ))
  90. return
  91. }