You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

441 lines
11 KiB

  1. // Copyright 2018, OpenCensus Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package ochttp
  15. import (
  16. "context"
  17. "io"
  18. "net/http"
  19. "strconv"
  20. "sync"
  21. "time"
  22. "go.opencensus.io/stats"
  23. "go.opencensus.io/tag"
  24. "go.opencensus.io/trace"
  25. "go.opencensus.io/trace/propagation"
  26. )
  27. // Handler is an http.Handler wrapper to instrument your HTTP server with
  28. // OpenCensus. It supports both stats and tracing.
  29. //
  30. // Tracing
  31. //
  32. // This handler is aware of the incoming request's span, reading it from request
  33. // headers as configured using the Propagation field.
  34. // The extracted span can be accessed from the incoming request's
  35. // context.
  36. //
  37. // span := trace.FromContext(r.Context())
  38. //
  39. // The server span will be automatically ended at the end of ServeHTTP.
  40. type Handler struct {
  41. // Propagation defines how traces are propagated. If unspecified,
  42. // B3 propagation will be used.
  43. Propagation propagation.HTTPFormat
  44. // Handler is the handler used to handle the incoming request.
  45. Handler http.Handler
  46. // StartOptions are applied to the span started by this Handler around each
  47. // request.
  48. //
  49. // StartOptions.SpanKind will always be set to trace.SpanKindServer
  50. // for spans started by this transport.
  51. StartOptions trace.StartOptions
  52. // GetStartOptions allows to set start options per request. If set,
  53. // StartOptions is going to be ignored.
  54. GetStartOptions func(*http.Request) trace.StartOptions
  55. // IsPublicEndpoint should be set to true for publicly accessible HTTP(S)
  56. // servers. If true, any trace metadata set on the incoming request will
  57. // be added as a linked trace instead of being added as a parent of the
  58. // current trace.
  59. IsPublicEndpoint bool
  60. // FormatSpanName holds the function to use for generating the span name
  61. // from the information found in the incoming HTTP Request. By default the
  62. // name equals the URL Path.
  63. FormatSpanName func(*http.Request) string
  64. }
  65. func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  66. var tags addedTags
  67. r, traceEnd := h.startTrace(w, r)
  68. defer traceEnd()
  69. w, statsEnd := h.startStats(w, r)
  70. defer statsEnd(&tags)
  71. handler := h.Handler
  72. if handler == nil {
  73. handler = http.DefaultServeMux
  74. }
  75. r = r.WithContext(context.WithValue(r.Context(), addedTagsKey{}, &tags))
  76. handler.ServeHTTP(w, r)
  77. }
  78. func (h *Handler) startTrace(w http.ResponseWriter, r *http.Request) (*http.Request, func()) {
  79. if isHealthEndpoint(r.URL.Path) {
  80. return r, func() {}
  81. }
  82. var name string
  83. if h.FormatSpanName == nil {
  84. name = spanNameFromURL(r)
  85. } else {
  86. name = h.FormatSpanName(r)
  87. }
  88. ctx := r.Context()
  89. startOpts := h.StartOptions
  90. if h.GetStartOptions != nil {
  91. startOpts = h.GetStartOptions(r)
  92. }
  93. var span *trace.Span
  94. sc, ok := h.extractSpanContext(r)
  95. if ok && !h.IsPublicEndpoint {
  96. ctx, span = trace.StartSpanWithRemoteParent(ctx, name, sc,
  97. trace.WithSampler(startOpts.Sampler),
  98. trace.WithSpanKind(trace.SpanKindServer))
  99. } else {
  100. ctx, span = trace.StartSpan(ctx, name,
  101. trace.WithSampler(startOpts.Sampler),
  102. trace.WithSpanKind(trace.SpanKindServer),
  103. )
  104. if ok {
  105. span.AddLink(trace.Link{
  106. TraceID: sc.TraceID,
  107. SpanID: sc.SpanID,
  108. Type: trace.LinkTypeParent,
  109. Attributes: nil,
  110. })
  111. }
  112. }
  113. span.AddAttributes(requestAttrs(r)...)
  114. return r.WithContext(ctx), span.End
  115. }
  116. func (h *Handler) extractSpanContext(r *http.Request) (trace.SpanContext, bool) {
  117. if h.Propagation == nil {
  118. return defaultFormat.SpanContextFromRequest(r)
  119. }
  120. return h.Propagation.SpanContextFromRequest(r)
  121. }
  122. func (h *Handler) startStats(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, func(tags *addedTags)) {
  123. ctx, _ := tag.New(r.Context(),
  124. tag.Upsert(Host, r.Host),
  125. tag.Upsert(Path, r.URL.Path),
  126. tag.Upsert(Method, r.Method))
  127. track := &trackingResponseWriter{
  128. start: time.Now(),
  129. ctx: ctx,
  130. writer: w,
  131. }
  132. if r.Body == nil {
  133. // TODO: Handle cases where ContentLength is not set.
  134. track.reqSize = -1
  135. } else if r.ContentLength > 0 {
  136. track.reqSize = r.ContentLength
  137. }
  138. stats.Record(ctx, ServerRequestCount.M(1))
  139. return track.wrappedResponseWriter(), track.end
  140. }
  141. type trackingResponseWriter struct {
  142. ctx context.Context
  143. reqSize int64
  144. respSize int64
  145. start time.Time
  146. statusCode int
  147. statusLine string
  148. endOnce sync.Once
  149. writer http.ResponseWriter
  150. }
  151. // Compile time assertion for ResponseWriter interface
  152. var _ http.ResponseWriter = (*trackingResponseWriter)(nil)
  153. var logTagsErrorOnce sync.Once
  154. func (t *trackingResponseWriter) end(tags *addedTags) {
  155. t.endOnce.Do(func() {
  156. if t.statusCode == 0 {
  157. t.statusCode = 200
  158. }
  159. span := trace.FromContext(t.ctx)
  160. span.SetStatus(TraceStatus(t.statusCode, t.statusLine))
  161. span.AddAttributes(trace.Int64Attribute(StatusCodeAttribute, int64(t.statusCode)))
  162. m := []stats.Measurement{
  163. ServerLatency.M(float64(time.Since(t.start)) / float64(time.Millisecond)),
  164. ServerResponseBytes.M(t.respSize),
  165. }
  166. if t.reqSize >= 0 {
  167. m = append(m, ServerRequestBytes.M(t.reqSize))
  168. }
  169. allTags := make([]tag.Mutator, len(tags.t)+1)
  170. allTags[0] = tag.Upsert(StatusCode, strconv.Itoa(t.statusCode))
  171. copy(allTags[1:], tags.t)
  172. stats.RecordWithTags(t.ctx, allTags, m...)
  173. })
  174. }
  175. func (t *trackingResponseWriter) Header() http.Header {
  176. return t.writer.Header()
  177. }
  178. func (t *trackingResponseWriter) Write(data []byte) (int, error) {
  179. n, err := t.writer.Write(data)
  180. t.respSize += int64(n)
  181. return n, err
  182. }
  183. func (t *trackingResponseWriter) WriteHeader(statusCode int) {
  184. t.writer.WriteHeader(statusCode)
  185. t.statusCode = statusCode
  186. t.statusLine = http.StatusText(t.statusCode)
  187. }
  188. // wrappedResponseWriter returns a wrapped version of the original
  189. // ResponseWriter and only implements the same combination of additional
  190. // interfaces as the original.
  191. // This implementation is based on https://github.com/felixge/httpsnoop.
  192. func (t *trackingResponseWriter) wrappedResponseWriter() http.ResponseWriter {
  193. var (
  194. hj, i0 = t.writer.(http.Hijacker)
  195. cn, i1 = t.writer.(http.CloseNotifier)
  196. pu, i2 = t.writer.(http.Pusher)
  197. fl, i3 = t.writer.(http.Flusher)
  198. rf, i4 = t.writer.(io.ReaderFrom)
  199. )
  200. switch {
  201. case !i0 && !i1 && !i2 && !i3 && !i4:
  202. return struct {
  203. http.ResponseWriter
  204. }{t}
  205. case !i0 && !i1 && !i2 && !i3 && i4:
  206. return struct {
  207. http.ResponseWriter
  208. io.ReaderFrom
  209. }{t, rf}
  210. case !i0 && !i1 && !i2 && i3 && !i4:
  211. return struct {
  212. http.ResponseWriter
  213. http.Flusher
  214. }{t, fl}
  215. case !i0 && !i1 && !i2 && i3 && i4:
  216. return struct {
  217. http.ResponseWriter
  218. http.Flusher
  219. io.ReaderFrom
  220. }{t, fl, rf}
  221. case !i0 && !i1 && i2 && !i3 && !i4:
  222. return struct {
  223. http.ResponseWriter
  224. http.Pusher
  225. }{t, pu}
  226. case !i0 && !i1 && i2 && !i3 && i4:
  227. return struct {
  228. http.ResponseWriter
  229. http.Pusher
  230. io.ReaderFrom
  231. }{t, pu, rf}
  232. case !i0 && !i1 && i2 && i3 && !i4:
  233. return struct {
  234. http.ResponseWriter
  235. http.Pusher
  236. http.Flusher
  237. }{t, pu, fl}
  238. case !i0 && !i1 && i2 && i3 && i4:
  239. return struct {
  240. http.ResponseWriter
  241. http.Pusher
  242. http.Flusher
  243. io.ReaderFrom
  244. }{t, pu, fl, rf}
  245. case !i0 && i1 && !i2 && !i3 && !i4:
  246. return struct {
  247. http.ResponseWriter
  248. http.CloseNotifier
  249. }{t, cn}
  250. case !i0 && i1 && !i2 && !i3 && i4:
  251. return struct {
  252. http.ResponseWriter
  253. http.CloseNotifier
  254. io.ReaderFrom
  255. }{t, cn, rf}
  256. case !i0 && i1 && !i2 && i3 && !i4:
  257. return struct {
  258. http.ResponseWriter
  259. http.CloseNotifier
  260. http.Flusher
  261. }{t, cn, fl}
  262. case !i0 && i1 && !i2 && i3 && i4:
  263. return struct {
  264. http.ResponseWriter
  265. http.CloseNotifier
  266. http.Flusher
  267. io.ReaderFrom
  268. }{t, cn, fl, rf}
  269. case !i0 && i1 && i2 && !i3 && !i4:
  270. return struct {
  271. http.ResponseWriter
  272. http.CloseNotifier
  273. http.Pusher
  274. }{t, cn, pu}
  275. case !i0 && i1 && i2 && !i3 && i4:
  276. return struct {
  277. http.ResponseWriter
  278. http.CloseNotifier
  279. http.Pusher
  280. io.ReaderFrom
  281. }{t, cn, pu, rf}
  282. case !i0 && i1 && i2 && i3 && !i4:
  283. return struct {
  284. http.ResponseWriter
  285. http.CloseNotifier
  286. http.Pusher
  287. http.Flusher
  288. }{t, cn, pu, fl}
  289. case !i0 && i1 && i2 && i3 && i4:
  290. return struct {
  291. http.ResponseWriter
  292. http.CloseNotifier
  293. http.Pusher
  294. http.Flusher
  295. io.ReaderFrom
  296. }{t, cn, pu, fl, rf}
  297. case i0 && !i1 && !i2 && !i3 && !i4:
  298. return struct {
  299. http.ResponseWriter
  300. http.Hijacker
  301. }{t, hj}
  302. case i0 && !i1 && !i2 && !i3 && i4:
  303. return struct {
  304. http.ResponseWriter
  305. http.Hijacker
  306. io.ReaderFrom
  307. }{t, hj, rf}
  308. case i0 && !i1 && !i2 && i3 && !i4:
  309. return struct {
  310. http.ResponseWriter
  311. http.Hijacker
  312. http.Flusher
  313. }{t, hj, fl}
  314. case i0 && !i1 && !i2 && i3 && i4:
  315. return struct {
  316. http.ResponseWriter
  317. http.Hijacker
  318. http.Flusher
  319. io.ReaderFrom
  320. }{t, hj, fl, rf}
  321. case i0 && !i1 && i2 && !i3 && !i4:
  322. return struct {
  323. http.ResponseWriter
  324. http.Hijacker
  325. http.Pusher
  326. }{t, hj, pu}
  327. case i0 && !i1 && i2 && !i3 && i4:
  328. return struct {
  329. http.ResponseWriter
  330. http.Hijacker
  331. http.Pusher
  332. io.ReaderFrom
  333. }{t, hj, pu, rf}
  334. case i0 && !i1 && i2 && i3 && !i4:
  335. return struct {
  336. http.ResponseWriter
  337. http.Hijacker
  338. http.Pusher
  339. http.Flusher
  340. }{t, hj, pu, fl}
  341. case i0 && !i1 && i2 && i3 && i4:
  342. return struct {
  343. http.ResponseWriter
  344. http.Hijacker
  345. http.Pusher
  346. http.Flusher
  347. io.ReaderFrom
  348. }{t, hj, pu, fl, rf}
  349. case i0 && i1 && !i2 && !i3 && !i4:
  350. return struct {
  351. http.ResponseWriter
  352. http.Hijacker
  353. http.CloseNotifier
  354. }{t, hj, cn}
  355. case i0 && i1 && !i2 && !i3 && i4:
  356. return struct {
  357. http.ResponseWriter
  358. http.Hijacker
  359. http.CloseNotifier
  360. io.ReaderFrom
  361. }{t, hj, cn, rf}
  362. case i0 && i1 && !i2 && i3 && !i4:
  363. return struct {
  364. http.ResponseWriter
  365. http.Hijacker
  366. http.CloseNotifier
  367. http.Flusher
  368. }{t, hj, cn, fl}
  369. case i0 && i1 && !i2 && i3 && i4:
  370. return struct {
  371. http.ResponseWriter
  372. http.Hijacker
  373. http.CloseNotifier
  374. http.Flusher
  375. io.ReaderFrom
  376. }{t, hj, cn, fl, rf}
  377. case i0 && i1 && i2 && !i3 && !i4:
  378. return struct {
  379. http.ResponseWriter
  380. http.Hijacker
  381. http.CloseNotifier
  382. http.Pusher
  383. }{t, hj, cn, pu}
  384. case i0 && i1 && i2 && !i3 && i4:
  385. return struct {
  386. http.ResponseWriter
  387. http.Hijacker
  388. http.CloseNotifier
  389. http.Pusher
  390. io.ReaderFrom
  391. }{t, hj, cn, pu, rf}
  392. case i0 && i1 && i2 && i3 && !i4:
  393. return struct {
  394. http.ResponseWriter
  395. http.Hijacker
  396. http.CloseNotifier
  397. http.Pusher
  398. http.Flusher
  399. }{t, hj, cn, pu, fl}
  400. case i0 && i1 && i2 && i3 && i4:
  401. return struct {
  402. http.ResponseWriter
  403. http.Hijacker
  404. http.CloseNotifier
  405. http.Pusher
  406. http.Flusher
  407. io.ReaderFrom
  408. }{t, hj, cn, pu, fl, rf}
  409. default:
  410. return struct {
  411. http.ResponseWriter
  412. }{t}
  413. }
  414. }