tracinghandler.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package handler
  2. import (
  3. "net/http"
  4. "github.com/zeromicro/go-zero/core/collection"
  5. "github.com/zeromicro/go-zero/core/trace"
  6. "github.com/zeromicro/go-zero/rest/internal/response"
  7. "go.opentelemetry.io/otel"
  8. "go.opentelemetry.io/otel/propagation"
  9. semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
  10. oteltrace "go.opentelemetry.io/otel/trace"
  11. )
  12. type (
  13. // TracingOption defines the method to customize an tracingOptions.
  14. TracingOption func(options *tracingOptions)
  15. // tracingOptions is TracingHandler options.
  16. tracingOptions struct {
  17. traceIgnorePaths []string
  18. }
  19. )
  20. // TracingHandler return a middleware that process the opentelemetry.
  21. func TracingHandler(serviceName, path string, opts ...TracingOption) func(http.Handler) http.Handler {
  22. var tracingOpts tracingOptions
  23. for _, opt := range opts {
  24. opt(&tracingOpts)
  25. }
  26. ignorePaths := collection.NewSet()
  27. ignorePaths.AddStr(tracingOpts.traceIgnorePaths...)
  28. traceHandler := func(checkIgnore bool) func(http.Handler) http.Handler {
  29. return func(next http.Handler) http.Handler {
  30. propagator := otel.GetTextMapPropagator()
  31. tracer := otel.GetTracerProvider().Tracer(trace.TraceName)
  32. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  33. spanName := path
  34. if len(spanName) == 0 {
  35. spanName = r.URL.Path
  36. }
  37. if checkIgnore && ignorePaths.Contains(spanName) {
  38. next.ServeHTTP(w, r)
  39. return
  40. }
  41. ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
  42. spanCtx, span := tracer.Start(
  43. ctx,
  44. spanName,
  45. oteltrace.WithSpanKind(oteltrace.SpanKindServer),
  46. oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(
  47. serviceName, spanName, r)...),
  48. )
  49. defer span.End()
  50. // convenient for tracking error messages
  51. propagator.Inject(spanCtx, propagation.HeaderCarrier(w.Header()))
  52. trw := &response.WithCodeResponseWriter{Writer: w, Code: http.StatusOK}
  53. next.ServeHTTP(trw, r.WithContext(spanCtx))
  54. span.SetAttributes(semconv.HTTPAttributesFromHTTPStatusCode(trw.Code)...)
  55. span.SetStatus(semconv.SpanStatusFromHTTPStatusCodeAndSpanKind(trw.Code, oteltrace.SpanKindServer))
  56. })
  57. }
  58. }
  59. checkIgnore := ignorePaths.Count() > 0
  60. return traceHandler(checkIgnore)
  61. }
  62. // WithTraceIgnorePaths specifies the traceIgnorePaths option for TracingHandler.
  63. func WithTraceIgnorePaths(traceIgnorePaths []string) TracingOption {
  64. return func(options *tracingOptions) {
  65. options.traceIgnorePaths = append(options.traceIgnorePaths, traceIgnorePaths...)
  66. }
  67. }