// Package http provides net/http middleware that injects a logger into the
// request context and emits an access log entry. Intended for Lambda-behind-
// ALB and for local development of handlers that also run outside Lambda.
//
// See mercemay.top/src/lambdalog/middleware/http/.
package http
import (
"context"
"net/http"
"sync/atomic"
)
// Logger is the narrow interface consumed by the middleware. It mirrors the
// other adapters so user code can share a single logger shim.
type Logger interface {
Info(msg string, fields ...Field)
Error(msg string, fields ...Field)
With(fields ...Field) Logger
}
// Field mirrors encoder.Field.
type Field struct {
Key string
Value any
}
type ctxKey struct{}
var loggerKey ctxKey
// WithLogger attaches l to ctx. Handlers downstream can read it with
// LoggerFrom.
func WithLogger(ctx context.Context, l Logger) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, loggerKey, l)
}
// LoggerFrom returns the logger attached by WithLogger, or fallback if none
// is attached.
func LoggerFrom(ctx context.Context, fallback Logger) Logger {
if ctx == nil {
return fallback
}
if l, ok := ctx.Value(loggerKey).(Logger); ok && l != nil {
return l
}
return fallback
}
// Chain composes multiple middleware. The returned function wraps the final
// handler with the first element of the chain outermost.
func Chain(mws ...func(http.Handler) http.Handler) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
for i := len(mws) - 1; i >= 0; i-- {
h = mws[i](h)
}
return h
}
}
// Counter is a simple atomic counter used by middleware for per-route tallies.
type Counter struct{ v atomic.Int64 }
// Inc increments the counter.
func (c *Counter) Inc() { c.v.Add(1) }
// Load returns the current value.
func (c *Counter) Load() int64 { return c.v.Load() }
// Reset sets the counter back to zero.
func (c *Counter) Reset() { c.v.Store(0) }
// InjectLogger returns a middleware that attaches l to each request.
func InjectLogger(l Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(WithLogger(r.Context(), l))
next.ServeHTTP(w, r)
})
}
}