-
Notifications
You must be signed in to change notification settings - Fork 492
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into otelmux-span-options
- Loading branch information
Showing
27 changed files
with
572 additions
and
258 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module go.opentelemetry.io/contrib/bridges/otelslog | ||
|
||
go 1.21 | ||
|
||
require ( | ||
github.com/stretchr/testify v1.9.0 | ||
go.opentelemetry.io/otel/log v0.0.1-alpha.0.20240319182811-335f4de960ff | ||
go.opentelemetry.io/otel/sdk v1.24.0 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/go-logr/logr v1.4.1 // indirect | ||
github.com/go-logr/stdr v1.2.2 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
go.opentelemetry.io/otel v1.24.0 // indirect | ||
go.opentelemetry.io/otel/metric v1.24.0 // indirect | ||
go.opentelemetry.io/otel/trace v1.24.0 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= | ||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= | ||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= | ||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= | ||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | ||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= | ||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= | ||
go.opentelemetry.io/otel/log v0.0.1-alpha.0.20240319182811-335f4de960ff h1:WMikyBC7alFcyvvVj22Spm8ad72hjUJTS5BQ4YlBDXY= | ||
go.opentelemetry.io/otel/log v0.0.1-alpha.0.20240319182811-335f4de960ff/go.mod h1:ToOZ06+agH/C+P2+bp6Ea/CLMDviyMVUNUQaKTB1ieg= | ||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= | ||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= | ||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw= | ||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= | ||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= | ||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Package otelslog provides [Handler], an [slog.Handler] implementation, that | ||
// can be used to bridge between the [log/slog] API and [OpenTelemetry]. | ||
// | ||
// [OpenTelemetry]: https://opentelemetry.io/docs/concepts/signals/logs/ | ||
package otelslog // import "go.opentelemetry.io/contrib/bridges/otelslog" | ||
|
||
import ( | ||
"context" | ||
"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 { | ||
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 { | ||
apply(config) config | ||
} | ||
|
||
type optFunc func(config) config | ||
|
||
func (f optFunc) apply(c config) config { return f(c) } | ||
|
||
// WithInstrumentationScope returns an [Option] that configures the scope of | ||
// the [log.Logger] used by a [Handler]. | ||
// | ||
// By default if this Option is not provided, the Handler will use a default | ||
// instrumentation scope describing this bridge package. It is recommended to | ||
// provide this so log data can be associated with its source package or | ||
// module. | ||
func WithInstrumentationScope(scope instrumentation.Scope) Option { | ||
return optFunc(func(c config) config { | ||
c.scope = scope | ||
return c | ||
}) | ||
} | ||
|
||
// WithLoggerProvider returns an [Option] that configures [log.LoggerProvider] | ||
// used by a [Handler] to create its [log.Logger]. | ||
// | ||
// By default if this Option is not provided, the Handler will use the global | ||
// LoggerProvider. | ||
func WithLoggerProvider(provider log.LoggerProvider) Option { | ||
return optFunc(func(c config) config { | ||
c.provider = provider | ||
return c | ||
}) | ||
} | ||
|
||
// Handler is an [slog.Handler] that sends all logging records it receives to | ||
// OpenTelemetry. | ||
type Handler struct { | ||
// Ensure forward compatibility by explicitly making this not comparable. | ||
noCmp [0]func() //nolint: unused // This is indeed used. | ||
|
||
logger log.Logger | ||
} | ||
|
||
// Compile-time check *Handler implements slog.Handler. | ||
var _ slog.Handler = (*Handler)(nil) | ||
|
||
// NewHandler returns a new [Handler] to be used as an [slog.Handler]. | ||
// | ||
// If [WithLoggerProvider] is not provided, the returned Handler will use the | ||
// global LoggerProvider. | ||
// | ||
// By default the returned Handler will use a [log.Logger] that is identified | ||
// with this bridge package information. [WithInstrumentationScope] should be | ||
// used to override this with details about the package or module the handler | ||
// will instrument. | ||
func NewHandler(options ...Option) *Handler { | ||
cfg := newConfig(options) | ||
return &Handler{logger: cfg.logger()} | ||
} | ||
|
||
// Handle handles the passed record. | ||
func (h *Handler) Handle(ctx context.Context, record slog.Record) error { | ||
// TODO: implement. | ||
return nil | ||
} | ||
|
||
// Enable returns true if the Handler is enabled to log for the provided | ||
// context and Level. Otherwise, false is returned if it is not enabled. | ||
func (h *Handler) Enabled(ctx context.Context, l slog.Level) bool { | ||
var record log.Record | ||
const sevOffset = slog.Level(log.SeverityDebug) - slog.LevelDebug | ||
record.SetSeverity(log.Severity(l + sevOffset)) | ||
return h.logger.Enabled(ctx, record) | ||
} | ||
|
||
// WithAttrs returns a new [slog.Handler] based on h that will log using the | ||
// passed attrs. | ||
func (h *Handler) WithAttrs(attrs []slog.Attr) slog.Handler { | ||
// TODO: implement. | ||
return h | ||
} | ||
|
||
// WithGroup returns a new [slog.Handler] based on h that will log all messages | ||
// and attributes within a group of the provided name. | ||
func (h *Handler) WithGroup(name string) slog.Handler { | ||
// TODO: implement. | ||
return h | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package otelslog | ||
|
||
import ( | ||
"context" | ||
"log/slog" | ||
"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) { | ||
assert.IsType(t, &Handler{}, NewLogger().Handler()) | ||
} | ||
|
||
// embeddedLogger is a type alias so the embedded.Logger type doesn't conflict | ||
// with the Logger method of the recorder when it is embedded. | ||
type embeddedLogger = embedded.Logger // nolint:unused // Used below. | ||
|
||
// recorder records all [log.Record]s it is ased to emit. | ||
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(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 | ||
|
||
var enableKey enablerKey | ||
|
||
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 | ||
|
||
h := NewHandler(WithLoggerProvider(r)) | ||
h.logger = r.Logger("") // TODO: Remove when #5311 merged. | ||
|
||
ctx := context.Background() | ||
assert.False(t, h.Enabled(ctx, slog.LevelDebug), "level conversion: permissive") | ||
assert.True(t, h.Enabled(ctx, slog.LevelInfo), "level conversion: restrictive") | ||
|
||
ctx = context.WithValue(ctx, enableKey, true) | ||
assert.True(t, h.Enabled(ctx, slog.LevelDebug), "context not passed") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.