diff --git a/otelslog/otel_handler.go b/otelslog/otel_handler.go index 2bf1273..c2dfd2c 100644 --- a/otelslog/otel_handler.go +++ b/otelslog/otel_handler.go @@ -37,11 +37,19 @@ type OtelHandler struct { otelHandler } +// HandlerOptions are options for a OtelHandler. +// A zero HandlerOptions consists entirely of default values. +type HandlerOptions struct { + level slog.Leveler +} + type otelHandler struct { - logger otel.Logger - opts HandlerOptions - mu *sync.Mutex - w io.Writer + logger otel.Logger + opts HandlerOptions + groupPrefix string + attrs []slog.Attr + mu *sync.Mutex + w io.Writer } // compilation time verification handler implement interface @@ -54,7 +62,11 @@ var instrumentationScope = instrumentation.Scope{ } func (o otelHandler) Enabled(ctx context.Context, level slog.Level) bool { - return true + minLevel := slog.LevelInfo + if o.opts.level != nil { + minLevel = o.opts.level.Level() + } + return level >= minLevel } func (o otelHandler) Handle(ctx context.Context, record slog.Record) error { @@ -76,8 +88,12 @@ func (o otelHandler) Handle(ctx context.Context, record slog.Record) error { var attributes []attribute.KeyValue - record.Attrs(func(attr slog.Attr) bool { + for _, attr := range o.attrs { attributes = append(attributes, otelAttribute(attr)...) + } + + record.Attrs(func(attr slog.Attr) bool { + attributes = append(attributes, otelAttribute(withGroupPrefix(o.groupPrefix, attr))...) return true }) @@ -100,19 +116,41 @@ func (o otelHandler) Handle(ctx context.Context, record slog.Record) error { return nil } +func withGroupPrefix(groupPrefix string, attr slog.Attr) slog.Attr { + if groupPrefix != "" { + attr.Key = groupPrefix + attr.Key + } + return attr +} + func (o otelHandler) WithAttrs(attrs []slog.Attr) slog.Handler { - //TODO implement me - panic("not implemented yet") + for i, attr := range attrs { + attrs[i] = withGroupPrefix(o.groupPrefix, attr) + } + + return &otelHandler{ + logger: o.logger, + opts: o.opts, + groupPrefix: o.groupPrefix, + attrs: append(o.attrs, attrs...), + } } func (o otelHandler) WithGroup(name string) slog.Handler { - //TODO implement me - panic("not implemented yet") -} + if name == "" { + return o + } + prefix := name + "." + if o.groupPrefix != "" { + prefix = o.groupPrefix + prefix + } -// HandlerOptions are options for a OtelHandler. -// A zero HandlerOptions consists entirely of default values. -type HandlerOptions struct { + return &otelHandler{ + logger: o.logger, + opts: o.opts, + attrs: o.attrs, + groupPrefix: prefix, + } } // NewOtelHandler creates a OtelHandler that writes to otlp, @@ -124,8 +162,9 @@ func NewOtelHandler(loggerProvider otel.LoggerProvider, opts *HandlerOptions) *O otel.WithInstrumentationVersion(instrumentationScope.Version), ) return &OtelHandler{ - otelHandler{ + otelHandler: otelHandler{ logger: logger, + opts: *opts, }, } } diff --git a/otelslog/otel_handler_test.go b/otelslog/otel_handler_test.go index 378437a..eeafb87 100644 --- a/otelslog/otel_handler_test.go +++ b/otelslog/otel_handler_test.go @@ -41,7 +41,15 @@ func TestNewOtelHandler(t *testing.T) { sdk.WithResource(newResource()), ) - otelLogger := slog.New(NewOtelHandler(loggerProvider, &HandlerOptions{})) + handler := NewOtelHandler(loggerProvider, &HandlerOptions{ + level: slog.LevelInfo, + }). + WithAttrs([]slog.Attr{slog.String("first", "value1")}). + WithGroup("group1"). + WithAttrs([]slog.Attr{slog.String("second", "value2")}). + WithGroup("group2") + + otelLogger := slog.New(handler) slog.SetDefault(otelLogger) doSomething(ctx) @@ -50,5 +58,5 @@ func TestNewOtelHandler(t *testing.T) { actual := buf.String() - assert.Contains(t, actual, "INFO hello slog [scopeInfo: github.com/agoda-com/otelslog:0.0.1] {host.name=CLX4NV72V6, service.name=otelslog-example, service.version=1.0.0, myKey=myValue}") + assert.Contains(t, actual, "INFO hello slog [scopeInfo: github.com/agoda-com/otelslog:0.0.1] {host.name=CLX4NV72V6, service.name=otelslog-example, service.version=1.0.0, first=value1, group1.second=value2, group1.group2.myKey=myValue}") }