diff --git a/cmd/start.go b/cmd/start.go index 6524684..4c9e445 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -37,6 +37,7 @@ var startCmd = &cobra.Command{ // Create middleware stack stack := middleware.CreateStack( middleware.Recovery(), // Add recovery middleware first to catch all panics + middleware.RequestID(), // Add request ID middleware for tracing middleware.SecureHeaders, middleware.Logging, middleware.MaxBodySize(1024*1024), // 1MB size limit diff --git a/go.mod b/go.mod index cea6373..caaddd8 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( ) require ( + github.com/google/uuid v1.6.0 // indirect github.com/pquerna/cachecontrol v0.2.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect ) diff --git a/go.sum b/go.sum index f7b4b73..b69b142 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,8 @@ github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0 github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= diff --git a/internal/middleware/logging.go b/internal/middleware/logging.go index 6ac6ca7..87d4a46 100644 --- a/internal/middleware/logging.go +++ b/internal/middleware/logging.go @@ -26,8 +26,11 @@ func Logging(next http.Handler) http.Handler { statusCode: http.StatusOK, } + // Get request ID from header (set by RequestID middleware) + requestID := r.Header.Get(RequestIDHeader) + next.ServeHTTP(wrapped, r) - log.Println(wrapped.statusCode, r.Method, r.URL.Path, time.Since(start)) + log.Printf("[%s] %d %s %s %v", requestID, wrapped.statusCode, r.Method, r.URL.Path, time.Since(start)) }) -} +} \ No newline at end of file diff --git a/internal/middleware/requestid.go b/internal/middleware/requestid.go new file mode 100644 index 0000000..2b39382 --- /dev/null +++ b/internal/middleware/requestid.go @@ -0,0 +1,50 @@ +package middleware + +import ( + "context" + "log" + "net/http" + + "github.com/google/uuid" +) + +// RequestIDKey is the context key for the request ID +type requestIDKey struct{} + +// RequestIDHeader is the header key for the request ID +const RequestIDHeader = "X-Request-ID" + +// RequestID middleware generates a unique ID for each request +func RequestID() Middleware { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Check if request already has an ID + requestID := r.Header.Get(RequestIDHeader) + if requestID == "" { + // Generate a new UUID if none exists + requestID = uuid.New().String() + } + + // Add the request ID to the response headers + w.Header().Set(RequestIDHeader, requestID) + + // Store the request ID in the request context + ctx := context.WithValue(r.Context(), requestIDKey{}, requestID) + + // Log the request with its ID + log.Printf("[%s] %s %s", requestID, r.Method, r.URL.Path) + + // Call the next handler with the updated context + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + +// GetRequestID retrieves the request ID from the context +func GetRequestID(ctx context.Context) string { + id, ok := ctx.Value(requestIDKey{}).(string) + if !ok { + return "" + } + return id +} \ No newline at end of file