request_id.go 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package tsweb
  4. import (
  5. "context"
  6. "net/http"
  7. "tailscale.com/util/ctxkey"
  8. "tailscale.com/util/fastuuid"
  9. )
  10. // RequestID is an opaque identifier for a HTTP request, used to correlate
  11. // user-visible errors with backend server logs. The RequestID is typically
  12. // threaded through an HTTP Middleware (WithRequestID) and then can be extracted
  13. // by HTTP Handlers to include in their logs.
  14. //
  15. // RequestID is an opaque identifier for a HTTP request, used to correlate
  16. // user-visible errors with backend server logs. If present in the context, the
  17. // RequestID will be printed alongside the message text and logged in the
  18. // AccessLogRecord.
  19. //
  20. // A RequestID has the format "REQ-1{ID}", and the ID should be treated as an
  21. // opaque string. The current implementation uses a UUID.
  22. type RequestID string
  23. // String returns the string format of the request ID, for use in e.g. setting
  24. // a [http.Header].
  25. func (r RequestID) String() string {
  26. return string(r)
  27. }
  28. // RequestIDKey stores and loads [RequestID] values within a [context.Context].
  29. var RequestIDKey ctxkey.Key[RequestID]
  30. // RequestIDHeader is a custom HTTP header that the WithRequestID middleware
  31. // uses to determine whether to re-use a given request ID from the client
  32. // or generate a new one.
  33. const RequestIDHeader = "X-Tailscale-Request-Id"
  34. // GenerateRequestID generates a new request ID with the current format.
  35. func GenerateRequestID() RequestID {
  36. // REQ-1 indicates the version of the RequestID pattern. It is
  37. // currently arbitrary but allows for forward compatible
  38. // transitions if needed.
  39. return RequestID("REQ-1" + fastuuid.NewUUID().String())
  40. }
  41. // SetRequestID is an HTTP middleware that injects a RequestID in the
  42. // *http.Request Context. The value of that request id is either retrieved from
  43. // the RequestIDHeader or a randomly generated one if not exists. Inner
  44. // handlers can retrieve this ID from the RequestIDFromContext function.
  45. func SetRequestID(h http.Handler) http.Handler {
  46. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  47. var rid RequestID
  48. if id := r.Header.Get(RequestIDHeader); id != "" {
  49. rid = RequestID(id)
  50. } else {
  51. rid = GenerateRequestID()
  52. }
  53. ctx := RequestIDKey.WithValue(r.Context(), rid)
  54. r = r.WithContext(ctx)
  55. h.ServeHTTP(w, r)
  56. })
  57. }
  58. // RequestIDFromContext retrieves the RequestID from context that can be set by
  59. // the SetRequestID function.
  60. //
  61. // Deprecated: Use [RequestIDKey.Value] instead.
  62. func RequestIDFromContext(ctx context.Context) RequestID {
  63. return RequestIDKey.Value(ctx)
  64. }