mirror of https://github.com/tailscale/tailscale/
tsweb: propagate RequestID via context and entire request
The recent addition of RequestID was only populated if the HTTP Request had returned an error. This meant that the underlying handler has no access to this request id and any logs it may have emitted were impossible to correlate to that request id. Therefore, this PR adds a middleware to generate request ids and pass them through the request context. The tsweb.StdHandler automatically populates this request id if the middleware is being used. Finally, inner handlers can use the context to retrieve that same request id and use it so that all logs and events can be correlated. Updates #2549 Signed-off-by: Marwan Sulaiman <marwan@tailscale.com>marwan/displayname
parent
c27aa9e7ff
commit
b819f66eb1
@ -0,0 +1,63 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package tsweb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// RequestID is an opaque identifier for a HTTP request, used to correlate
|
||||
// user-visible errors with backend server logs. The RequestID is typically
|
||||
// threaded through an HTTP Middleware (WithRequestID) and then can be extracted
|
||||
// by HTTP Handlers to include in their logs.
|
||||
//
|
||||
// RequestID is an opaque identifier for a HTTP request, used to correlate
|
||||
// user-visible errors with backend server logs. If present in the context, the
|
||||
// RequestID will be printed alongside the message text and logged in the
|
||||
// AccessLogRecord.
|
||||
//
|
||||
// A RequestID has the format "REQ-1{ID}", and the ID should be treated as an
|
||||
// opaque string. The current implementation uses a UUID.
|
||||
type RequestID string
|
||||
|
||||
// RequestIDHeader is a custom HTTP header that the WithRequestID middleware
|
||||
// uses to determine whether to re-use a given request ID from the client
|
||||
// or generate a new one.
|
||||
const RequestIDHeader = "X-Tailscale-Request-Id"
|
||||
|
||||
// SetRequestID is an HTTP middleware that injects a RequestID in the
|
||||
// *http.Request Context. The value of that request id is either retrieved from
|
||||
// the RequestIDHeader or a randomly generated one if not exists. Inner
|
||||
// handlers can retrieve this ID from the RequestIDFromContext function.
|
||||
func SetRequestID(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.Header.Get(RequestIDHeader)
|
||||
if id == "" {
|
||||
// REQ-1 indicates the version of the RequestID pattern. It is
|
||||
// currently arbitrary but allows for forward compatible
|
||||
// transitions if needed.
|
||||
id = "REQ-1" + uuid.NewString()
|
||||
}
|
||||
ctx := withRequestID(r.Context(), RequestID(id))
|
||||
r = r.WithContext(ctx)
|
||||
h.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
type requestIDKey struct{}
|
||||
|
||||
// RequestIDFromContext retrieves the RequestID from context that can be set by
|
||||
// the SetRequestID function.
|
||||
func RequestIDFromContext(ctx context.Context) RequestID {
|
||||
val, _ := ctx.Value(requestIDKey{}).(RequestID)
|
||||
return val
|
||||
}
|
||||
|
||||
// withRequestID sets the given request id value in the given context.
|
||||
func withRequestID(ctx context.Context, rid RequestID) context.Context {
|
||||
return context.WithValue(ctx, requestIDKey{}, rid)
|
||||
}
|
Loading…
Reference in New Issue