### Production Setup with Custom Options Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Illustrates a production-ready setup for the httplog middleware, including custom options for logging level, content type filtering, and request ID generation. ```go package main import ( "net/http" "os" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog" ) func main() { router := chi.NewRouter() // Initialize the logger with production options log := httplog.NewLogger(httplog.WithLevel("info"), httplog.WithFormatter(httplog.NewGCPFormatter(os.Stdout)), httplog.WithPublicFieldFilter(httplog.DefaultPublicFieldFilter()), httplog.WithRequestIDGenerator(httplog.NewDefaultRequestIDGenerator())) router.Use(log.Handler(httplog.With()) .WithRequestID()) router.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, world!")) }) http.ListenAndServe(":8080", router) } ``` -------------------------------- ### Basic httplog Setup with Chi Router Source: https://github.com/go-chi/httplog/blob/master/README.md Demonstrates how to initialize httplog with a custom logger and configure its options, including log schema, panic recovery, and request skipping. This setup is suitable for production environments. ```go package main import ( "errors" "fmt" "log" "log/slog" "net/http" "os" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/httplog/v3" ) func main() { logFormat := httplog.SchemaECS.Concise(isLocalhost) logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: logFormat.ReplaceAttr, })).With( slog.String("app", "example-app"), slog.String("version", "v1.0.0-a1fa420"), slog.String("env", "production"), ) r := chi.NewRouter() // Request logger r.Use(httplog.RequestLogger(logger, &httplog.Options{ // Level defines the verbosity of the request logs: // slog.LevelDebug - log all responses (incl. OPTIONS) // slog.LevelInfo - log responses (excl. OPTIONS) // slog.LevelWarn - log 4xx and 5xx responses only (except for 429) // slog.LevelError - log 5xx responses only Level: slog.LevelInfo, // Set log output to Elastic Common Schema (ECS) format. Schema: logFormat, // RecoverPanics recovers from panics occurring in the underlying HTTP handlers // and middlewares. It returns HTTP 500 unless response status was already set. // // NOTE: Panics are logged as errors automatically, regardless of this setting. RecoverPanics: true, // Optionally, filter out some request logs. Skip: func(req *http.Request, respStatus int) bool { return respStatus == 404 || respStatus == 405 }, // Optionally, log selected request/response headers explicitly. LogRequestHeaders: []string{"Origin"}, LogResponseHeaders: []string{}, // Optionally, enable logging of request/response body based on custom conditions. // Useful for debugging payload issues in development. LogRequestBody: isDebugHeaderSet, LogResponseBody: isDebugHeaderSet, })) // Set request log attribute from within middleware. r.Use(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() httplog.SetAttrs(ctx, slog.String("user", "user1")) next.ServeHTTP(w, r.WithContext(ctx)) }) }) r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world \n")) }) http.ListenAndServe("localhost:8000", r) } func isDebugHeaderSet(r *http.Request) bool { return r.Header.Get("Debug") == "reveal-body-logs" } ``` -------------------------------- ### Minimal httplog Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Use this for a basic setup with all default options. ```go r.Use(httplog.RequestLogger(logger, nil)) // Uses all defaults ``` -------------------------------- ### Install httplog Package Source: https://github.com/go-chi/httplog/blob/master/_autodocs/00-START-HERE.txt Install the latest version of the httplog package using go get. ```bash go get github.com/go-chi/httplog/v3@latest ``` -------------------------------- ### Development Setup with Concise Logging in Go Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Configure httplog for development using a concise schema for development. This setup uses slog for structured logging and chi for routing. ```go package main import ( "log/slog" "net/http" "os" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog/v3" ) func main() { // Use concise schema for development sschema := httplog.SchemaECS.Concise(true) // For prettier output in development // In reality, you'd use a library like github.com/golang-cz/devslog handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: schema.ReplaceAttr, }) logger := slog.New(handler) r := chi.NewRouter() // More verbose logging for debugging r.Use(httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelDebug, Schema: schema, LogRequestBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogResponseBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogBodyMaxLen: -1, })) r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) }) http.ListenAndServe(":8000", r) } ``` -------------------------------- ### Basic Request Logging Setup Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/RequestLogger.md Demonstrates how to initialize and use the RequestLogger middleware with default options in a Chi router. ```go package main import ( "log/slog" "net/http" "os" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog/v3" ) func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) r := chi.NewRouter() r.Use(httplog.RequestLogger(logger, nil)) r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) http.ListenAndServe(":8000", r) } ``` -------------------------------- ### Example Usage: Running httplog Locally Source: https://github.com/go-chi/httplog/blob/master/README.md Provides commands to run the httplog example locally. Use the first command for a standard JSON logger in production, and the second command to enable a pretty logger when running on localhost. ```sh $ cd _example # JSON logger (production) $ go run . # Pretty logger (localhost) $ ENV=localhost go run . ``` -------------------------------- ### Concise Method Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Shows how to use the `Concise` method for a more compact log output format. This is useful for reducing log verbosity in certain environments. ```go package main import ( "os" "github.com/go-chi/httplog" "github.com/go-chi/httplog/httploggers" ) func main() { // Create a logger with concise output format log := httplog.NewLogger( httplog.WithFormatter(httploggers.NewConciseFormatter(os.Stdout)), ) // Use the logger middleware (example assumes router setup) // router.Use(log.Handler) } ``` -------------------------------- ### Minimal HTTP Request Logging Setup Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Set up basic HTTP request logging with a JSON handler writing to standard output. This is suitable for development environments. ```go package main import ( "log/slog" "net/http" "os" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog/v3" ) func main() { // Create a logger logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // Create router r := chi.NewRouter() // Add HTTP request logging middleware r.Use(httplog.RequestLogger(logger, nil)) // Add your routes r.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello world")) }) http.ListenAndServe(":8000", r) } ``` -------------------------------- ### Basic Chi Router Integration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Demonstrates the basic integration of the httplog middleware with a Chi router. This setup is suitable for initial project configuration. ```go package main import ( "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog" ) func main() { router := chi.NewRouter() // Initialize the logger middleware logger := httplog.NewLogger() // Use the logger middleware router.Use(logger.Handler) // Define routes router.Get("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, world!")) }) // Start the server http.ListenAndServe(":8080", router) } ``` -------------------------------- ### Per-Route Configuration Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Shows how to apply different logging configurations to specific routes within the same router. This allows for tailored logging based on route sensitivity or importance. ```go package main import ( "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog" ) func main() { router := chi.NewRouter() // Default logger configuration defaultLogger := httplog.NewLogger() // Sensitive route logger configuration (e.g., disable body logging) ssensitiveLogger := httplog.NewLogger(httplog.WithNoBody()) router.Use(defaultLogger.Handler) // Apply default logger to all routes router.Route("/sensitive", func() { router.Use(sensitiveLogger.Handler) // Apply sensitive logger to this group router.Post("/data", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Sensitive data processed")) }) }) http.ListenAndServe(":8080", router) } ``` -------------------------------- ### Standard Library HTTP Server Integration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Shows how to use the httplog middleware with Go's standard library `net/http` package. This is a fundamental integration example. ```go package main import ( "net/http" "github.com/go-chi/httplog" ) func main() { // Initialize the logger middleware logger := httplog.NewLogger() http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Hello, world!")) }) // Wrap the handler with the logger middleware http.ListenAndServe(":8080", logger.Handler(nil, http.DefaultServeMux)) } ``` -------------------------------- ### Echo Router Integration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Demonstrates integrating the httplog middleware with an Echo web framework router. This example is useful for users of the Echo framework. ```go package main import ( "net/http" "github.com/labstack/echo/v4" "github.com/go-chi/httplog" ) func main() { router := echo.New() // Initialize the logger middleware logger := httplog.NewLogger() // Use the logger middleware with Echo router.Use(func(c echo.Context) error { logger.Handler(c.Response(), c.Request()) return nil }) router.GET("/", func(c echo.Context) error { return c.String(http.StatusOK, "Hello, world!") }) router.Start(":8080") } ``` -------------------------------- ### Gin Router Integration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Shows how to integrate the httplog middleware with a Gin web framework router. This example highlights adapting the middleware for a different routing library. ```go package main import ( "net/http" "github.com/gin-gonic/gin" "github.com/go-chi/httplog" ) func main() { router := gin.Default() // Initialize the logger middleware logger := httplog.NewLogger() // Use the logger middleware with Gin router.Use(func(c *gin.Context) { logger.Handler(c.Writer, c.Request) c.Next() }) router.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "Hello, world!") }) router.Run(":8080") } ``` -------------------------------- ### Using Concise Schema Based on Environment Source: https://github.com/go-chi/httplog/blob/master/_autodocs/types.md This example demonstrates conditionally applying a concise schema based on an environment variable, reducing log verbosity in local development. ```go isLocalhost := os.Getenv("ENV") == "localhost" logFormat := httplog.SchemaECS.Concise(isLocalhost) // For localhost: uses concise schema (less verbose) // For production: uses full schema with all fields ``` -------------------------------- ### Schema Definition and Methods Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Documentation for schema types, including all 24 fields, and the ReplaceAttr, Concise methods with their signatures, behavior, and examples. Also covers pre-defined schemas (ECS, OTEL, GCP), field name comparison, and custom schema examples. ```APIDOC ## Schema Documentation ### Type Definition - All 24 fields documented. ### Methods #### ReplaceAttr ##### Signature `ReplaceAttr(key string, value interface{})` ##### Behavior Replaces an existing attribute or adds a new one if it does not exist. ##### Examples ```go // Example usage of ReplaceAttr log.ReplaceAttr("user_id", 12345) ``` #### Concise ##### Signature `Concise(enabled bool)` ##### Behavior Enables or disables concise logging format. ##### Examples ```go // Example usage of Concise log.Concise(true) ``` ### Pre-defined Schemas - ECS (Elastic Common Schema) - OTEL (OpenTelemetry) - GCP (Google Cloud Platform) ### Field Name Comparison Table - Compares field names across different schema types. ### Custom Schema Examples - Demonstrates how to define and use custom schemas. ``` -------------------------------- ### Production ECS Log Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Demonstrates a typical production log entry in ECS format for a successful API request. ```bash $ curl http://localhost:8000/api/users ``` ```json { "@timestamp": "2024-01-01T12:34:56Z", "log.level": "INFO", "message": "POST /api/users => HTTP 201 (45ms)", "http.request.method": "POST", "url.full": "http://localhost:8000/api/users", "client.ip": "127.0.0.1", "http.response.status_code": 201, "event.duration": 45000000, "http.response.body.bytes": 128, "action": "create_user" } ``` -------------------------------- ### Development with Concise Output Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Schema.md This example demonstrates how to conditionally use a concise schema for local development and a full schema for production. It configures a slog logger with the appropriate schema and applies it to the request logger. ```go isLocalhost := os.Getenv("ENV") == "localhost" // Full schema for production // Concise schema for localhost development logFormat := httplog.SchemaECS.Concise(isLocalhost) handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: logFormat.ReplaceAttr, }) logger := slog.New(handler) r.Use(httplog.RequestLogger(logger, &httplog.Options{ Schema: logFormat, })) ``` ```json { "@timestamp": "2024-01-01T12:34:56Z", "log.level": "INFO", "message": "GET /api/users => HTTP 200 (45ms)", "http.request.method": "GET", "url.full": "http://localhost:8000/api/users", "url.path": "/api/users", "client.ip": "127.0.0.1", "url.domain": "localhost:8000", "url.scheme": "http", "http.version": "HTTP/1.1", "user_agent.original": "curl/7.64.1", "http.request.headers": {"Content-Type": "application/json"}, "http.request.body.bytes": 0, "http.response.status_code": 200, "event.duration": 45000000, "http.response.body.bytes": 256 } ``` ```json { "message": "GET /api/users => HTTP 200 (45ms)", "http.response.headers": {"Content-Type": "application/json"}, "http.response.body.bytes": 256 } ``` -------------------------------- ### Production HTTP Request Logging Setup Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Configure HTTP request logging for production with ECS schema, custom attributes, and skipping 404/405 status codes. This setup provides more structured and filtered logs. ```go package main import ( "log/slog" "net/http" "os" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog/v3" ) func main() { // Create logger with ECS schema schema := httplog.SchemaECS handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: schema.ReplaceAttr, }) logger := slog.New(handler).With( slog.String("app", "myapp"), slog.String("env", "production"), ) // Create router r := chi.NewRouter() // Add logging with production config r.Use(httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelInfo, Schema: schema, Skip: func(req *http.Request, status int) bool { return status == 404 || status == 405 }, })) r.Get("/api/users", handleGetUsers) r.Post("/api/users", handleCreateUser) http.ListenAndServe(":8000", r) } func handleGetUsers(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`[{"id":1,"name":"John"}]`)) } func handleCreateUser(w http.ResponseWriter, r *http.Request) { // Add custom attributes to the request log httplog.SetAttrs(r.Context(), slog.String("action", "create_user")) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) w.Write([]byte(`{"id":2,"name":"Jane"}`)) } ``` -------------------------------- ### Error Rate Monitoring Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Illustrates how to implement error rate monitoring using the httplog middleware. This involves tracking the number of non-2xx/3xx responses. ```go package main import ( "net/http" "sync/atomic" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog" ) var errorCount int64 func main() { router := chi.NewRouter() log := httplog.NewLogger() router.Use(log.Handler, func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wrappedWriter := httplog.NewResponseLogger(w, log.Logger, log.LogStatus) // Assuming LogStatus is accessible or similar logic next.ServeHTTP(wrappedWriter, r) if wrappedWriter.Status() < 200 || wrappedWriter.Status() >= 400 { atomic.AddInt64(&errorCount, 1) } }) }) router.Get("/", func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Internal Server Error", http.StatusInternalServerError) }) http.ListenAndServe(":8080", router) } ``` -------------------------------- ### PII Filtering Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Demonstrates how to filter Personally Identifiable Information (PII) from log output using the `WithPublicFieldFilter` option. This is crucial for privacy compliance. ```go package main import ( "os" "github.com/go-chi/httplog" "github.com/go-chi/httplog/httploggers" ) func main() { // Define a filter that excludes sensitive fields like 'user.email' publicFilter := httploggers.DefaultPublicFieldFilter().Exclude("user.email") log := httplog.NewLogger( httplog.WithFormatter(httploggers.NewJSONFormatter(os.Stdout, httploggers.JSONFormatterOptions{}) .WithPublicFieldFilter(publicFilter)), ) // Use the logger middleware (example assumes router setup) // router.Use(log.Handler) } ``` -------------------------------- ### Initialize JSON Handler with Schema.ReplaceAttr Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Schema.md Demonstrates how to use Schema.ReplaceAttr with slog.NewJSONHandler to customize log output. This example shows how standard keys like slog.TimeKey and slog.LevelKey are transformed. ```go schema := httplog.SchemaECS handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: schema.ReplaceAttr, AddSource: true, }) logger := slog.New(handler) // When logger outputs attributes, slog.TimeKey becomes "@timestamp", // slog.LevelKey becomes "log.level", etc. logger.Info("request processed", slog.String("user_id", "123"), slog.Any("error", fmt.Errorf("invalid data")), ) // Output: {"@timestamp":"2024-01-01T...", "log.level":"INFO", "message":"request processed", "user_id":"123", "error.message":"invalid data"} ``` -------------------------------- ### Conditional Body Logging Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Demonstrates how to conditionally log the request and response bodies based on specific criteria, such as content type or route. This helps manage log size and sensitivity. ```go package main import ( "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/httplog" ) func main() { router := chi.NewRouter() // Configure logger to only log bodies for JSON content types log := httplog.NewLogger(httplog.WithBodyAllCrossOrigin()) router.Use(log.Handler) router.Post("/data", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Data received")) }) http.ListenAndServe(":8080", router) } ``` -------------------------------- ### Options Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Detailed documentation for all 11 fields of the Options type, including default values, complete configuration examples, log levels, content-type filtering, best practices, and field initialization rules. ```APIDOC ## Options Configuration ### Type Definition - All 11 fields documented in detail. ### Default Options Reference - Lists the default values for all configuration options. ### Complete Configuration Examples - Provides comprehensive examples of how to configure the logger. ### Log Levels Documentation - Explains the different log levels (DEBUG, INFO, WARN, ERROR) and their usage. ### Content-Type Filtering Guide - Details how to filter logs based on the Content-Type header. ### Best Practices - Offers recommendations for optimal logger configuration and usage. ### Field Initialization Rules - Describes the rules governing the initialization of logger fields. ``` -------------------------------- ### Selectively Log Headers Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Be selective about which request and response headers are logged. This example only logs the 'Content-Type' header. ```go LogRequestHeaders: []string{"Content-Type"} LogResponseHeaders: []string{} ``` -------------------------------- ### Using ReplaceAttr with slog.HandlerOptions Source: https://github.com/go-chi/httplog/blob/master/_autodocs/types.md This example shows how to integrate the httplog schema's ReplaceAttr function into slog.HandlerOptions for custom log formatting. ```go handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: httplog.SchemaECS.ReplaceAttr, }) ``` -------------------------------- ### Router and Platform Integration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Guides for integrating the HTTPLog middleware with various routers (Chi, stdlib, Gin, Echo) and logging platforms (ES/ECS, GCP, OTEL). Covers middleware composition, advanced configuration, per-route configuration, conditional body logging, and more. ```APIDOC ## Integration Patterns ### Router Integration - **Chi**: `httplog.ChiLogger(log).Handler() - **stdlib**: `httplog.ServerHTTP(log, next) - **Gin**: `r.Use(httplog.GinLogger(log)) - **Echo**: `e.Use(httplog.EchoLogger(log)) ### Logging Platform Integration - **ES/ECS**: Configure logger to output logs in Elastic Common Schema format. - **GCP**: Configure logger for Google Cloud Platform logging. - **OTEL**: Integrate with OpenTelemetry for distributed tracing and metrics. ### Middleware Composition Patterns - Demonstrates how to combine HTTPLog middleware with other middleware. ### Advanced Configuration - Covers complex configuration scenarios. ### Per-Route Configuration - Shows how to apply specific configurations to individual routes. ### Conditional Body Logging - Explains how to log request/response bodies conditionally. ### Unit Testing Patterns - Provides strategies for unit testing the logger middleware. ### Error Rate Monitoring - Discusses how to monitor error rates using the logger. ### Security Considerations - Highlights security aspects related to logging sensitive data. ### PII Filtering - Details methods for filtering Personally Identifiable Information (PII). ``` -------------------------------- ### Client Abort Log Output Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/errors.md Illustrates the JSON log output when a client abort is detected, showing HTTP status 0 and specific error fields. ```json { "message": "GET /api/slow => HTTP 0 (5s)", "error": "request aborted: client disconnected before response was sent", "error_type": "ClientAborted", "http.response.status_code": 0 } ``` -------------------------------- ### ReplaceAttr Method Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt Demonstrates the usage of the `ReplaceAttr` method to modify or replace attributes in log entries. This allows for fine-grained control over log output. ```go package main import ( "os" "github.com/go-chi/httplog" "github.com/go-chi/httplog/httploggers" ) func main() { // Create a logger with a custom attribute replacer log := httplog.NewLogger( httplog.WithFormatter(httploggers.NewJSONFormatter(os.Stdout, httploggers.JSONFormatterOptions{}) .ReplaceAttr(func(key string, val zapcore.Value) string { if key == "remote_addr" { return "client_ip" } return key })), ) // Use the logger middleware (example assumes router setup) // router.Use(log.Handler) } ``` -------------------------------- ### Error Response Sample Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt An example of a log entry corresponding to an error response (e.g., HTTP 500). This highlights how error details are captured. ```json { "remote_addr": "192.168.1.101", "request_id": "ghijkl67890", "remote_user": "-", "time": "2023-10-27T10:05:00Z", "request": "POST /api/data HTTP/1.1", "status": 500, "bytes_sent": 50, "duration": "200ms", "error": "internal server error" } ``` -------------------------------- ### Integration with Tracing and CURL Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/CURL.md This example integrates CURL command generation into tracing context for validation errors. It sets attributes like the validation error message and the CURL command, which can be useful for debugging in distributed tracing systems. ```go r.Post("/api/submit", func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() body := readBody(r) if err := validatePayload(body); err != nil { curlCmd := httplog.CURL(r, string(body)) httplog.SetAttrs(ctx, slog.String("validation_error", err.Error()), slog.String("curl_command", curlCmd), ) w.WriteHeader(http.StatusUnprocessableEntity) return } // ... success path }) ``` -------------------------------- ### Control Request Body Logging Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Use predicates to control when the request body is logged. This example logs the body only when an 'X-Debug' header is explicitly set to 'true'. ```go LogRequestBody: func(req *http.Request) bool { // Only log body on explicit request return req.Header.Get("X-Debug") == "true" } ``` -------------------------------- ### Field Name Comparison Table Example Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt This snippet is a placeholder for demonstrating a field name comparison table, typically used to map different logging schema fields. Actual code would involve defining and comparing field names. ```markdown | Field Name (httplog) | Field Name (ECS) | Field Name (OTEL) | Field Name (GCP) | |---|---|---|---| | `remote_addr` | `source.ip` | `source.ip` | `remote_ip` | | `request_id` | `trace.id` | `trace.id` | `httpRequest.traceId` | | `status` | `http.response.status_code` | `http.status_code` | `httpRequest.status` | ``` -------------------------------- ### Manual Debugging with CURL Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/CURL.md This example shows how to generate a CURL command for manual debugging when JSON unmarshalling fails. It captures the request body and uses httplog.CURL to create a reproducible command, logging it with an error message. ```go r.Post("/api/process", func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var payload map[string]interface{} body := readBody(r) if err := json.Unmarshal(body, &payload); err != nil { // Log curl command for manual debugging curlCmd := httplog.CURL(r, string(body)) logger.Error("invalid JSON", slog.String("curl", curlCmd)) w.WriteHeader(http.StatusBadRequest) return } // ... continue processing }) ``` -------------------------------- ### Per-Route Logging Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/INTEGRATION.md Configure different logging levels and attributes for distinct route groups within a Chi router. This example shows global middleware and route-specific configurations. ```go func setupRouter(logger *slog.Logger) *chi.Mux { r := chi.NewRouter() // Global middleware r.Use(httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelInfo, })) // Public routes (verbose logging) r.Route("/public", func(r chi.Router) { r.Get("/", handlePublic) }) // API routes (moderate logging) r.Route("/api", func(r chi.Router) { // Could add additional middleware per group if needed r.Post("/submit", handleAPISubmit) }) // Admin routes (detailed logging) r.Route("/admin", func(r chi.Router) { r.Use(adminLogMiddleware(logger)) r.Get("/dashboard", handleAdminDashboard) }) return r } func adminLogMiddleware(logger *slog.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() httplog.SetAttrs(ctx, slog.String("access_level", "admin")) next.ServeHTTP(w, r.WithContext(ctx)) }) } } ``` -------------------------------- ### Conditional Body Logging Examples Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Demonstrates how to conditionally log request and response bodies. Request bodies are logged when 'X-Log-Body: true' is set, and response bodies are logged when 'X-Debug-Mode: true' is set (note: response status is not available in this predicate). ```go // Log request body only when explicitly requested LogRequestBody: func(req *http.Request) bool { return req.Header.Get("X-Log-Body") == "true" }, // Log response body on errors only LogResponseBody: func(req *http.Request) bool { // Note: response status is not available in this predicate // Use LogExtraAttrs for status-based body logging return req.Header.Get("X-Debug-Mode") == "true" }, ``` -------------------------------- ### Multi-environment Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Schema.md This function dynamically selects the appropriate logging schema based on the environment variable 'ENV'. It returns a concise schema for 'development' and the default schema otherwise. The example shows how to use this function to configure a slog logger. ```go func getLogSchema(env string) *httplog.Schema { schema := httplog.SchemaECS if env == "development" { schema = schema.Concise(true) } return schema } // Usage schema := getLogSchema(os.Getenv("ENV")) logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ ReplaceAttr: schema.ReplaceAttr, })) ``` -------------------------------- ### Generate Curl Command for GET Request Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/CURL.md This snippet shows how to generate a curl command for a simple GET request. The CURL function automatically handles the URL and omits the method flag for GET requests. ```bash curl 'https://api.example.com/users/123' ``` -------------------------------- ### Minimal Options Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/types.md Sets the minimum log level for request logs. Use this for basic configuration. ```go // Minimal configuration opts := &httplog.Options{ Level: slog.LevelInfo, } ``` -------------------------------- ### Initialize and Use Request Logger Source: https://github.com/go-chi/httplog/blob/master/_autodocs/00-START-HERE.txt Import the httplog package and use the RequestLogger middleware with a configured slog logger. ```go import "github.com/go-chi/httplog/v3" logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) r.Use(httplog.RequestLogger(logger, nil)) ``` -------------------------------- ### Chain Multiple Middlewares Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Demonstrates how to chain multiple middlewares in a Chi router, including custom log, authentication, and recovery middlewares. ```go r := chi.NewRouter() // Chain multiple middlewares r.Use(logMiddleware(logger)) r.Use(authMiddleware) r.Use(recoveryMiddleware) func logMiddleware(logger *slog.Logger) func(http.Handler) http.Handler { return httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelInfo, }) } func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Extract user from context user := extractUser(r) if user != nil { httplog.SetAttrs(r.Context(), slog.String("user_id", user.ID), slog.String("user_role", user.Role), ) } next.ServeHTTP(w, r) }) } func recoveryMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if rec := recover(); rec != nil { logger.Error("panic recovered", slog.Any("error", rec)) w.WriteHeader(http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) } ``` -------------------------------- ### Switch Schemas at Runtime Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Schema.md Dynamically select a logging schema based on an environment variable. This allows for flexible logging configurations in different environments. ```go func configureLogging(platform string) *httplog.Schema { switch platform { case "elasticsearch": return httplog.SchemaECS case "otel": return httplog.SchemaOTEL case "gcp": return httplog.SchemaGCP default: return httplog.SchemaECS } } schema := configureLogging(os.Getenv("LOG_PLATFORM")) r.Use(httplog.RequestLogger(logger, &httplog.Options{ Schema: schema, })) ``` -------------------------------- ### Development httplog Configuration (Concise Logging) Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Sets up logging for development with DEBUG level, concise ECS schema, panic recovery, and conditional logging for request/response bodies based on a 'Debug' header. It also logs specific headers and sets a maximum body length. ```go r.Use(httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelDebug, Schema: httplog.SchemaECS.Concise(true), RecoverPanics: true, LogRequestHeaders: []string{"Content-Type", "Authorization"}, LogResponseHeaders: []string{"Content-Type"}, LogRequestBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogResponseBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogBodyMaxLen: -1, })) ``` -------------------------------- ### Unit Testing Handlers with httplog Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MODULE.md Demonstrates how to unit test handlers wrapped with the httplog.RequestLogger middleware. It shows setting up a test logger to capture log records and applying the middleware to a handler. ```go logCalls := []*slog.Record{} handler := slog.NewJSONHandler(io.Discard, nil) // Test handler with middleware req := httptest.NewRequest("GET", "/test", nil) w := httptest.NewRecorder() middleware := httplog.RequestLogger(logger, opts) middleware(yourHandler).ServeHTTP(w, req) ``` -------------------------------- ### Different Schema Format Sample (GCP) Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MANIFEST.txt A sample log entry formatted for Google Cloud Platform (GCP) logging. This demonstrates the structure expected by GCP services. ```json { "httpRequest": { "requestMethod": "GET", "requestUrl": "/resource", "status": 200, "responseSize": 1024, "userAgent": "Mozilla/5.0", "remoteIp": "192.168.1.100", "latency": "0.150s" }, "severity": "INFO", "message": "GET /resource HTTP/1.1", "logging.googleapis.com/trace": "abcdef12345", "timestamp": "2023-10-27T10:00:00.000Z" } ``` -------------------------------- ### Conditional CURL Logging with Extra Attributes Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Use LogExtraAttrs for conditional curl logging. This example adds a 'curl' attribute with the CURL command only when the status code is 400 or greater. ```go LogExtraAttrs: func(req *http.Request, body string, status int) []slog.Attr { if status >= 400 { return []slog.Attr{slog.String("curl", httplog.CURL(req, body))} } return nil } ``` -------------------------------- ### Handling Multiple Cancellation Sources in Go Source: https://github.com/go-chi/httplog/blob/master/_autodocs/errors.md Demonstrates handling both server-initiated timeouts and client-initiated aborts within a Go HTTP handler using context and error checking. ```go r.Post("/process", func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second) defer cancel() // Process with timeout result, err := processWithContext(ctx) if errors.Is(err, context.DeadlineExceeded) { // Handler timeout (server-initiated) httplog.SetAttrs(r.Context(), slog.String("error_source", "timeout")) w.WriteHeader(http.StatusRequestTimeout) return } if errors.Is(err, context.Canceled) && errors.Is(r.Context().Err(), context.Canceled) { // Client abort (client-initiated) httplog.SetAttrs(r.Context(), slog.String("error_source", "client_abort")) // Middleware will log ErrClientAborted return } // ... success path }) ``` -------------------------------- ### Complex Request Skipping Logic Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Options.md Implement advanced filtering in the Skip predicate to exclude OPTIONS requests, 204/404 status codes, and internal API paths starting with '/__'. ```go Skip: func(req *http.Request, status int) bool { // Skip OPTIONS, 204, 404 if req.Method == "OPTIONS" || status == 204 || status == 404 { return true } // Skip internal endpoints if strings.HasPrefix(req.URL.Path, "/__") { return true } return false } ``` -------------------------------- ### Options Struct Definition Source: https://github.com/go-chi/httplog/blob/master/_autodocs/types.md Defines all configuration parameters for the RequestLogger middleware. ```go type Options struct { Level slog.Level Schema *Schema RecoverPanics bool Skip func(req *http.Request, respStatus int) bool LogRequestHeaders []string LogRequestBody func(req *http.Request) bool LogResponseHeaders []string LogResponseBody func(req *http.Request) bool LogBodyContentTypes []string LogBodyMaxLen int LogExtraAttrs func(req *http.Request, reqBody string, respStatus int) []slog.Attr } ``` -------------------------------- ### Configure Body Logging Length and Content Types Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Illustrates how to configure the maximum length of request/response bodies to log and filter which content types are logged to manage memory usage with large bodies. ```go LogBodyMaxLen: 1024, // Truncate large bodies LogBodyContentTypes: ["application/json"], // Filter binary types ``` -------------------------------- ### Error Response Log Example with Curl Source: https://github.com/go-chi/httplog/blob/master/_autodocs/QUICK-START.md Shows how an error response, specifically a 400 Bad Request due to invalid content type, is logged. Includes the curl command used to trigger the error. ```bash $ curl http://localhost:8000/api/users -H "Content-Type: text/plain" -d "invalid" ``` ```json { "@timestamp": "2024-01-01T12:34:57Z", "log.level": "WARN", "message": "POST /api/users => HTTP 400 (12ms)", "http.request.method": "POST", "url.full": "http://localhost:8000/api/users", "http.response.status_code": 400, "curl": "curl -X POST 'http://localhost:8000/api/users' --data-raw 'invalid' -H 'Content-Type: text/plain'", "error.message": "invalid Content-Type" } ``` -------------------------------- ### Log Application Errors with SetError Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/ContextFunctions.md Use SetError to log errors encountered during application logic, such as JSON decoding or data validation. This example demonstrates setting the error and appropriate HTTP status codes. ```go r.Post("/api/users", func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() var user User if err := json.NewDecoder(r.Body).Decode(&user); err != nil { httplog.SetError(ctx, fmt.Errorf("invalid JSON: %w", err)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Invalid JSON")) return } if err := validateUser(user); err != nil { httplog.SetError(ctx, fmt.Errorf("validation failed: %w", err)) w.WriteHeader(http.StatusUnprocessableEntity) w.Write([]byte("Validation failed")) return } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(user) }) ``` -------------------------------- ### Production Logging Options Source: https://github.com/go-chi/httplog/blob/master/_autodocs/MODULE.md Configure the logger for production environments with a warning level and a specific schema. Define which request and response headers to log, and skip logging for 404 and 405 status codes. ```go opts := &httplog.Options{ Level: slog.LevelWarn, Schema: httplog.SchemaECS, Skip: func(r *http.Request, status int) bool { return status == 404 || status == 405 }, LogRequestHeaders: []string{"Content-Type"}, LogResponseHeaders: []string{}, } ``` -------------------------------- ### Conditional Body Logging with Caching Source: https://github.com/go-chi/httplog/blob/master/_autodocs/INTEGRATION.md Implement conditional logging of request bodies and other attributes based on the response status code. This example logs the request body for 4xx/5xx errors and the curl command for 5xx errors. ```go func setupRouter(logger *slog.Logger) *chi.Mux { r := chi.NewRouter() r.Use(httplog.RequestLogger(logger, &httplog.Options{ Level: slog.LevelInfo, LogExtraAttrs: func(req *http.Request, body string, status int) []slog.Attr { var attrs []slog.Attr // Log body for 4xx/5xx errors if status >= 400 && len(body) > 0 { attrs = append(attrs, slog.String("request_body", body)) } // Log curl command for debugging if status >= 500 { req.Header.Del("Authorization") attrs = append(attrs, slog.String("curl", httplog.CURL(req, body))) } return attrs }, })) return r } ``` -------------------------------- ### Minimal httplog Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Options.md Applies the default httplog options by passing nil to the RequestLogger. Useful for basic logging without custom configuration. ```go r.Use(httplog.RequestLogger(logger, nil)) ``` -------------------------------- ### Sanitize Headers and Generate CURL Command Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/CURL.md Remove sensitive headers like Authorization and Cookie before generating the cURL command to prevent logging secrets. This example demonstrates the safe pattern for preparing a request for cURL generation. ```go // Safe pattern: remove sensitive headers before curl generation req.Header.Del("Authorization") req.Header.Del("Cookie") req.Header.Del("X-API-Key") req.Header.Del("Signature") curlCmd := httplog.CURL(req, reqBody) logger.Info("request details", slog.String("curl", curlCmd)) ``` -------------------------------- ### Full Options Configuration Source: https://github.com/go-chi/httplog/blob/master/_autodocs/types.md Provides comprehensive configuration for the RequestLogger middleware, including custom skip functions, conditional logging, and extra attributes. ```go // Full configuration opts := &httplog.Options{ Level: slog.LevelDebug, Schema: httplog.SchemaECS.Concise(true), RecoverPanics: true, Skip: func(req *http.Request, status int) bool { return status == 404 || status == 405 }, LogRequestHeaders: []string{"Authorization", "X-Request-ID"}, LogRequestBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogResponseHeaders: []string{"Content-Type", "X-Response-Time"}, LogResponseBody: func(req *http.Request) bool { return req.Header.Get("Debug") == "true" }, LogBodyContentTypes: []string{"application/json", "text/plain"}, LogBodyMaxLen: 2048, LogExtraAttrs: func(req *http.Request, body string, status int) []slog.Attr { if status >= 400 { return []slog.Attr{slog.String("curl", httplog.CURL(req, body))} } return nil }, } ``` -------------------------------- ### Handle Source Location with GroupDelimiter Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Schema.md Demonstrates creating a nested structure for source location attributes when a GroupDelimiter is used. This organizes source information under a specific group key. ```go return slog.Group(grp, slog.String(file, source.File), slog.Int(line, source.Line), slog.String(fn, source.Function), ) // Creates: {"logging.googleapis.com/sourceLocation": {"file": "...", "line": 42, ...}} ``` -------------------------------- ### Configure Concise Schema for Development Source: https://github.com/go-chi/httplog/blob/master/_autodocs/api-reference/Options.md Set the Schema field to httplog.SchemaECS.Concise(true) for a more concise logging output during development. This reduces verbosity while maintaining structure. ```go opts.Schema = httplog.SchemaECS.Concise(true) ``` -------------------------------- ### Control Log Volume with Levels Source: https://github.com/go-chi/httplog/blob/master/_autodocs/configuration.md Use appropriate log levels to control the volume of logs in production. Setting the level to Warn reduces the verbosity. ```go Level: slog.LevelWarn // Reduce production log volume ```