Categorygithub.com/go-chi/httplog
repository
3.3.0
Repository: https://github.com/go-chi/httplog.git
Documentation: pkg.go.dev

# Packages

No description provided by the author

# README

httplog

Structured HTTP request logging middleware for Go, built on the standard library log/slog package

Go Reference Go Report Card MIT License

httplog is a lightweight, high-performance HTTP request logging middleware for Go web applications. Built on Go 1.21+'s standard log/slog package, it provides structured logging with zero external dependencies.

Features

  • 🚀 High Performance: Minimal overhead
  • 📋 Structured Logging: Built on Go's standard log/slog package
  • 🎯 Smart Log Levels: Auto-assigns levels by status code (5xx = error, 4xx = warn)
  • 📊 Schema Support: Compatible with ECS, OTEL, and GCP logging formats
  • 🛡️ Panic Recovery: Recovers panics with stack traces and HTTP 500 responses
  • 🔍 Body Logging: Conditional request/response body capture with content-type filtering
  • 📝 Custom Attributes: Add log attributes from handlers and middlewares
  • 🎨 Developer Friendly: Concise mode and curl command generation
  • 🔗 Router Agnostic: Works with Chi, Gin, Echo, and standard http.ServeMux

Usage

go get github.com/go-chi/httplog/v3@latest

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: httplog.SchemaECS,

		// 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"
}

Example

See _example/main.go and try it locally:

$ cd _example

# JSON logger (production)
$ go run .

# Pretty logger (localhost)
$ ENV=localhost go run .

License

MIT license