feat(lib/logging): add slog filter handler, move slog init logic here

Signed-off-by: Xe Iaso <me@xeiaso.net>
This commit is contained in:
Xe Iaso
2025-08-05 02:11:14 +00:00
parent e0d2136ee6
commit 53516738c1
2 changed files with 91 additions and 0 deletions

67
lib/logging/filter.go Normal file
View File

@@ -0,0 +1,67 @@
package logging
import (
"context"
"log/slog"
)
// Filterer is the shape of any type that can perform log filtering. This takes
// the context of the log filtering call and the log record to be filtered.
type Filterer interface {
Filter(ctx context.Context, r slog.Record) bool
}
// FilterFunc lets you make inline log filters with plain functions.
type FilterFunc func(ctx context.Context, r *slog.Record) bool
// Filter implements Filterer for FilterFunc.
func (ff FilterFunc) Filter(ctx context.Context, r *slog.Record) bool {
return ff(ctx, r)
}
// FilterHandler wraps a slog Handler with one or more filters, enabling administrators
// to customize the logging subsystem of Anubis.
type FilterHandler struct {
next slog.Handler
filters []Filterer
}
// NewFilterHandler creates a new filtering handler with the given base handler and filters.
func NewFilterHandler(handler slog.Handler, filters ...Filterer) *FilterHandler {
return &FilterHandler{
next: handler,
filters: filters,
}
}
// Enabled passes through to the upstream slog Handler.
func (h *FilterHandler) Enabled(ctx context.Context, level slog.Level) bool {
return h.next.Enabled(ctx, level)
}
// Handle implements slog.Handler and applies all filters before delegating to the base handler.
func (h *FilterHandler) Handle(ctx context.Context, r slog.Record) error {
// Apply all filters - if any filter returns false, skip the log
for _, filter := range h.filters {
if !filter.Filter(ctx, r) {
return nil // Skip this log record
}
}
return h.next.Handle(ctx, r)
}
// WithAttrs implements slog.Handler.
func (h *FilterHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &FilterHandler{
next: h.next.WithAttrs(attrs),
filters: h.filters,
}
}
// WithGroup implements slog.Handler.
func (h *FilterHandler) WithGroup(name string) slog.Handler {
return &FilterHandler{
next: h.next.WithGroup(name),
filters: h.filters,
}
}

24
lib/logging/logging.go Normal file
View File

@@ -0,0 +1,24 @@
package logging
import (
"fmt"
"log/slog"
"os"
)
func Init(level string) slog.Handler {
var programLevel slog.Level
if err := (&programLevel).UnmarshalText([]byte(level)); err != nil {
fmt.Fprintf(os.Stderr, "invalid log level %s: %v, using info\n", level, err)
programLevel = slog.LevelInfo
}
leveler := &slog.LevelVar{}
leveler.Set(programLevel)
h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
AddSource: true,
Level: leveler,
})
return h
}