log: use sync map

This commit is contained in:
MariusVanDerWijden 2025-05-29 11:21:13 +02:00 committed by lightclient
parent 64773bf6bc
commit 4fb7b3a24e

View file

@ -21,7 +21,6 @@ import (
"errors"
"fmt"
"log/slog"
"maps"
"regexp"
"runtime"
"strconv"
@ -42,18 +41,17 @@ type GlogHandler struct {
level atomic.Int32 // Current log level, atomically accessible
override atomic.Bool // Flag whether overrides are used, atomically accessible
patterns []pattern // Current list of patterns to override with
siteCache map[uintptr]slog.Level // Cache of callsite pattern evaluations
location string // file:line location where to do a stackdump at
lock sync.RWMutex // Lock protecting the override pattern list
patterns []pattern // Current list of patterns to override with
siteCache sync.Map // Cache of callsite pattern evaluations, maps uintptr -> slog.Level
location string // file:line location where to do a stackdump at
lock sync.RWMutex // Lock protecting the override pattern list
}
// NewGlogHandler creates a new log handler with filtering functionality similar
// to Google's glog logger. The returned handler implements Handler.
func NewGlogHandler(h slog.Handler) *GlogHandler {
return &GlogHandler{
origin: h,
siteCache: make(map[uintptr]slog.Level),
origin: h,
}
}
@ -69,7 +67,7 @@ type pattern struct {
func (h *GlogHandler) Verbosity(level slog.Level) {
h.level.Store(int32(level))
// clear the cache to make sure the verbosity is applied correctly.
h.siteCache = make(map[uintptr]slog.Level)
h.siteCache.Clear()
}
// Vmodule sets the glog verbosity pattern.
@ -133,10 +131,9 @@ func (h *GlogHandler) Vmodule(ruleset string) error {
}
// Swap out the vmodule pattern for the new filter system
h.lock.Lock()
defer h.lock.Unlock()
h.patterns = filter
h.siteCache = make(map[uintptr]slog.Level)
h.lock.Unlock()
h.siteCache.Clear()
h.override.Store(len(filter) != 0)
return nil
@ -152,18 +149,15 @@ func (h *GlogHandler) Enabled(ctx context.Context, lvl slog.Level) bool {
// WithAttrs implements slog.Handler, returning a new Handler whose attributes
// consist of both the receiver's attributes and the arguments.
func (h *GlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
patterns := []pattern{}
h.lock.RLock()
siteCache := maps.Clone(h.siteCache)
patterns = append(patterns, h.patterns...)
h.lock.RUnlock()
patterns := []pattern{}
patterns = append(patterns, h.patterns...)
res := GlogHandler{
origin: h.origin.WithAttrs(attrs),
patterns: patterns,
siteCache: siteCache,
location: h.location,
origin: h.origin.WithAttrs(attrs),
patterns: patterns,
location: h.location,
}
res.level.Store(h.level.Load())
@ -183,30 +177,28 @@ func (h *GlogHandler) WithGroup(name string) slog.Handler {
// local and backtrace filters, finally emitting it if either allow it through.
func (h *GlogHandler) Handle(_ context.Context, r slog.Record) error {
// Check callsite cache for previously calculated log levels
h.lock.RLock()
lvl, ok := h.siteCache[r.PC]
h.lock.RUnlock()
lvl, ok := h.siteCache.Load(r.PC)
// If we didn't cache the callsite yet, calculate it
if !ok {
h.lock.Lock()
fs := runtime.CallersFrames([]uintptr{r.PC})
frame, _ := fs.Next()
h.lock.RLock()
for _, rule := range h.patterns {
if rule.pattern.MatchString(fmt.Sprintf("+%s", frame.File)) {
h.siteCache[r.PC], lvl, ok = rule.level, rule.level, true
h.siteCache.Store(r.PC, rule.level)
lvl, ok = rule.level, true
}
}
h.lock.RUnlock()
// If no rule matched, use the default log lvl
if !ok {
lvl = slog.Level(h.level.Load())
h.siteCache[r.PC] = lvl
h.siteCache.Store(r.PC, lvl)
}
h.lock.Unlock()
}
if lvl <= r.Level {
if lvl.(slog.Level) <= r.Level {
return h.origin.Handle(context.Background(), r)
}
return nil