Files
8ac4281a42 feat(httpserver): skip configurable paths in request logs and traces (#648)
LoggerMiddleware and OTLPMiddleware now accept variadic Options to control
which request paths are excluded from logging and tracing. By default they
skip the infrastructure probe endpoints /_healthcheck and /_info, which
otherwise emit one log line and one span on every scrape.

- AppendIgnoredPaths(...) adds to the defaults (the common case).
- WithIgnoredPaths(...) replaces the default set entirely; WithIgnoredPaths()
  with no arguments logs and traces everything.

Tracing exclusion is delegated to otelchi's WithFilter so no span is even
started for ignored paths. Signatures stay backward-compatible (variadic),
so existing callers compile unchanged and pick up the quiet defaults for free.

Constraint: shared go-libs middleware — must stay source-compatible for all existing callers
Rejected: per-service skipPaths wrapper | duplicated across 3+ repos, drifts out of sync
Rejected: trace sampler dropping healthcheck spans | trace-only, would not silence logs
Confidence: high
Scope-risk: moderate
Directive: pass the SAME Option set to both middlewares in a service, or logs and spans diverge
Not-tested: prefix/wildcard path matching — exclusion is exact-path match only

Co-authored-by: fabrice guery <[email protected]>
2026-06-17 14:48:45 +00:00

63 lines
1.9 KiB
Go

package httpserver
// defaultIgnoredPaths lists the request paths that the logging and tracing
// middlewares skip by default. These are infrastructure probe endpoints that
// would otherwise emit one log line and one span on every scrape, drowning out
// real traffic.
//
// Extend the set with AppendIgnoredPaths, or replace it entirely with
// WithIgnoredPaths.
var defaultIgnoredPaths = []string{"/_healthcheck", "/_info"}
// Option configures the logging and tracing middlewares (LoggerMiddleware and
// OTLPMiddleware). Options are applied in the order they are given.
type Option func(*middlewareConfig)
type middlewareConfig struct {
ignoredPaths map[string]struct{}
}
func newMiddlewareConfig(opts ...Option) *middlewareConfig {
cfg := &middlewareConfig{ignoredPaths: pathSet(defaultIgnoredPaths)}
for _, opt := range opts {
opt(cfg)
}
return cfg
}
func pathSet(paths []string) map[string]struct{} {
set := make(map[string]struct{}, len(paths))
for _, p := range paths {
set[p] = struct{}{}
}
return set
}
func (c *middlewareConfig) isIgnored(path string) bool {
_, ok := c.ignoredPaths[path]
return ok
}
// AppendIgnoredPaths adds paths to the set the middlewares skip, on top of the
// defaults ("/_healthcheck", "/_info"). This is the option to reach for in
// almost all cases.
func AppendIgnoredPaths(paths ...string) Option {
return func(c *middlewareConfig) {
for _, p := range paths {
c.ignoredPaths[p] = struct{}{}
}
}
}
// WithIgnoredPaths replaces the entire ignore set, discarding the defaults —
// including "/_healthcheck" and "/_info". Pass them explicitly if you still
// want them skipped. Calling WithIgnoredPaths() with no arguments makes the
// middlewares log and trace every request.
//
// Most callers want AppendIgnoredPaths instead.
func WithIgnoredPaths(paths ...string) Option {
return func(c *middlewareConfig) {
c.ignoredPaths = pathSet(paths)
}
}