server.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. // +build go1.7
  2. package nethttp
  3. import (
  4. "net/http"
  5. "net/url"
  6. opentracing "github.com/opentracing/opentracing-go"
  7. "github.com/opentracing/opentracing-go/ext"
  8. )
  9. type mwOptions struct {
  10. opNameFunc func(r *http.Request) string
  11. spanFilter func(r *http.Request) bool
  12. spanObserver func(span opentracing.Span, r *http.Request)
  13. urlTagFunc func(u *url.URL) string
  14. componentName string
  15. }
  16. // MWOption controls the behavior of the Middleware.
  17. type MWOption func(*mwOptions)
  18. // OperationNameFunc returns a MWOption that uses given function f
  19. // to generate operation name for each server-side span.
  20. func OperationNameFunc(f func(r *http.Request) string) MWOption {
  21. return func(options *mwOptions) {
  22. options.opNameFunc = f
  23. }
  24. }
  25. // MWComponentName returns a MWOption that sets the component name
  26. // for the server-side span.
  27. func MWComponentName(componentName string) MWOption {
  28. return func(options *mwOptions) {
  29. options.componentName = componentName
  30. }
  31. }
  32. // MWSpanFilter returns a MWOption that filters requests from creating a span
  33. // for the server-side span.
  34. // Span won't be created if it returns false.
  35. func MWSpanFilter(f func(r *http.Request) bool) MWOption {
  36. return func(options *mwOptions) {
  37. options.spanFilter = f
  38. }
  39. }
  40. // MWSpanObserver returns a MWOption that observe the span
  41. // for the server-side span.
  42. func MWSpanObserver(f func(span opentracing.Span, r *http.Request)) MWOption {
  43. return func(options *mwOptions) {
  44. options.spanObserver = f
  45. }
  46. }
  47. // MWURLTagFunc returns a MWOption that uses given function f
  48. // to set the span's http.url tag. Can be used to change the default
  49. // http.url tag, eg to redact sensitive information.
  50. func MWURLTagFunc(f func(u *url.URL) string) MWOption {
  51. return func(options *mwOptions) {
  52. options.urlTagFunc = f
  53. }
  54. }
  55. // Middleware wraps an http.Handler and traces incoming requests.
  56. // Additionally, it adds the span to the request's context.
  57. //
  58. // By default, the operation name of the spans is set to "HTTP {method}".
  59. // This can be overriden with options.
  60. //
  61. // Example:
  62. // http.ListenAndServe("localhost:80", nethttp.Middleware(tracer, http.DefaultServeMux))
  63. //
  64. // The options allow fine tuning the behavior of the middleware.
  65. //
  66. // Example:
  67. // mw := nethttp.Middleware(
  68. // tracer,
  69. // http.DefaultServeMux,
  70. // nethttp.OperationNameFunc(func(r *http.Request) string {
  71. // return "HTTP " + r.Method + ":/api/customers"
  72. // }),
  73. // nethttp.MWSpanObserver(func(sp opentracing.Span, r *http.Request) {
  74. // sp.SetTag("http.uri", r.URL.EscapedPath())
  75. // }),
  76. // )
  77. func Middleware(tr opentracing.Tracer, h http.Handler, options ...MWOption) http.Handler {
  78. return MiddlewareFunc(tr, h.ServeHTTP, options...)
  79. }
  80. // MiddlewareFunc wraps an http.HandlerFunc and traces incoming requests.
  81. // It behaves identically to the Middleware function above.
  82. //
  83. // Example:
  84. // http.ListenAndServe("localhost:80", nethttp.MiddlewareFunc(tracer, MyHandler))
  85. func MiddlewareFunc(tr opentracing.Tracer, h http.HandlerFunc, options ...MWOption) http.HandlerFunc {
  86. opts := mwOptions{
  87. opNameFunc: func(r *http.Request) string {
  88. return "HTTP " + r.Method
  89. },
  90. spanFilter: func(r *http.Request) bool { return true },
  91. spanObserver: func(span opentracing.Span, r *http.Request) {},
  92. urlTagFunc: func(u *url.URL) string {
  93. return u.String()
  94. },
  95. }
  96. for _, opt := range options {
  97. opt(&opts)
  98. }
  99. fn := func(w http.ResponseWriter, r *http.Request) {
  100. if !opts.spanFilter(r) {
  101. h(w, r)
  102. return
  103. }
  104. ctx, _ := tr.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
  105. sp := tr.StartSpan(opts.opNameFunc(r), ext.RPCServerOption(ctx))
  106. ext.HTTPMethod.Set(sp, r.Method)
  107. ext.HTTPUrl.Set(sp, opts.urlTagFunc(r.URL))
  108. opts.spanObserver(sp, r)
  109. // set component name, use "net/http" if caller does not specify
  110. componentName := opts.componentName
  111. if componentName == "" {
  112. componentName = defaultComponentName
  113. }
  114. ext.Component.Set(sp, componentName)
  115. sct := &statusCodeTracker{ResponseWriter: w}
  116. r = r.WithContext(opentracing.ContextWithSpan(r.Context(), sp))
  117. defer func() {
  118. ext.HTTPStatusCode.Set(sp, uint16(sct.status))
  119. if sct.status >= http.StatusInternalServerError || !sct.wroteheader {
  120. ext.Error.Set(sp, true)
  121. }
  122. sp.Finish()
  123. }()
  124. h(sct.wrappedResponseWriter(), r)
  125. }
  126. return http.HandlerFunc(fn)
  127. }