From c35b44b283ee805e0505a5bca1e4e9f716effd78 Mon Sep 17 00:00:00 2001 From: CZ Date: Tue, 10 Sep 2024 02:21:50 +1200 Subject: [PATCH 1/5] Add skeleton for otellogr bridge --- bridges/otellogr/example_test.go | 22 +++++ bridges/otellogr/go.mod | 19 ++++ bridges/otellogr/go.sum | 25 ++++++ bridges/otellogr/logsink.go | 146 +++++++++++++++++++++++++++++++ bridges/otellogr/logsink_test.go | 98 +++++++++++++++++++++ versions.yaml | 1 + 6 files changed, 311 insertions(+) create mode 100644 bridges/otellogr/example_test.go create mode 100644 bridges/otellogr/go.mod create mode 100644 bridges/otellogr/go.sum create mode 100644 bridges/otellogr/logsink.go create mode 100644 bridges/otellogr/logsink_test.go diff --git a/bridges/otellogr/example_test.go b/bridges/otellogr/example_test.go new file mode 100644 index 00000000000..4b544a0b2b5 --- /dev/null +++ b/bridges/otellogr/example_test.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otellogr_test + +import ( + "github.com/go-logr/logr" + + "go.opentelemetry.io/contrib/bridges/otellogr" + "go.opentelemetry.io/otel/log/noop" +) + +func Example() { + // Use a working LoggerProvider implementation instead e.g. using go.opentelemetry.io/otel/sdk/log. + provider := noop.NewLoggerProvider() + + // Create an logr.Logger with *otellogr.LogSink and use it in your application. + logr.New(otellogr.NewLogSink( + "my/pkg/name", + otellogr.WithLoggerProvider(provider)), + ) +} diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod new file mode 100644 index 00000000000..6c30f3e814a --- /dev/null +++ b/bridges/otellogr/go.mod @@ -0,0 +1,19 @@ +module go.opentelemetry.io/contrib/bridges/otellogr + +go 1.21 + +require ( + github.com/go-logr/logr v1.4.2 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/otel/log v0.4.0 +) + +require ( + github.com/davecgh/go-spew v1.1.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.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum new file mode 100644 index 00000000000..399466bb30c --- /dev/null +++ b/bridges/otellogr/go.sum @@ -0,0 +1,25 @@ +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.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= +go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +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= diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go new file mode 100644 index 00000000000..0a2270e4056 --- /dev/null +++ b/bridges/otellogr/logsink.go @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// Package otellogr provides a [LogSink], a [logr.LogSink] implementation that +// can be used to bridge between the [logr] API and [OpenTelemetry]. +// +// [OpenTelemetry]: https://opentelemetry.io/docs/concepts/signals/logs/ +package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" + +import ( + "github.com/go-logr/logr" + + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/global" +) + +type config struct { + provider log.LoggerProvider + version string + schemaURL string +} + +func newConfig(options []Option) config { + var c config + for _, opt := range options { + c = opt.apply(c) + } + + if c.provider == nil { + c.provider = global.GetLoggerProvider() + } + + return c +} + +func (c config) logger(name string) log.Logger { + var opts []log.LoggerOption + if c.version != "" { + opts = append(opts, log.WithInstrumentationVersion(c.version)) + } + if c.schemaURL != "" { + opts = append(opts, log.WithSchemaURL(c.schemaURL)) + } + return c.provider.Logger(name, opts...) +} + +// Option configures a [LogSink]. +type Option interface { + apply(config) config +} + +type optFunc func(config) config + +func (f optFunc) apply(c config) config { return f(c) } + +// WithVersion returns an [Option] that configures the version of the +// [log.Logger] used by a [Hook]. The version should be the version of the +// package that is being logged. +func WithVersion(version string) Option { + return optFunc(func(c config) config { + c.version = version + return c + }) +} + +// WithSchemaURL returns an [Option] that configures the semantic convention +// schema URL of the [log.Logger] used by a [Hook]. The schemaURL should be +// the schema URL for the semantic conventions used in log records. +func WithSchemaURL(schemaURL string) Option { + return optFunc(func(c config) config { + c.schemaURL = schemaURL + return c + }) +} + +// WithLoggerProvider returns an [Option] that configures [log.LoggerProvider] +// used by a [LogSink] to create its [log.Logger]. +// +// By default if this Option is not provided, the LogSink will use the global +// LoggerProvider. +func WithLoggerProvider(provider log.LoggerProvider) Option { + return optFunc(func(c config) config { + c.provider = provider + return c + }) +} + +// NewLogSink returns a new [LogSink] to be used as a [logr.LogSink]. +// +// If [WithLoggerProvider] is not provided, the returned LogSink will use the +// global LoggerProvider. +func NewLogSink(name string, options ...Option) *LogSink { + c := newConfig(options) + return &LogSink{ + name: name, + logger: c.logger(name), + } +} + +// LogSink is a [logr.LogSink] that sends all logging records it receives to +// OpenTelemetry. See package documentation for how conversions are made. +type LogSink struct { + // Ensure forward compatibility by explicitly making this not comparable. + noCmp [0]func() //nolint: unused // This is indeed used. + + name string + logger log.Logger +} + +// Compile-time check *Handler implements logr.LogSink. +var _ logr.LogSink = (*LogSink)(nil) + +// Enabled tests whether this LogSink is enabled at the specified V-level. +// For example, commandline flags might be used to set the logging +// verbosity and disable some info logs. +func (l *LogSink) Enabled(level int) bool { + // TODO + return true +} + +// Error logs an error, with the given message and key/value pairs. +func (l *LogSink) Error(err error, msg string, keysAndValues ...any) { + // TODO +} + +// Info logs a non-error message with the given key/value pairs. +func (l *LogSink) Info(level int, msg string, keysAndValues ...any) { + // TODO +} + +// Init initializes the LogSink. +func (l *LogSink) Init(info logr.RuntimeInfo) { + // TODO +} + +// WithName returns a new LogSink with the specified name appended. +func (l LogSink) WithName(name string) logr.LogSink { + // TODO + return &l +} + +// WithValues returns a new LogSink with additional key/value pairs. +func (l LogSink) WithValues(keysAndValues ...any) logr.LogSink { + // TODO + return &l +} diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go new file mode 100644 index 00000000000..59fa5bc7589 --- /dev/null +++ b/bridges/otellogr/logsink_test.go @@ -0,0 +1,98 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +package otellogr + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/embedded" + "go.opentelemetry.io/otel/log/global" +) + +type mockLoggerProvider struct { + embedded.LoggerProvider +} + +func (mockLoggerProvider) Logger(name string, options ...log.LoggerOption) log.Logger { + return nil +} + +func TestNewConfig(t *testing.T) { + customLoggerProvider := mockLoggerProvider{} + + for _, tt := range []struct { + name string + options []Option + + wantConfig config + }{ + { + name: "with no options", + + wantConfig: config{ + provider: global.GetLoggerProvider(), + }, + }, + { + name: "with a custom instrumentation scope", + options: []Option{ + WithVersion("42.0"), + }, + + wantConfig: config{ + version: "42.0", + provider: global.GetLoggerProvider(), + }, + }, + { + name: "with a custom logger provider", + options: []Option{ + WithLoggerProvider(customLoggerProvider), + }, + + wantConfig: config{ + provider: customLoggerProvider, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.wantConfig, newConfig(tt.options)) + }) + } +} + +func TestNewLogSink(t *testing.T) { + const name = "test_logsink" + provider := global.GetLoggerProvider() + + for _, tt := range []struct { + name string + options []Option + wantLogger log.Logger + }{ + { + name: "with default options", + wantLogger: provider.Logger(name), + }, + { + name: "with version and schema URL", + options: []Option{ + WithVersion("1.0"), + WithSchemaURL("https://example.com"), + }, + wantLogger: provider.Logger(name, + log.WithInstrumentationVersion("1.0"), + log.WithSchemaURL("https://example.com"), + ), + }, + } { + t.Run(tt.name, func(t *testing.T) { + hook := NewLogSink(name, tt.options...) + assert.NotNil(t, hook) + assert.Equal(t, tt.wantLogger, hook.logger) + }) + } +} diff --git a/versions.yaml b/versions.yaml index d7902caf28d..1c35ccdec46 100644 --- a/versions.yaml +++ b/versions.yaml @@ -88,6 +88,7 @@ module-sets: modules: - go.opentelemetry.io/contrib/detectors/azure/azurevm excluded-modules: + - go.opentelemetry.io/contrib/bridges/otellogr - go.opentelemetry.io/contrib/instrgen - go.opentelemetry.io/contrib/instrgen/driver - go.opentelemetry.io/contrib/instrgen/testdata/interface From 5c4369bfbc8570e0d121513cf153ba79e3829540 Mon Sep 17 00:00:00 2001 From: CZ Date: Tue, 10 Sep 2024 02:26:44 +1200 Subject: [PATCH 2/5] Bump go to v1.22 --- bridges/otellogr/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index 6c30f3e814a..6a183b3c713 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -1,6 +1,6 @@ module go.opentelemetry.io/contrib/bridges/otellogr -go 1.21 +go 1.22 require ( github.com/go-logr/logr v1.4.2 From 8ed297e5fe4e0b4addd3a85a2545081d41d94bc5 Mon Sep 17 00:00:00 2001 From: CZ Date: Tue, 10 Sep 2024 02:28:46 +1200 Subject: [PATCH 3/5] fix go.mod version --- bridges/otellogr/go.mod | 8 ++++---- bridges/otellogr/go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index 6a183b3c713..598d0ed949e 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -5,15 +5,15 @@ go 1.22 require ( github.com/go-logr/logr v1.4.2 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel/log v0.4.0 + go.opentelemetry.io/otel/log v0.5.0 ) require ( github.com/davecgh/go-spew v1.1.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.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/otel v1.29.0 // indirect + go.opentelemetry.io/otel/metric v1.29.0 // indirect + go.opentelemetry.io/otel/trace v1.29.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 399466bb30c..e05b72b8c4d 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -11,14 +11,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb 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.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/log v0.4.0 h1:/vZ+3Utqh18e8TPjuc3ecg284078KWrR8BRz+PQAj3o= -go.opentelemetry.io/otel/log v0.4.0/go.mod h1:DhGnQvky7pHy82MIRV43iXh3FlKN8UUKftn0KbLOq6I= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= +go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= +go.opentelemetry.io/otel/log v0.5.0 h1:x1Pr6Y3gnXgl1iFBwtGy1W/mnzENoK0w0ZoaeOI3i30= +go.opentelemetry.io/otel/log v0.5.0/go.mod h1:NU/ozXeGuOR5/mjCRXYbTC00NFJ3NYuraV/7O78F0rE= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= +go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= +go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= 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= From 4e1982604b1a54d4cd5e1ce4ff5c1e0b1246f053 Mon Sep 17 00:00:00 2001 From: CZ Date: Wed, 11 Sep 2024 11:13:32 +1200 Subject: [PATCH 4/5] Add CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index f825c8de04f..327380ad9aa 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,6 +29,7 @@ bridges/otelslog @open-te bridges/otellogrus/ @open-telemetry/go-approvers @dmathieu @pellared bridges/prometheus/ @open-telemetry/go-approvers @dashpole bridges/otelzap/ @open-telemetry/go-approvers @pellared @khushijain21 +bridges/otellogr/ @open-telemetry/go-approvers @scorpionknifes config/ @open-telemetry/go-approvers @MadVikingGod @pellared @codeboten From 06874755bd9e598b7c354c7a0dfbe59d022e0fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Paj=C4=85k?= Date: Wed, 11 Sep 2024 14:20:48 +0200 Subject: [PATCH 5/5] Update CODEOWNERS --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 327380ad9aa..7d5e8ad529d 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,7 +29,7 @@ bridges/otelslog @open-te bridges/otellogrus/ @open-telemetry/go-approvers @dmathieu @pellared bridges/prometheus/ @open-telemetry/go-approvers @dashpole bridges/otelzap/ @open-telemetry/go-approvers @pellared @khushijain21 -bridges/otellogr/ @open-telemetry/go-approvers @scorpionknifes +bridges/otellogr/ @open-telemetry/go-approvers @scorpionknifes @pellared config/ @open-telemetry/go-approvers @MadVikingGod @pellared @codeboten