From 851b2fd97fa45bb502cc094b6e3e84464a140fc3 Mon Sep 17 00:00:00 2001 From: Tyler Yahn Date: Wed, 20 Mar 2024 16:48:35 -0700 Subject: [PATCH] Implement the slog bridge config (#5311) * Add Version func * Implement the config * Add TestNewHandlerConfiguration * Replace Version func with const --- bridges/otelslog/handler.go | 48 ++++++++++++++++++++++++++--- bridges/otelslog/handler_test.go | 52 +++++++++++++++++++++++++++++++- bridges/otelslog/version.go | 7 +++++ 3 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 bridges/otelslog/version.go diff --git a/bridges/otelslog/handler.go b/bridges/otelslog/handler.go index 1675a10842a..dbf8e498256 100644 --- a/bridges/otelslog/handler.go +++ b/bridges/otelslog/handler.go @@ -12,16 +12,54 @@ import ( "log/slog" "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/sdk/instrumentation" ) +const bridgeName = "go.opentelemetry.io/contrib/bridges/otelslog" + // NewLogger returns a new [slog.Logger] backed by a new [Handler]. See // [NewHandler] for details on how the backing Handler is created. func NewLogger(options ...Option) *slog.Logger { return slog.New(NewHandler(options...)) } -type config struct{} +type config struct { + provider log.LoggerProvider + scope instrumentation.Scope +} + +func newConfig(options []Option) config { + var c config + for _, opt := range options { + c = opt.apply(c) + } + + var emptyScope instrumentation.Scope + if c.scope == emptyScope { + c.scope = instrumentation.Scope{ + Name: bridgeName, + Version: version, + } + } + + if c.provider == nil { + c.provider = global.GetLoggerProvider() + } + + return c +} + +func (c config) logger() log.Logger { + var opts []log.LoggerOption + if c.scope.Version != "" { + opts = append(opts, log.WithInstrumentationVersion(c.scope.Version)) + } + if c.scope.SchemaURL != "" { + opts = append(opts, log.WithSchemaURL(c.scope.SchemaURL)) + } + return c.provider.Logger(c.scope.Name, opts...) +} // Option configures a [Handler]. type Option interface { @@ -41,7 +79,7 @@ func (f optFunc) apply(c config) config { return f(c) } // module. func WithInstrumentationScope(scope instrumentation.Scope) Option { return optFunc(func(c config) config { - // TODO: implement. + c.scope = scope return c }) } @@ -53,7 +91,7 @@ func WithInstrumentationScope(scope instrumentation.Scope) Option { // LoggerProvider. func WithLoggerProvider(provider log.LoggerProvider) Option { return optFunc(func(c config) config { - // TODO: implement. + c.provider = provider return c }) } @@ -80,8 +118,8 @@ var _ slog.Handler = (*Handler)(nil) // used to override this with details about the package or module the handler // will instrument. func NewHandler(options ...Option) *Handler { - // TODO: implement. - return &Handler{} + cfg := newConfig(options) + return &Handler{logger: cfg.logger()} } // Handle handles the passed record. diff --git a/bridges/otelslog/handler_test.go b/bridges/otelslog/handler_test.go index f685d6b13bb..0822606648d 100644 --- a/bridges/otelslog/handler_test.go +++ b/bridges/otelslog/handler_test.go @@ -9,9 +9,12 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/embedded" + "go.opentelemetry.io/otel/log/global" + "go.opentelemetry.io/otel/sdk/instrumentation" ) func TestNewLogger(t *testing.T) { @@ -27,14 +30,28 @@ type recorder struct { embedded.LoggerProvider embeddedLogger // nolint:unused // Used to embed embedded.Logger. + // Scope is the Logger scope recorder received when Logger was called. + Scope instrumentation.Scope + // MinSeverity is the minimum severity the recorder will return true for // when Enabled is called (unless enableKey is set). MinSeverity log.Severity } -func (r *recorder) Logger(string, ...log.LoggerOption) log.Logger { return r } +func (r *recorder) Logger(name string, opts ...log.LoggerOption) log.Logger { + cfg := log.NewLoggerConfig(opts...) + + r2 := *r + r2.Scope = instrumentation.Scope{ + Name: name, + Version: cfg.InstrumentationVersion(), + SchemaURL: cfg.SchemaURL(), + } + return &r2 +} func (r *recorder) Emit(context.Context, log.Record) { + // TODO: implement. } type enablerKey uint @@ -45,6 +62,39 @@ func (r *recorder) Enabled(ctx context.Context, record log.Record) bool { return ctx.Value(enableKey) != nil || record.Severity() >= r.MinSeverity } +func TestNewHandlerConfiguration(t *testing.T) { + t.Run("Default", func(t *testing.T) { + r := new(recorder) + global.SetLoggerProvider(r) + + var h *Handler + assert.NotPanics(t, func() { h = NewHandler() }) + assert.NotNil(t, h.logger) + require.IsType(t, &recorder{}, h.logger) + + l := h.logger.(*recorder) + want := instrumentation.Scope{Name: bridgeName, Version: version} + assert.Equal(t, want, l.Scope) + }) + + t.Run("Options", func(t *testing.T) { + r := new(recorder) + scope := instrumentation.Scope{Name: "name", Version: "ver", SchemaURL: "url"} + var h *Handler + assert.NotPanics(t, func() { + h = NewHandler( + WithLoggerProvider(r), + WithInstrumentationScope(scope), + ) + }) + assert.NotNil(t, h.logger) + require.IsType(t, &recorder{}, h.logger) + + l := h.logger.(*recorder) + assert.Equal(t, scope, l.Scope) + }) +} + func TestHandlerEnabled(t *testing.T) { r := new(recorder) r.MinSeverity = log.SeverityInfo diff --git a/bridges/otelslog/version.go b/bridges/otelslog/version.go new file mode 100644 index 00000000000..a8aaac0cc33 --- /dev/null +++ b/bridges/otelslog/version.go @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otelslog // import "go.opentelemetry.io/contrib/bridges/otelslog" + +// version is the current release version of otelslog in use. +const version = "0.0.1-alpha"