diff --git a/app/common/telemetry.go b/app/common/telemetry.go index e53990a90..572153777 100644 --- a/app/common/telemetry.go +++ b/app/common/telemetry.go @@ -102,7 +102,10 @@ func NewLogger(conf config.LogTelemetryConfig, res *resource.Resource, loggerPro ).Handler(conf.NewHandler(os.Stdout)), // Otel logger - realotelslog.NewHandler(metadata.OpenTelemetryName, realotelslog.WithLoggerProvider(loggerProvider)), + NewLevelHandler( + realotelslog.NewHandler(metadata.OpenTelemetryName, realotelslog.WithLoggerProvider(loggerProvider)), + conf.Level, + ), )), ) } @@ -220,3 +223,37 @@ func NewTelemetryRouterHook(meterProvider metric.MeterProvider, tracerProvider t }) } } + +// Compile-time check LevelHandler implements slog.Handler. +var _ slog.Handler = (*LevelHandler)(nil) + +// NewLevelHandler returns a new LevelHandler. +func NewLevelHandler(handler slog.Handler, level slog.Leveler) *LevelHandler { + return &LevelHandler{ + handler: handler, + level: level, + } +} + +// LevelHandler is a slog.Handler that filters log records based on the log level. +type LevelHandler struct { + handler slog.Handler + level slog.Leveler +} + +func (h *LevelHandler) Enabled(ctx context.Context, level slog.Level) bool { + // The higher the level, the more important or severe the event. + return level >= h.level.Level() && h.handler.Enabled(ctx, level) +} + +func (h *LevelHandler) WithGroup(name string) slog.Handler { + return h.handler.WithGroup(name) +} + +func (h *LevelHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h.handler.WithAttrs(attrs) +} + +func (h *LevelHandler) Handle(ctx context.Context, record slog.Record) error { + return h.handler.Handle(ctx, record) +} diff --git a/app/common/telemetry_test.go b/app/common/telemetry_test.go new file mode 100644 index 000000000..b2ca77fd9 --- /dev/null +++ b/app/common/telemetry_test.go @@ -0,0 +1,46 @@ +package common + +import ( + "context" + "log/slog" + "testing" + + "github.com/stretchr/testify/mock" +) + +func TestLevelHandler(t *testing.T) { + mockHandler := &MockHandler{} + logger := slog.New(NewLevelHandler(mockHandler, slog.LevelInfo)) + + mockHandler.On("Enabled", mock.Anything, slog.LevelInfo).Return(true) + mockHandler.On("Enabled", mock.Anything, slog.LevelWarn).Return(true) + mockHandler.On("Enabled", mock.Anything, slog.LevelError).Return(true) + + logger.Debug("debug") + logger.Info("info") + logger.Warn("warn") + logger.Error("error") + + mockHandler.AssertExpectations(t) +} + +type MockHandler struct { + mock.Mock +} + +func (h *MockHandler) Enabled(ctx context.Context, level slog.Level) bool { + args := h.Called(ctx, level) + return args.Bool(0) +} + +func (h *MockHandler) WithGroup(name string) slog.Handler { + return h +} + +func (h *MockHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h +} + +func (h *MockHandler) Handle(ctx context.Context, record slog.Record) error { + return nil +}