| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- package main
- import (
- "context"
- "log/slog"
- "strings"
- "sync"
- "github.com/slackhq/nebula/logging"
- )
- // newPlatformLogger returns a *slog.Logger that routes every log record
- // through the Windows service logger so records end up in the Windows
- // Event Log. All the heavy lifting (level management, format swap,
- // timestamp toggle, WithAttrs/WithGroup) comes from logging.NewHandler;
- // this file only contributes:
- //
- // - an io.Writer that forwards each formatted line to the service
- // logger at the current record's Event Log severity, and
- // - a thin severityTag that embeds *logging.Handler and overrides
- // only Handle / WithAttrs / WithGroup, so Event Viewer's severity
- // column and severity-based filters keep working the way they did
- // before the slog migration.
- //
- // Format (text vs json) is carried by the embedded *logging.Handler, so
- // logging.format: json in config still produces JSON lines in Event
- // Viewer, same as the pre-slog logrus setup.
- func newPlatformLogger() *slog.Logger {
- w := &eventLogWriter{}
- return slog.New(&severityTag{Handler: logging.NewHandler(w), w: w})
- }
- // eventLogWriter forwards slog-formatted lines to the Windows service
- // logger at the severity most recently stashed by severityTag.Handle.
- // The mutex serializes the stash + inner.Handle + Write cycle per record
- // across all concurrent goroutines; slog's builtin text/json handlers
- // each hold their own mutex around Write, but that only protects the
- // Write call itself, not our stash-then-handle sequence.
- type eventLogWriter struct {
- mu sync.Mutex
- level slog.Level
- }
- func (w *eventLogWriter) Write(p []byte) (int, error) {
- line := strings.TrimRight(string(p), "\n")
- switch {
- case w.level >= slog.LevelError:
- return len(p), logger.Error(line)
- case w.level >= slog.LevelWarn:
- return len(p), logger.Warning(line)
- default:
- return len(p), logger.Info(line)
- }
- }
- // severityTag embeds *logging.Handler to pick up everything it does for
- // free (Enabled, SetLevel, GetLevel, SetFormat, GetFormat,
- // SetDisableTimestamp) and overrides only Handle / WithAttrs / WithGroup
- // so each record's slog.Level is stashed on the writer before formatting
- // and so derived handlers stay wrapped as severityTag rather than
- // downgrading to bare *logging.Handler.
- type severityTag struct {
- *logging.Handler
- w *eventLogWriter
- }
- func (s *severityTag) Handle(ctx context.Context, r slog.Record) error {
- s.w.mu.Lock()
- defer s.w.mu.Unlock()
- s.w.level = r.Level
- return s.Handler.Handle(ctx, r)
- }
- func (s *severityTag) WithAttrs(attrs []slog.Attr) slog.Handler {
- if len(attrs) == 0 {
- return s
- }
- return &severityTag{Handler: s.Handler.WithAttrs(attrs).(*logging.Handler), w: s.w}
- }
- func (s *severityTag) WithGroup(name string) slog.Handler {
- if name == "" {
- return s
- }
- return &severityTag{Handler: s.Handler.WithGroup(name).(*logging.Handler), w: s.w}
- }
|