tracinghandler.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package handler
  2. import (
  3. "bufio"
  4. "errors"
  5. "net"
  6. "net/http"
  7. "sync"
  8. "github.com/zeromicro/go-zero/core/lang"
  9. "github.com/zeromicro/go-zero/core/trace"
  10. "go.opentelemetry.io/otel"
  11. "go.opentelemetry.io/otel/attribute"
  12. "go.opentelemetry.io/otel/codes"
  13. "go.opentelemetry.io/otel/propagation"
  14. semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
  15. oteltrace "go.opentelemetry.io/otel/trace"
  16. )
  17. const traceKeyStatusCode = "http.status_code"
  18. var notTracingSpans sync.Map
  19. // DontTraceSpan disable tracing for the specified span name.
  20. func DontTraceSpan(spanName string) {
  21. notTracingSpans.Store(spanName, lang.Placeholder)
  22. }
  23. type traceResponseWriter struct {
  24. w http.ResponseWriter
  25. code int
  26. }
  27. // Flush implements the http.Flusher interface.
  28. func (w *traceResponseWriter) Flush() {
  29. if flusher, ok := w.w.(http.Flusher); ok {
  30. flusher.Flush()
  31. }
  32. }
  33. // Hijack implements the http.Hijacker interface.
  34. func (w *traceResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
  35. if hijacked, ok := w.w.(http.Hijacker); ok {
  36. return hijacked.Hijack()
  37. }
  38. return nil, nil, errors.New("server doesn't support hijacking")
  39. }
  40. func (w *traceResponseWriter) Header() http.Header {
  41. return w.w.Header()
  42. }
  43. func (w *traceResponseWriter) Write(data []byte) (int, error) {
  44. return w.w.Write(data)
  45. }
  46. func (w *traceResponseWriter) WriteHeader(statusCode int) {
  47. w.w.WriteHeader(statusCode)
  48. w.code = statusCode
  49. }
  50. // TracingHandler return a middleware that process the opentelemetry.
  51. func TracingHandler(serviceName, path string) func(http.Handler) http.Handler {
  52. return func(next http.Handler) http.Handler {
  53. propagator := otel.GetTextMapPropagator()
  54. tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
  55. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  56. spanName := path
  57. if len(spanName) == 0 {
  58. spanName = r.URL.Path
  59. }
  60. if _, ok := notTracingSpans.Load(spanName); ok {
  61. next.ServeHTTP(w, r)
  62. return
  63. }
  64. ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
  65. spanCtx, span := tracer.Start(
  66. ctx,
  67. spanName,
  68. oteltrace.WithSpanKind(oteltrace.SpanKindServer),
  69. oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(
  70. serviceName, spanName, r)...),
  71. )
  72. defer span.End()
  73. // convenient for tracking error messages
  74. propagator.Inject(spanCtx, propagation.HeaderCarrier(w.Header()))
  75. trw := &traceResponseWriter{
  76. w: w,
  77. code: http.StatusOK,
  78. }
  79. next.ServeHTTP(trw, r.WithContext(spanCtx))
  80. span.SetAttributes(attribute.KeyValue{
  81. Key: traceKeyStatusCode,
  82. Value: attribute.IntValue(trw.code),
  83. })
  84. if trw.code >= http.StatusBadRequest {
  85. span.SetStatus(codes.Error, http.StatusText(trw.code))
  86. } else {
  87. span.SetStatus(codes.Ok, http.StatusText(trw.code))
  88. }
  89. })
  90. }
  91. }