Logger Middleware
The Logger middleware logs the start and end of each request, including useful data about the request, response status, processing time, and more.
Usage
Basic Usage
import ( "go-slim.dev/slim" "go-slim.dev/slim/middleware")func main() { s := slim.New() // Configure error handler to log errors s.ErrorHandler = func(c slim.Context, err error) error { middleware.LogEnd(c, err) // Ensure errors are logged return err } // Logger should come before Recovery s.Use(middleware.Logger()) s.Use(slim.Recovery()) s.GET("/", func(c slim.Context) error { return c.String(200, "Hello, World!") }) s.Start(":8080")}Custom Configuration
s := slim.New()s.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ TimeLayout: "2006-01-02 15:04:05", NewEntry: func(c slim.Context) middleware.LogEntry { return middleware.NewLogEntry(os.Stdout) },}))Configuration Options
LoggerConfig
type LoggerConfig struct { // TimeLayout defines the time format // Optional. Default value "2006/01/02 15:04:05.000" TimeLayout string // NewEntry creates a log entry for each request // Optional. Defaults to standard output NewEntry func(slim.Context) LogEntry}LogEntry Interface
type LogEntry interface { Begin(c slim.Context) map[string]any End(c slim.Context, err error) map[string]any SetTimeLayout(layout string) SetColorable(colorable bool) Print(c slim.Context, p LogPayload) Panic(v any, stack []byte)}LogPayload Structure
type LogPayload struct { StartTime time.Time // Request start time Proto string // HTTP protocol version RequestURI string // Request URI RawQuery string // Query string Method string // HTTP method RemoteAddr string // Client address Extra map[string]any // Extra data Error error // Error information StatusCode int // Response status code Written int // Response bytes Elapsed time.Duration // Processing time}Log Output Format
Default log output includes:
2006/01/02 15:04:05.000 [request-id] "GET /api/users HTTP/1.1" from 127.0.0.1:54321 - 200 1234B in 45msLog format components:
- Timestamp (configurable format)
- Request ID (if present)
- HTTP method and request path
- Client address
- Response status code (with color)
- Response size
- Processing time (with color)
Examples
1. Basic Logging
s := slim.New()s.ErrorHandler = func(c slim.Context, err error) error { middleware.LogEnd(c, err) return err}s.Use(middleware.Logger())s.GET("/api/users", func(c slim.Context) error { return c.JSON(200, map[string]string{"message": "success"})})Output example:
2024/11/08 10:30:15.123 "GET /api/users HTTP/1.1" from 127.0.0.1:54321 - 200 28B in 2.5ms2. Custom Time Format
s.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ TimeLayout: "2006-01-02 15:04:05",}))Output example:
2024-11-08 10:30:15 "GET /api/users HTTP/1.1" from 127.0.0.1:54321 - 200 28B in 2.5ms3. Custom Log Entry
type customLogEntry struct { *middleware.defaultLogEntry logger *log.Logger}func (c *customLogEntry) Begin(ctx slim.Context) map[string]any { // Log additional info at request start return map[string]any{ "user_agent": ctx.Request().UserAgent(), }}func (c *customLogEntry) End(ctx slim.Context, err error) map[string]any { // Log additional info at request end extra := make(map[string]any) if userID := ctx.Get("user_id"); userID != nil { extra["user_id"] = userID } return extra}s.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ NewEntry: func(c slim.Context) middleware.LogEntry { return &customLogEntry{ logger: log.New(os.Stdout, "", 0), } },}))4. Add Request ID
s.Use(middleware.RequestID()) // Add request ID middlewares.Use(middleware.Logger()) // Logger will automatically log request IDs.GET("/api/users", func(c slim.Context) error { return c.JSON(200, map[string]string{"message": "success"})})Output example:
2024/11/08 10:30:15.123 [abc123-def456] "GET /api/users HTTP/1.1" from 127.0.0.1:54321 - 200 28B in 2.5ms5. Log Error Information
s.ErrorHandler = func(c slim.Context, err error) error { middleware.LogEnd(c, err) // Log the error return c.JSON(500, map[string]string{"error": err.Error()})}s.Use(middleware.Logger())s.GET("/api/error", func(c slim.Context) error { return errors.New("something went wrong")})Output example:
2024/11/08 10:30:15.123 "GET /api/error HTTP/1.1" from 127.0.0.1:54321 - 500 35B in 1.2msSpot a mistake: something went wrong6. Log Additional Data
s.Use(middleware.Logger())s.GET("/api/users/:id", func(c slim.Context) error { // Add extra info to logs payload, _ := middleware.GetLogPayload(c) if payload.Extra == nil { payload.Extra = make(map[string]any) } payload.Extra["user_id"] = c.Param("id") middleware.ProvideLogPayload(c, payload) return c.JSON(200, map[string]string{"id": c.Param("id")})})Output example:
2024/11/08 10:30:15.123 "GET /api/users/123 HTTP/1.1" from 127.0.0.1:54321 - 200 15B in 3msAdditional data: { "user_id": "123" }Color Output
The Logger middleware automatically uses colored output in TTY environments:
-
Status code colors:
- 1xx (Informational): Blue
- 2xx (Success): Green
- 3xx (Redirection): Cyan
- 4xx (Client Error): Yellow
- 5xx (Server Error): Red
-
Elapsed time colors:
- < 500ms: Green
- 500ms - 5s: Yellow
-
5s: Red
How It Works
- Request Start: The
Loggermiddleware records start time and basic info inLogBegin() - Process Request: Calls the next handler to process the request
- Request End:
- If no error, calls
LogEnd(c, nil)in the middleware to log - If error, needs to call
LogEnd(c, err)in theErrorHandlerto log
- If no error, calls
- Output Log: The
Print()method formats and outputs the log
Middleware Order
Important: Logger should come before Recovery to ensure panics are properly logged:
s.ErrorHandler = func (c slim.Context, err error) error { middleware.LogEnd(c, err) // Log in error handler return err}s.Use(middleware.Logger()) // Logger firsts.Use(slim.Recovery()) // Recovery afterBest Practices
1. Configure Error Handler
s.ErrorHandler = func(c slim.Context, err error) error { middleware.LogEnd(c, err) // Return different responses based on error type if he, ok := err.(*slim.HTTPError); ok { return c.JSON(he.Code, he) } return c.JSON(500, map[string]string{"error": "Internal Server Error"})}2. Use Structured Logging
type structuredLogEntry struct { logger *slog.Logger}func (s *structuredLogEntry) Print(c slim.Context, p middleware.LogPayload) { s.logger.Info("request completed", slog.String("method", p.Method), slog.String("uri", p.RequestURI), slog.Int("status", p.StatusCode), slog.Duration("elapsed", p.Elapsed), slog.String("remote_addr", p.RemoteAddr), )}s.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{ NewEntry: func(c slim.Context) middleware.LogEntry { return &structuredLogEntry{ logger: slog.New(slog.NewJSONHandler(os.Stdout, nil)), } },}))3. Adjust Log Level by Environment
func newLogger() slim.MiddlewareFunc { if os.Getenv("ENV") == "production" { // Production: use structured logging return middleware.LoggerWithConfig(middleware.LoggerConfig{ NewEntry: func(c slim.Context) middleware.LogEntry { return newStructuredLogger() }, }) } // Development: use default colored logging return middleware.Logger()}s.Use(newLogger())4. Log Slow Requests
s.Use(middleware.Logger())s.Use(func(c slim.Context, next slim.HandlerFunc) error { start := time.Now() err := next(c) elapsed := time.Since(start) // Log slow requests (> 1 second) if elapsed > time.Second { log.Printf("SLOW REQUEST: %s %s took %v", c.Request().Method, c.Request().URL.Path, elapsed) } return err})Advanced Features
LogBegin and LogEnd
You can manually control logging:
// Manually log in handlers.GET("/api/custom", func(c slim.Context) error { // Begin logging middleware.LogBegin(c) // Process request result := doSomething() // End logging middleware.LogEnd(c, nil) return c.JSON(200, result)})Get and Set Log Data
s.Use(middleware.Logger())s.GET("/api/users", func(c slim.Context) error { // Get log payload payload, ok := middleware.GetLogPayload(c) if ok { // Add custom data if payload.Extra == nil { payload.Extra = make(map[string]any) } payload.Extra["custom_field"] = "custom_value" // Update log payload middleware.ProvideLogPayload(c, payload) } return c.JSON(200, map[string]string{"message": "success"})})Integration with Other Logging Libraries
Integration with slog
import ( "log/slog" "go-slim.dev/slim/middleware")type slogEntry struct { logger *slog.Logger}func (s *slogEntry) Begin(c slim.Context) map[string]any { return nil }func (s *slogEntry) End(c slim.Context, err error) map[string]any { return nil }func (s *slogEntry) SetTimeLayout(layout string) {}func (s *slogEntry) SetColorable(colorable bool) {}func (s *slogEntry) Print(c slim.Context, p middleware.LogPayload) { attrs := []slog.Attr{ slog.String("method", p.Method), slog.String("uri", p.RequestURI), slog.Int("status", p.StatusCode), slog.Duration("elapsed", p.Elapsed), } if p.Error != nil { s.logger.LogAttrs(context.Background(), slog.LevelError, "request failed", attrs...) } else { s.logger.LogAttrs(context.Background(), slog.LevelInfo, "request completed", attrs...) }}func (s *slogEntry) Panic(v any, stack []byte) { s.logger.Error("panic recovered", slog.Any("panic", v), slog.String("stack", string(stack)))}