request_id.go 2.7 KB

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