From 10239528f12f9872d3df9dba594f9d68e57ffef6 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Thu, 4 Apr 2024 03:33:35 +1300 Subject: [PATCH 01/21] WIP: Logging bridge for logr --- .github/dependabot.yml | 9 ++ bridges/otellogr/config.go | 82 ++++++++++ bridges/otellogr/example_test.go | 18 +++ bridges/otellogr/go.mod | 16 ++ bridges/otellogr/go.sum | 25 +++ bridges/otellogr/logsink.go | 255 +++++++++++++++++++++++++++++++ bridges/otellogr/logsink_test.go | 32 ++++ bridges/otellogr/version.go | 7 + 8 files changed, 444 insertions(+) create mode 100644 bridges/otellogr/config.go 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 create mode 100644 bridges/otellogr/version.go diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bb6bf7d7278..3e6cbd5f723 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -109,6 +109,15 @@ updates: schedule: interval: weekly day: sunday + - package-ecosystem: gomod + directory: /bridges/otellogr + labels: + - dependencies + - go + - Skip Changelog + schedule: + interval: weekly + day: sunday - package-ecosystem: gomod directory: /bridges/prometheus labels: diff --git a/bridges/otellogr/config.go b/bridges/otellogr/config.go new file mode 100644 index 00000000000..e4998ef7eb8 --- /dev/null +++ b/bridges/otellogr/config.go @@ -0,0 +1,82 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" + +import ( + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/global" + "go.opentelemetry.io/otel/sdk/instrumentation" +) + +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 [LogSink]. +// +// By default if this Option is not provided, the LogSink 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 [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 + }) +} diff --git a/bridges/otellogr/example_test.go b/bridges/otellogr/example_test.go new file mode 100644 index 00000000000..d511a210ad5 --- /dev/null +++ b/bridges/otellogr/example_test.go @@ -0,0 +1,18 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otellogr_test + +import ( + "github.com/go-logr/logr" + otellogr "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 *slog.Logger with *otelslog.Handler and use it in your application. + logr.New(otellogr.NewLogSink(otellogr.WithLoggerProvider(provider))) +} diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod new file mode 100644 index 00000000000..c4f2ea3f0f1 --- /dev/null +++ b/bridges/otellogr/go.mod @@ -0,0 +1,16 @@ +module go.opentelemetry.io/contrib/bridges/otellogr + +go 1.21 + +require ( + github.com/go-logr/logr v1.4.1 + go.opentelemetry.io/otel/log v0.0.1-alpha.0.20240319182811-335f4de960ff + go.opentelemetry.io/otel/sdk v1.24.0 +) + +require ( + github.com/go-logr/stdr v1.2.2 // 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 +) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum new file mode 100644 index 00000000000..193ec03125f --- /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.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/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..3ac2b3c240c --- /dev/null +++ b/bridges/otellogr/logsink.go @@ -0,0 +1,255 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" + +import ( + "context" + "fmt" + "reflect" + "strconv" + "time" + + "github.com/go-logr/logr" + "go.opentelemetry.io/otel/log" +) + +const ( + bridgeName = "go.opentelemetry.io/contrib/bridges/otellogr" + + // nameKey is used to log the `WithName` values as an additional attribute. + nameKey = "logger" + + // errKey is used to log the error parameter of Error as an additional attribute. + errKey = "err" +) + +type LogSink struct { + name string + logger log.Logger + values []log.KeyValue +} + +// Compile-time check *Handler implements logr.LogSink. +var _ logr.LogSink = (*LogSink)(nil) + +func NewLogSink(options ...Option) *LogSink { + c := newConfig(options) + return &LogSink{ + logger: c.logger(), + } +} + +func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...any) { + var record log.Record + record.SetTimestamp(time.Now()) + record.SetBody(log.StringValue(msg)) + record.SetSeverity(serverity) + + if l.name != "" { + record.AddAttributes(log.String(nameKey, l.name)) + } + + if err != nil { + record.AddAttributes(log.KeyValue{ + Key: errKey, + Value: convertValue(err), + }) + } + + if len(l.values) > 0 { + record.AddAttributes(l.values...) + } + + kv := convertKVList(kvList) + if len(kv) > 0 { + record.AddAttributes(kv...) + } + + ctx := context.Background() + l.logger.Emit(ctx, record) +} + +// 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 { + var record log.Record + const sevOffset = int(log.SeverityDebug) + record.SetSeverity(log.Severity(level + sevOffset)) + ctx := context.Background() + return l.logger.Enabled(ctx, record) +} + +// Error logs an error, with the given message and key/value pairs as +// context. +func (l *LogSink) Error(err error, msg string, keysAndValues ...any) { + const severity = log.SeverityError + + l.log(err, msg, severity, keysAndValues...) +} + +// Info logs a non-error message with the given key/value pairs as context. +func (l *LogSink) Info(level int, msg string, keysAndValues ...any) { + const sevOffset = int(log.SeverityInfo) + severity := log.Severity(sevOffset + level) + + l.log(nil, msg, severity, keysAndValues...) +} + +// Init receives optional information about the logr library this +// implementation does not use it. +func (l *LogSink) Init(info logr.RuntimeInfo) { + // We don't need to do anything here. + // CallDepth is used to calculate the caller's PC. + // PC is dropped. +} + +// WithName returns a new LogSink with the specified name appended. +func (l LogSink) WithName(name string) logr.LogSink { + if len(l.name) > 0 { + l.name += "/" + } + l.name += name + return &l +} + +// WithValues returns a new LogSink with additional key/value pairs. +func (l LogSink) WithValues(keysAndValues ...any) logr.LogSink { + attrs := convertKVList(keysAndValues) + l.values = append(l.values, attrs...) + return &l +} + +func convertKVList(kvList []any) []log.KeyValue { + if len(kvList) == 0 { + return nil + } + if len(kvList)%2 != 0 { + // Ensure an odd number of items here does not corrupt the list + kvList = append(kvList, nil) + } + + kv := make([]log.KeyValue, 0, len(kvList)/2) + for i := 0; i < len(kvList); i += 2 { + k, ok := kvList[i].(string) + if !ok { + // Ensure that the key is a string + k = fmt.Sprintf("%v", kvList[i]) + } + kv = append(kv, log.KeyValue{ + Key: k, + Value: convertValue(kvList[i+1]), + }) + } + return kv +} + +func convertValue(v interface{}) log.Value { + // Handling the most common types without reflect is a small perf win. + switch val := v.(type) { + case bool: + return log.BoolValue(val) + case string: + return log.StringValue(val) + case int: + return log.Int64Value(int64(val)) + case int8: + return log.Int64Value(int64(val)) + case int16: + return log.Int64Value(int64(val)) + case int32: + return log.Int64Value(int64(val)) + case int64: + return log.Int64Value(val) + case uint: + return assignUintValue(uint64(val)) + case uint8: + return log.Int64Value(int64(val)) + case uint16: + return log.Int64Value(int64(val)) + case uint32: + return log.Int64Value(int64(val)) + case uint64: + return assignUintValue(val) + case uintptr: + return assignUintValue(uint64(val)) + case float32: + return log.Float64Value(float64(val)) + case float64: + return log.Float64Value(val) + case time.Duration: + return log.Int64Value(val.Nanoseconds()) + case complex64: + stringValue := `"` + strconv.FormatComplex(complex128(val), 'f', -1, 64) + `"` + return log.StringValue(stringValue) + case complex128: + stringValue := `"` + strconv.FormatComplex(val, 'f', -1, 128) + `"` + return log.StringValue(stringValue) + case time.Time: + return log.Int64Value(val.UnixNano()) + case []byte: + return log.BytesValue(val) + case error: + return log.StringValue(fmt.Sprintf("%+v", val)) + } + + t := reflect.TypeOf(v) + if t == nil { + return log.Value{} + } + val := reflect.ValueOf(v) + switch t.Kind() { + case reflect.Bool: + return log.BoolValue(val.Bool()) + case reflect.String: + return log.StringValue(val.String()) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return log.Int64Value(val.Int()) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return assignUintValue(val.Uint()) + case reflect.Float32, reflect.Float64: + return log.Float64Value(val.Float()) + case reflect.Complex64, reflect.Complex128: + stringValue := `"` + strconv.FormatComplex(complex128(val.Complex()), 'f', -1, 64) + `"` + return log.StringValue(stringValue) + case reflect.Struct: + return log.StringValue(fmt.Sprintf("%+v", v)) + case reflect.Slice, reflect.Array: + items := make([]log.Value, 0, val.Len()) + for i := 0; i < val.Len(); i++ { + items = append(items, convertValue(val.Index(i).Interface())) + } + return log.SliceValue(items...) + case reflect.Map: + kvs := make([]log.KeyValue, 0, val.Len()) + for _, k := range val.MapKeys() { + kvs = append(kvs, log.KeyValue{ + Key: k.String(), + Value: convertValue(val.MapIndex(k).Interface()), + }) + } + return log.MapValue(kvs...) + case reflect.Ptr, reflect.Interface: + if val.IsNil() { + return log.Value{} + } + return convertValue(val.Elem().Interface()) + } + + // Try to handle this as gracefully as possible. + // + // Don't panic here. it is preferable to have user's open issue + // asking why their attributes have a "unhandled: " prefix than + // say that their code is panicking. + return log.StringValue(fmt.Sprintf("unhandled: (%s) %+v", t, v)) +} + +func assignUintValue(v uint64) log.Value { + const maxInt64 = ^uint64(0) >> 1 + if v > maxInt64 { + value := strconv.FormatUint(v, 10) + return log.StringValue(value) + } + return log.Int64Value(int64(v)) +} diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go new file mode 100644 index 00000000000..c2e94e6dec4 --- /dev/null +++ b/bridges/otellogr/logsink_test.go @@ -0,0 +1,32 @@ +package otellogr + +import ( + "errors" + "testing" + + "github.com/go-logr/logr" + "go.opentelemetry.io/otel/log/noop" +) + +func TestWIP(t *testing.T) { + provider := noop.NewLoggerProvider() + logger := logr.New(NewLogSink(WithLoggerProvider(provider))) + + // Tests are WIP, the following code is just to make sure the code compiles + + logger.Info("This is a test message") + + logger.Error(errors.New("This is a test error message"), "This is a test error message") + + logger.V(1).Info("This is a test message with verbosity level 1") + + logger.WithName("test").Info("This is a test message with a name") + + logger.WithValues("key", "value").Info("This is a test message with values") + + logger.WithName("test").WithValues("key", "value").Info("This is a test message with a name and values") + + logger.Info("This is a test message with a name and values", "key", "value") + + logger.Info("This is a test message with a name and values", "int", 10, "bool", true) +} diff --git a/bridges/otellogr/version.go b/bridges/otellogr/version.go new file mode 100644 index 00000000000..c0931956f91 --- /dev/null +++ b/bridges/otellogr/version.go @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" + +// version is the current release version of otelslog in use. +const version = "0.0.1-alpha" From d3c70aea96a4d976a4f4114daea0b0f9eea7cd24 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Sat, 6 Apr 2024 14:33:49 +1300 Subject: [PATCH 02/21] Add basic unit tests --- bridges/otellogr/go.mod | 4 + bridges/otellogr/go.sum | 2 + bridges/otellogr/logsink.go | 89 +++--- bridges/otellogr/logsink_test.go | 489 ++++++++++++++++++++++++++++++- 4 files changed, 521 insertions(+), 63 deletions(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index c4f2ea3f0f1..9afd4cbd411 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -4,13 +4,17 @@ go 1.21 require ( github.com/go-logr/logr v1.4.1 + 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/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 ) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 193ec03125f..472ab1bec59 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -21,5 +21,7 @@ go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucg 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= diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 3ac2b3c240c..08bfaa2c9fb 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -6,6 +6,7 @@ package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" import ( "context" "fmt" + "math" "reflect" "strconv" "time" @@ -19,11 +20,23 @@ const ( // nameKey is used to log the `WithName` values as an additional attribute. nameKey = "logger" - // errKey is used to log the error parameter of Error as an additional attribute. errKey = "err" ) +// 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(options ...Option) *LogSink { + c := newConfig(options) + return &LogSink{ + logger: c.logger(), + } +} + +// 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 { name string logger log.Logger @@ -33,13 +46,7 @@ type LogSink struct { // Compile-time check *Handler implements logr.LogSink. var _ logr.LogSink = (*LogSink)(nil) -func NewLogSink(options ...Option) *LogSink { - c := newConfig(options) - return &LogSink{ - logger: c.logger(), - } -} - +// log sends a log record to the OpenTelemetry logger. func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...any) { var record log.Record record.SetTimestamp(time.Now()) @@ -61,7 +68,7 @@ func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...a record.AddAttributes(l.values...) } - kv := convertKVList(kvList) + kv := convertKVs(kvList) if len(kv) > 0 { record.AddAttributes(kv...) } @@ -81,15 +88,14 @@ func (l *LogSink) Enabled(level int) bool { return l.logger.Enabled(ctx, record) } -// Error logs an error, with the given message and key/value pairs as -// context. +// Error logs an error, with the given message and key/value pairs. func (l *LogSink) Error(err error, msg string, keysAndValues ...any) { const severity = log.SeverityError l.log(err, msg, severity, keysAndValues...) } -// Info logs a non-error message with the given key/value pairs as context. +// Info logs a non-error message with the given key/value pairs. func (l *LogSink) Info(level int, msg string, keysAndValues ...any) { const sevOffset = int(log.SeverityInfo) severity := log.Severity(sevOffset + level) @@ -102,7 +108,7 @@ func (l *LogSink) Info(level int, msg string, keysAndValues ...any) { func (l *LogSink) Init(info logr.RuntimeInfo) { // We don't need to do anything here. // CallDepth is used to calculate the caller's PC. - // PC is dropped. + // PC is dropped as part of the conversion to the OpenTelemetry log.Record. } // WithName returns a new LogSink with the specified name appended. @@ -116,36 +122,36 @@ func (l LogSink) WithName(name string) logr.LogSink { // WithValues returns a new LogSink with additional key/value pairs. func (l LogSink) WithValues(keysAndValues ...any) logr.LogSink { - attrs := convertKVList(keysAndValues) + attrs := convertKVs(keysAndValues) l.values = append(l.values, attrs...) return &l } -func convertKVList(kvList []any) []log.KeyValue { - if len(kvList) == 0 { +func convertKVs(keysAndValues []any) []log.KeyValue { + if len(keysAndValues) == 0 { return nil } - if len(kvList)%2 != 0 { + if len(keysAndValues)%2 != 0 { // Ensure an odd number of items here does not corrupt the list - kvList = append(kvList, nil) + keysAndValues = append(keysAndValues, nil) } - kv := make([]log.KeyValue, 0, len(kvList)/2) - for i := 0; i < len(kvList); i += 2 { - k, ok := kvList[i].(string) + kv := make([]log.KeyValue, 0, len(keysAndValues)/2) + for i := 0; i < len(keysAndValues); i += 2 { + k, ok := keysAndValues[i].(string) if !ok { // Ensure that the key is a string - k = fmt.Sprintf("%v", kvList[i]) + k = fmt.Sprintf("%v", keysAndValues[i]) } kv = append(kv, log.KeyValue{ Key: k, - Value: convertValue(kvList[i+1]), + Value: convertValue(keysAndValues[i+1]), }) } return kv } -func convertValue(v interface{}) log.Value { +func convertValue(v any) log.Value { // Handling the most common types without reflect is a small perf win. switch val := v.(type) { case bool: @@ -163,7 +169,7 @@ func convertValue(v interface{}) log.Value { case int64: return log.Int64Value(val) case uint: - return assignUintValue(uint64(val)) + return convertUintValue(uint64(val)) case uint8: return log.Int64Value(int64(val)) case uint16: @@ -171,9 +177,9 @@ func convertValue(v interface{}) log.Value { case uint32: return log.Int64Value(int64(val)) case uint64: - return assignUintValue(val) + return convertUintValue(val) case uintptr: - return assignUintValue(uint64(val)) + return convertUintValue(uint64(val)) case float32: return log.Float64Value(float64(val)) case float64: @@ -181,11 +187,9 @@ func convertValue(v interface{}) log.Value { case time.Duration: return log.Int64Value(val.Nanoseconds()) case complex64: - stringValue := `"` + strconv.FormatComplex(complex128(val), 'f', -1, 64) + `"` - return log.StringValue(stringValue) + return log.StringValue(strconv.FormatComplex(complex128(val), 'f', -1, 64)) case complex128: - stringValue := `"` + strconv.FormatComplex(val, 'f', -1, 128) + `"` - return log.StringValue(stringValue) + return log.StringValue(strconv.FormatComplex(val, 'f', -1, 128)) case time.Time: return log.Int64Value(val.UnixNano()) case []byte: @@ -200,19 +204,6 @@ func convertValue(v interface{}) log.Value { } val := reflect.ValueOf(v) switch t.Kind() { - case reflect.Bool: - return log.BoolValue(val.Bool()) - case reflect.String: - return log.StringValue(val.String()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return log.Int64Value(val.Int()) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return assignUintValue(val.Uint()) - case reflect.Float32, reflect.Float64: - return log.Float64Value(val.Float()) - case reflect.Complex64, reflect.Complex128: - stringValue := `"` + strconv.FormatComplex(complex128(val.Complex()), 'f', -1, 64) + `"` - return log.StringValue(stringValue) case reflect.Struct: return log.StringValue(fmt.Sprintf("%+v", v)) case reflect.Slice, reflect.Array: @@ -225,7 +216,7 @@ func convertValue(v interface{}) log.Value { kvs := make([]log.KeyValue, 0, val.Len()) for _, k := range val.MapKeys() { kvs = append(kvs, log.KeyValue{ - Key: k.String(), + Key: fmt.Sprintf("%v", k.Interface()), Value: convertValue(val.MapIndex(k).Interface()), }) } @@ -245,11 +236,9 @@ func convertValue(v interface{}) log.Value { return log.StringValue(fmt.Sprintf("unhandled: (%s) %+v", t, v)) } -func assignUintValue(v uint64) log.Value { - const maxInt64 = ^uint64(0) >> 1 - if v > maxInt64 { - value := strconv.FormatUint(v, 10) - return log.StringValue(value) +func convertUintValue(v uint64) log.Value { + if v > math.MaxInt64 { + return log.StringValue(strconv.FormatUint(v, 10)) } return log.Int64Value(int64(v)) } diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index c2e94e6dec4..a53df7d7e35 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -1,32 +1,495 @@ package otellogr import ( + "context" "errors" + "fmt" "testing" + "time" "github.com/go-logr/logr" - "go.opentelemetry.io/otel/log/noop" + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/embedded" + "go.opentelemetry.io/otel/sdk/instrumentation" ) -func TestWIP(t *testing.T) { - provider := noop.NewLoggerProvider() - logger := logr.New(NewLogSink(WithLoggerProvider(provider))) +// 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. - // Tests are WIP, the following code is just to make sure the code compiles +// recorder records all [log.Record]s it is ased to emit. +type recorder struct { + embedded.LoggerProvider + embeddedLogger // nolint:unused // Used to embed embedded.Logger. - logger.Info("This is a test message") + // Records are the records emitted. + Records []log.Record - logger.Error(errors.New("This is a test error message"), "This is a test error message") + // Scope is the Logger scope recorder received when Logger was called. + Scope instrumentation.Scope - logger.V(1).Info("This is a test message with verbosity level 1") + // 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...) + + r.Scope = instrumentation.Scope{ + Name: name, + Version: cfg.InstrumentationVersion(), + SchemaURL: cfg.SchemaURL(), + } + return r +} + +func (r *recorder) Enabled(ctx context.Context, record log.Record) bool { + return record.Severity() >= r.MinSeverity +} + +func (r *recorder) Emit(_ context.Context, record log.Record) { + r.Records = append(r.Records, record) +} + +type expectedRecord struct { + Body log.Value + Severity log.Severity + Attributes []log.KeyValue +} + +var now = time.Now() - logger.WithName("test").Info("This is a test message with a name") +func TestLogSink(t *testing.T) { + for _, tt := range []struct { + name string + f func(*logr.Logger) + expectedRecords []expectedRecord + }{ + { + name: "info", + f: func(l *logr.Logger) { + l.Info("info message") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("info message"), + Severity: log.SeverityInfo, + }, + }, + }, + { + name: "info-multi-attrs", + f: func(l *logr.Logger) { + l.Info("msg", + "struct", struct{ data int64 }{data: 1}, + "bool", true, + "duration", time.Minute, + "float64", 3.14159, + "int64", -2, + "string", "str", + "time", now, + "uint64", uint64(3), + ) + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("msg"), + Severity: log.SeverityInfo, + Attributes: []log.KeyValue{ + log.String("struct", "{data:1}"), + log.Bool("bool", true), + log.Int64("duration", 60_000_000_000), + log.Float64("float64", 3.14159), + log.Int64("int64", -2), + log.String("string", "str"), + log.Int64("time", now.UnixNano()), + log.Int64("uint64", 3), + }, + }, + }, + }, + { + name: "error", + f: func(l *logr.Logger) { + l.Error(errors.New("test error"), "error message") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("error message"), + Severity: log.SeverityError, + Attributes: []log.KeyValue{ + log.String(errKey, "test error"), + }, + }, + }, + }, + { + name: "error-multi-attrs", + f: func(l *logr.Logger) { + l.Error(errors.New("error"), "msg", + "struct", struct{ data int64 }{data: 1}, + "bool", true, + "duration", time.Minute, + "float64", 3.14159, + "int64", -2, + "string", "str", + "time", now, + "uint64", uint64(3), + ) + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("msg"), + Severity: log.SeverityError, + Attributes: []log.KeyValue{ + log.String(errKey, "error"), + log.String("struct", "{data:1}"), + log.Bool("bool", true), + log.Int64("duration", 60_000_000_000), + log.Float64("float64", 3.14159), + log.Int64("int64", -2), + log.String("string", "str"), + log.Int64("time", now.UnixNano()), + log.Int64("uint64", 3), + }, + }, + }, + }, + { + name: "info-with-name", + f: func(l *logr.Logger) { + l.WithName("test").Info("info message with name") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("info message with name"), + Severity: log.SeverityInfo, + Attributes: []log.KeyValue{ + log.String(nameKey, "test"), + }, + }, + }, + }, + { + name: "info-with-name-nested", + f: func(l *logr.Logger) { + l.WithName("test").WithName("test").Info("info message with name") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("info message with name"), + Severity: log.SeverityInfo, + Attributes: []log.KeyValue{ + log.String(nameKey, "test/test"), + }, + }, + }, + }, + { + name: "info-with-attrs", + f: func(l *logr.Logger) { + l.WithValues("key", "value").Info("info message with attrs") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("info message with attrs"), + Severity: log.SeverityInfo, + Attributes: []log.KeyValue{ + log.String("key", "value"), + }, + }, + }, + }, + { + name: "info-with-attrs-nested", + f: func(l *logr.Logger) { + l.WithValues("key1", "value1").Info("info message with attrs", "key2", "value2") + }, + expectedRecords: []expectedRecord{ + { + Body: log.StringValue("info message with attrs"), + Severity: log.SeverityInfo, + Attributes: []log.KeyValue{ + log.String("key1", "value1"), + log.String("key2", "value2"), + }, + }, + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + r := new(recorder) + ls := NewLogSink(WithLoggerProvider(r)) + l := logr.New(ls) + tt.f(&l) - logger.WithValues("key", "value").Info("This is a test message with values") + assert.Len(t, r.Records, len(tt.expectedRecords)) + for i, record := range r.Records { + assert.Equal(t, tt.expectedRecords[i].Body, record.Body()) + assert.Equal(t, tt.expectedRecords[i].Severity, record.Severity()) - logger.WithName("test").WithValues("key", "value").Info("This is a test message with a name and values") + var attributes []log.KeyValue + record.WalkAttributes(func(kv log.KeyValue) bool { + attributes = append(attributes, kv) + return true + }) + assert.Equal(t, tt.expectedRecords[i].Attributes, attributes) + } + }) + } +} + +func TestLogSinkEnabled(t *testing.T) { + r := new(recorder) + ls := NewLogSink(WithLoggerProvider(r)) + + assert.True(t, ls.Enabled(1)) + assert.True(t, ls.Enabled(0)) + assert.False(t, ls.Enabled(-10)) +} + +func TestConvertKVs(t *testing.T) { + for _, tt := range []struct { + name string + + kvs []any + expectedKVs []log.KeyValue + }{ + { + name: "empty", + kvs: nil, + }, + { + name: "single-value", + kvs: []any{"key", "value"}, + expectedKVs: []log.KeyValue{ + log.String("key", "value"), + }, + }, + { + name: "multiple-values", + kvs: []any{"key1", "value1", "key2", "value2"}, + expectedKVs: []log.KeyValue{ + log.String("key1", "value1"), + log.String("key2", "value2"), + }, + }, + { + name: "missing-value", + kvs: []any{"key1", "value1", "key2"}, + expectedKVs: []log.KeyValue{ + log.String("key1", "value1"), + {Key: "key2", Value: log.Value{}}, + }, + }, + { + name: "key-not-string", + kvs: []any{42, "value"}, + expectedKVs: []log.KeyValue{ + log.String("42", "value"), + }, + }, + } { + t.Run(tt.name, func(t *testing.T) { + kvs := convertKVs(tt.kvs) + assert.Equal(t, tt.expectedKVs, kvs) + }) + } +} + +func TestConvertValue(t *testing.T) { + for _, tt := range []struct { + name string + + value any + expectedValue log.Value + }{ + { + name: "bool", + value: true, + expectedValue: log.BoolValue(true), + }, + { + name: "string", + value: "value", + expectedValue: log.StringValue("value"), + }, + { + name: "int", + value: 10, + expectedValue: log.Int64Value(10), + }, + { + name: "int8", + value: int8(127), + expectedValue: log.Int64Value(127), + }, + { + name: "int16", + value: int16(32767), + expectedValue: log.Int64Value(32767), + }, + { + name: "int32", + value: int32(2147483647), + expectedValue: log.Int64Value(2147483647), + }, + { + name: "int64", + value: int64(9223372036854775807), + expectedValue: log.Int64Value(9223372036854775807), + }, + { + name: "uint", + value: uint(42), + expectedValue: log.Int64Value(42), + }, + { + name: "uint8", + value: uint8(255), + expectedValue: log.Int64Value(255), + }, + { + name: "uint16", + value: uint16(65535), + expectedValue: log.Int64Value(65535), + }, + { + name: "uint32", + value: uint32(4294967295), + expectedValue: log.Int64Value(4294967295), + }, + { + name: "uint64", + value: uint64(9223372036854775807), + expectedValue: log.Int64Value(9223372036854775807), + }, + { + name: "uint64-max", + value: uint64(18446744073709551615), + expectedValue: log.StringValue("18446744073709551615"), + }, + { + name: "uintptr", + value: uintptr(12345), + expectedValue: log.Int64Value(12345), + }, + { + name: "float64", + value: float64(3.14159), + expectedValue: log.Float64Value(3.14159), + }, + { + name: "time.Duration", + value: time.Second, + expectedValue: log.Int64Value(1_000_000_000), + }, + { + name: "complex64", + value: complex(float32(1), float32(2)), + expectedValue: log.StringValue("(1+2i)"), + }, + { + name: "complex128", + value: complex(float64(3), float64(4)), + expectedValue: log.StringValue("(3+4i)"), + }, + { + name: "time.Time", + value: now, + expectedValue: log.Int64Value(now.UnixNano()), + }, + { + name: "[]byte", + value: []byte("hello"), + expectedValue: log.BytesValue([]byte("hello")), + }, + { + name: "error", + value: errors.New("test error"), + expectedValue: log.StringValue("test error"), + }, + { + name: "error", + value: errors.New("test error"), + expectedValue: log.StringValue("test error"), + }, + { + name: "error-nested", + value: fmt.Errorf("test error: %w", errors.New("nested error")), + expectedValue: log.StringValue("test error: nested error"), + }, + { + name: "nil", + value: nil, + expectedValue: log.Value{}, + }, + { + name: "ptrint", + value: func() *int { i := 93; return &i }(), + expectedValue: log.Int64Value(93), + }, + { + name: "ptrstring", + value: func() *string { s := "hello"; return &s }(), + expectedValue: log.StringValue("hello"), + }, + { + name: "ptrbool", + value: func() *bool { b := true; return &b }(), + expectedValue: log.BoolValue(true), + }, + { + name: "int-empty-array", + value: []int{}, + expectedValue: log.SliceValue([]log.Value{}...), + }, + { + name: "int-array", + value: []int{1, 2, 3}, + expectedValue: log.SliceValue([]log.Value{ + log.Int64Value(1), + log.Int64Value(2), + log.Int64Value(3), + }...), + }, + { + name: "key-value-map", + value: map[string]int{"one": 1}, + expectedValue: log.MapValue( + log.Int64("one", 1), + ), + }, + { + name: "int-string-map", + value: map[int]string{1: "one"}, + expectedValue: log.MapValue( + log.String("1", "one"), + ), + }, + { + name: "struct", + value: struct { + Name string + Age int + }{ + Name: "John", + Age: 42, + }, + expectedValue: log.StringValue("{Name:John Age:42}"), + }, + } { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, convertValue(tt.value), tt.expectedValue) + }) + } +} - logger.Info("This is a test message with a name and values", "key", "value") +func TestConvertValueFloat32(t *testing.T) { + actual := convertValue(float32(3.14)) + expected := log.Float64Value(3.14) - logger.Info("This is a test message with a name and values", "int", 10, "bool", true) + assert.InDelta(t, actual.AsFloat64(), expected.AsFloat64(), 0.0001) } From 9204132fde492a2f13a0974f395e4b89544903ff Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 02:45:35 +1200 Subject: [PATCH 03/21] add pkg docs --- bridges/otellogr/config.go | 82 -------- bridges/otellogr/example_test.go | 20 +- bridges/otellogr/logsink.go | 177 +++++++++++++++++- bridges/otellogr/logsink_test.go | 112 +++++++++-- config/generated_config.go | 8 +- .../driver/testdata/expected/basic/fib.go | 5 +- .../testdata/expected/basic/goroutines.go | 5 +- .../driver/testdata/expected/basic/main.go | 2 +- .../driver/testdata/expected/basic/methods.go | 3 +- .../driver/testdata/expected/basic/package.go | 5 +- .../testdata/expected/interface/app/impl.go | 5 +- .../testdata/expected/interface/main.go | 7 +- .../interface/serializer/interface.go | 2 +- .../driver/testdata/expected/selector/main.go | 3 +- instrgen/driver/testdata/interface/main.go | 2 +- .../otelgrpc/example/api/hello-service.pb.go | 13 +- .../jaeger-idl/proto/api_v2/sampling.pb.go | 7 +- tools/tools.go | 7 +- 18 files changed, 329 insertions(+), 136 deletions(-) delete mode 100644 bridges/otellogr/config.go diff --git a/bridges/otellogr/config.go b/bridges/otellogr/config.go deleted file mode 100644 index e4998ef7eb8..00000000000 --- a/bridges/otellogr/config.go +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" - -import ( - "go.opentelemetry.io/otel/log" - "go.opentelemetry.io/otel/log/global" - "go.opentelemetry.io/otel/sdk/instrumentation" -) - -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 [LogSink]. -// -// By default if this Option is not provided, the LogSink 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 [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 - }) -} diff --git a/bridges/otellogr/example_test.go b/bridges/otellogr/example_test.go index d511a210ad5..d7d9309e477 100644 --- a/bridges/otellogr/example_test.go +++ b/bridges/otellogr/example_test.go @@ -5,8 +5,11 @@ package otellogr_test import ( "github.com/go-logr/logr" - otellogr "go.opentelemetry.io/contrib/bridges/otellogr" + + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/noop" + + otellogr "go.opentelemetry.io/contrib/bridges/otellogr" ) func Example() { @@ -14,5 +17,18 @@ func Example() { provider := noop.NewLoggerProvider() // Create an *slog.Logger with *otelslog.Handler and use it in your application. - logr.New(otellogr.NewLogSink(otellogr.WithLoggerProvider(provider))) + logr.New(otellogr.NewLogSink( + otellogr.WithLoggerProvider(provider), + // Optionally, set the log level severity mapping. + otellogr.WithLevelSeverity(func(i int) log.Severity { + switch i { + case 0: + return log.SeverityInfo + case 1: + return log.SeverityWarn + default: + return log.SeverityFatal + } + })), + ) } diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 08bfaa2c9fb..660f774af12 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -1,6 +1,61 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +// Package otellogr provides [LogSink], an [logr.LogSink] implementation, that +// can be used to bridge between the [github.com/go-logr/logr] API and +// [OpenTelemetry]. +// +// # Record Conversion +// +// The logr records are converted to OpenTelemetry [log.Record] in the following +// way: +// +// - Time is set as the current time of conversion. +// - Message is set as the Body using a [log.StringValue]. +// - Level is transformed and set as the Severity. The SeverityText is not +// set. +// - PC is dropped. +// - KeyAndValues are transformed and set as Attributes. +// - Error is always logged as an additional attribute with the key "err" and +// with the severity [log.SeverityError]. +// - Name is logged as an additional attribute with the key "logger". +// - Context is not propagated to the OpenTelemetry log record, as logr does +// not provide a way to access it. +// +// The Level is transformed by using the [WithLevelSeverity] option. If this is +// not provided it would default to a function that adds an offset to the logr +// such that [logr.Info] is transformed to [log.SeverityInfo]. For example: +// +// - [logr.Info] is transformed to [log.SeverityInfo]. +// - [logr.V(0)] is transformed to [log.SeverityInfo]. +// - [logr.V(1)] is transformed to [log.SeverityInfo2]. +// - [logr.V(2)] is transformed to [log.SeverityInfo3]. +// - ... +// - [logr.V(15)] is transformed to [log.SeverityFatal4]. +// - [logr.Error] is transformed to [log.SeverityError]. +// +// KeysAndValues values are transformed based on their type. The following types are +// supported: +// +// - [bool] are transformed to [log.BoolValue]. +// - [string] are transformed to [log.StringValue]. +// - [int], [int8], [int16], [int32], [int64] are transformed to +// [log.Int64Value]. +// - [uint], [uint8], [uint16], [uint32], [uint64], [uintptr] are transformed +// to [log.Int64Value] or [log.StringValue] if the value is too large. +// - [float32], [float64] are transformed to [log.Float64Value]. +// - [time.Duration] are transformed to [log.Int64Value] with the nanoseconds. +// - [complex64], [complex128] are transformed to [log.StringValue] with the +// - [time.Time] are transformed to [log.Int64Value] with the nanoseconds. +// - [[]byte] are transformed to [log.BytesValue]. +// - [error] are transformed to [log.StringValue] with the error message. +// - [nil] are transformed to an empty [log.Value]. +// - [struct] are transformed to [log.StringValue] with the struct fields. +// - [slice], [array] are transformed to [log.SliceValue] with the elements. +// - [map] are transformed to [log.MapValue] with the key-value pairs. +// - [pointer], [interface] are transformed to the dereferenced value. +// +// [OpenTelemetry]: https://opentelemetry.io/docs/concepts/signals/logs/ package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" import ( @@ -12,18 +67,114 @@ import ( "time" "github.com/go-logr/logr" + "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/global" + "go.opentelemetry.io/otel/sdk/instrumentation" ) const ( bridgeName = "go.opentelemetry.io/contrib/bridges/otellogr" - // nameKey is used to log the `WithName` values as an additional attribute. nameKey = "logger" // errKey is used to log the error parameter of Error as an additional attribute. errKey = "err" ) +type config struct { + provider log.LoggerProvider + scope instrumentation.Scope + levelSeverity func(int) log.Severity +} + +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() + } + + if c.levelSeverity == nil { + c.levelSeverity = func(level int) log.Severity { + const sevOffset = int(log.SeverityInfo) + return log.Severity(level + sevOffset) + } + } + + 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 [LogSink]. +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 [LogSink]. +// +// By default if this Option is not provided, the LogSink 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 [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 + }) +} + +// WithLevelSeverity returns an [Option] that configures the function used to +// convert logr levels to OpenTelemetry log severities. +// +// By default if this Option is not provided, the LogSink will use a default +// conversion function which adds an offset to the logr level to get the +// OpenTelemetry severity. The offset is such that logr.Info("message") +// converts to OpenTelemetry [log.SeverityInfo]. +func WithLevelSeverity(f func(int) log.Severity) Option { + return optFunc(func(c config) config { + c.levelSeverity = f + 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 @@ -31,16 +182,21 @@ const ( func NewLogSink(options ...Option) *LogSink { c := newConfig(options) return &LogSink{ - logger: c.logger(), + logger: c.logger(), + levelSeverity: c.levelSeverity, } } // 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 { - name string - logger log.Logger - values []log.KeyValue + // Ensure forward compatibility by explicitly making this not comparable. + noCmp [0]func() //nolint: unused // This is indeed used. + + name string + logger log.Logger + levelSeverity func(int) log.Severity + values []log.KeyValue } // Compile-time check *Handler implements logr.LogSink. @@ -215,8 +371,15 @@ func convertValue(v any) log.Value { case reflect.Map: kvs := make([]log.KeyValue, 0, val.Len()) for _, k := range val.MapKeys() { + var key string + // If the key is a struct, use %+v to print the struct fields. + if k.Kind() == reflect.Struct { + key = fmt.Sprintf("%+v", k.Interface()) + } else { + key = fmt.Sprintf("%v", k.Interface()) + } kvs = append(kvs, log.KeyValue{ - Key: fmt.Sprintf("%v", k.Interface()), + Key: key, Value: convertValue(val.MapIndex(k).Interface()), }) } @@ -236,6 +399,8 @@ func convertValue(v any) log.Value { return log.StringValue(fmt.Sprintf("unhandled: (%s) %+v", t, v)) } +// convertUintValue converts a uint64 to a log.Value. +// If the value is too large to fit in an int64, it is converted to a string. func convertUintValue(v uint64) log.Value { if v > math.MaxInt64 { return log.StringValue(strconv.FormatUint(v, 10)) diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index a53df7d7e35..a21501616d1 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -1,3 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + package otellogr import ( @@ -9,8 +12,11 @@ import ( "github.com/go-logr/logr" "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" ) @@ -61,6 +67,39 @@ type expectedRecord struct { var now = time.Now() +func TestNewLogSinkConfiguration(t *testing.T) { + t.Run("default", func(t *testing.T) { + r := new(recorder) + global.SetLoggerProvider(r) + + var ls *LogSink + assert.NotPanics(t, func() { ls = NewLogSink() }) + assert.NotNil(t, ls) + require.IsType(t, &recorder{}, ls.logger) + + l := ls.logger.(*recorder) + want := instrumentation.Scope{Name: bridgeName, Version: version} + assert.Equal(t, want, l.Scope) + }) + + t.Run("with_options", func(t *testing.T) { + r := new(recorder) + wantScope := instrumentation.Scope{Name: "name", Version: "ver", SchemaURL: "url"} + var ls *LogSink + assert.NotPanics(t, func() { + ls = NewLogSink( + WithLoggerProvider(r), + WithInstrumentationScope(wantScope), + ) + }) + assert.NotNil(t, ls) + require.IsType(t, &recorder{}, ls.logger) + + l := ls.logger.(*recorder) + assert.Equal(t, wantScope, l.Scope) + }) +} + func TestLogSink(t *testing.T) { for _, tt := range []struct { name string @@ -80,7 +119,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "info-multi-attrs", + name: "info_multi_attrs", f: func(l *logr.Logger) { l.Info("msg", "struct", struct{ data int64 }{data: 1}, @@ -126,7 +165,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "error-multi-attrs", + name: "error_multi_attrs", f: func(l *logr.Logger) { l.Error(errors.New("error"), "msg", "struct", struct{ data int64 }{data: 1}, @@ -158,7 +197,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "info-with-name", + name: "info_with_name", f: func(l *logr.Logger) { l.WithName("test").Info("info message with name") }, @@ -173,7 +212,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "info-with-name-nested", + name: "info_with_name_nested", f: func(l *logr.Logger) { l.WithName("test").WithName("test").Info("info message with name") }, @@ -188,7 +227,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "info-with-attrs", + name: "info_with_attrs", f: func(l *logr.Logger) { l.WithValues("key", "value").Info("info message with attrs") }, @@ -203,7 +242,7 @@ func TestLogSink(t *testing.T) { }, }, { - name: "info-with-attrs-nested", + name: "info_with_attrs_nested", f: func(l *logr.Logger) { l.WithValues("key1", "value1").Info("info message with attrs", "key2", "value2") }, @@ -262,14 +301,14 @@ func TestConvertKVs(t *testing.T) { kvs: nil, }, { - name: "single-value", + name: "single_value", kvs: []any{"key", "value"}, expectedKVs: []log.KeyValue{ log.String("key", "value"), }, }, { - name: "multiple-values", + name: "multiple_values", kvs: []any{"key1", "value1", "key2", "value2"}, expectedKVs: []log.KeyValue{ log.String("key1", "value1"), @@ -277,7 +316,7 @@ func TestConvertKVs(t *testing.T) { }, }, { - name: "missing-value", + name: "missing_value", kvs: []any{"key1", "value1", "key2"}, expectedKVs: []log.KeyValue{ log.String("key1", "value1"), @@ -285,7 +324,7 @@ func TestConvertKVs(t *testing.T) { }, }, { - name: "key-not-string", + name: "key_not_string", kvs: []any{42, "value"}, expectedKVs: []log.KeyValue{ log.String("42", "value"), @@ -427,27 +466,32 @@ func TestConvertValue(t *testing.T) { expectedValue: log.Value{}, }, { - name: "ptrint", + name: "nil_ptr", + value: (*int)(nil), + expectedValue: log.Value{}, + }, + { + name: "int_ptr", value: func() *int { i := 93; return &i }(), expectedValue: log.Int64Value(93), }, { - name: "ptrstring", + name: "string_ptr", value: func() *string { s := "hello"; return &s }(), expectedValue: log.StringValue("hello"), }, { - name: "ptrbool", + name: "bool_ptr", value: func() *bool { b := true; return &b }(), expectedValue: log.BoolValue(true), }, { - name: "int-empty-array", + name: "int_empty_array", value: []int{}, expectedValue: log.SliceValue([]log.Value{}...), }, { - name: "int-array", + name: "int_array", value: []int{1, 2, 3}, expectedValue: log.SliceValue([]log.Value{ log.Int64Value(1), @@ -456,19 +500,37 @@ func TestConvertValue(t *testing.T) { }...), }, { - name: "key-value-map", + name: "key_value_map", value: map[string]int{"one": 1}, expectedValue: log.MapValue( log.Int64("one", 1), ), }, { - name: "int-string-map", + name: "int_string_map", value: map[int]string{1: "one"}, expectedValue: log.MapValue( log.String("1", "one"), ), }, + { + name: "nested_map", + value: map[string]map[string]int{"nested": {"one": 1}}, + expectedValue: log.MapValue( + log.Map("nested", + log.Int64("one", 1), + ), + ), + }, + { + name: "struct_key_map", + value: map[struct{ Name string }]int{ + {Name: "John"}: 42, + }, + expectedValue: log.MapValue( + log.Int64("{Name:John}", 42), + ), + }, { name: "struct", value: struct { @@ -480,6 +542,22 @@ func TestConvertValue(t *testing.T) { }, expectedValue: log.StringValue("{Name:John Age:42}"), }, + { + name: "struct_ptr", + value: &struct { + Name string + Age int + }{ + Name: "John", + Age: 42, + }, + expectedValue: log.StringValue("{Name:John Age:42}"), + }, + { + name: "ctx", + value: context.Background(), + expectedValue: log.StringValue("context.Background"), + }, } { t.Run(tt.name, func(t *testing.T) { assert.Equal(t, convertValue(tt.value), tt.expectedValue) diff --git a/config/generated_config.go b/config/generated_config.go index 68f8a3f9e07..c84f52b28a3 100644 --- a/config/generated_config.go +++ b/config/generated_config.go @@ -2,9 +2,11 @@ package config -import "encoding/json" -import "fmt" -import "reflect" +import ( + "encoding/json" + "fmt" + "reflect" +) type AttributeLimits struct { // AttributeCountLimit corresponds to the JSON schema field diff --git a/instrgen/driver/testdata/expected/basic/fib.go b/instrgen/driver/testdata/expected/basic/fib.go index f5ea42c5251..7e3eb31908c 100644 --- a/instrgen/driver/testdata/expected/basic/fib.go +++ b/instrgen/driver/testdata/expected/basic/fib.go @@ -5,12 +5,13 @@ package main import ( - "fmt" __atel_context "context" + "fmt" + __atel_otel "go.opentelemetry.io/otel" ) -func foo(__atel_tracing_ctx __atel_context.Context,) { +func foo(__atel_tracing_ctx __atel_context.Context) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("foo").Start(__atel_tracing_ctx, "foo") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/goroutines.go b/instrgen/driver/testdata/expected/basic/goroutines.go index 513874d215f..749f9c31b62 100644 --- a/instrgen/driver/testdata/expected/basic/goroutines.go +++ b/instrgen/driver/testdata/expected/basic/goroutines.go @@ -5,12 +5,13 @@ package main import ( - "fmt" __atel_context "context" + "fmt" + __atel_otel "go.opentelemetry.io/otel" ) -func goroutines(__atel_tracing_ctx __atel_context.Context,) { +func goroutines(__atel_tracing_ctx __atel_context.Context) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("goroutines").Start(__atel_tracing_ctx, "goroutines") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/main.go b/instrgen/driver/testdata/expected/basic/main.go index bd6cf7f951a..e9074d9076d 100644 --- a/instrgen/driver/testdata/expected/basic/main.go +++ b/instrgen/driver/testdata/expected/basic/main.go @@ -5,8 +5,8 @@ package main import ( - "fmt" __atel_context "context" + "fmt" "go.opentelemetry.io/contrib/instrgen/rtlib" __atel_otel "go.opentelemetry.io/otel" diff --git a/instrgen/driver/testdata/expected/basic/methods.go b/instrgen/driver/testdata/expected/basic/methods.go index 081d85a2fde..130cac65ad5 100644 --- a/instrgen/driver/testdata/expected/basic/methods.go +++ b/instrgen/driver/testdata/expected/basic/methods.go @@ -6,6 +6,7 @@ package main import ( __atel_context "context" + __atel_otel "go.opentelemetry.io/otel" ) @@ -46,7 +47,7 @@ func (e element) get(__atel_tracing_ctx __atel_context.Context, a int) { defer __atel_span.End() } -func methods(__atel_tracing_ctx __atel_context.Context,) { +func methods(__atel_tracing_ctx __atel_context.Context) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("methods").Start(__atel_tracing_ctx, "methods") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/package.go b/instrgen/driver/testdata/expected/basic/package.go index 1e2e34024d2..f118b41deb0 100644 --- a/instrgen/driver/testdata/expected/basic/package.go +++ b/instrgen/driver/testdata/expected/basic/package.go @@ -5,8 +5,9 @@ package main import ( - "os" __atel_context "context" + "os" + __atel_otel "go.opentelemetry.io/otel" ) @@ -14,7 +15,7 @@ func Close() error { return nil } -func pack(__atel_tracing_ctx __atel_context.Context,) { +func pack(__atel_tracing_ctx __atel_context.Context) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("pack").Start(__atel_tracing_ctx, "pack") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/interface/app/impl.go b/instrgen/driver/testdata/expected/interface/app/impl.go index 9b7bb311626..f5b0c7946a1 100644 --- a/instrgen/driver/testdata/expected/interface/app/impl.go +++ b/instrgen/driver/testdata/expected/interface/app/impl.go @@ -5,15 +5,16 @@ package app import ( - "fmt" __atel_context "context" + "fmt" + __atel_otel "go.opentelemetry.io/otel" ) type BasicSerializer struct { } -func (b BasicSerializer) Serialize(__atel_tracing_ctx __atel_context.Context,) { +func (b BasicSerializer) Serialize(__atel_tracing_ctx __atel_context.Context) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("Serialize").Start(__atel_tracing_ctx, "Serialize") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/interface/main.go b/instrgen/driver/testdata/expected/interface/main.go index 0d33e3e08d7..f3a2417e0de 100644 --- a/instrgen/driver/testdata/expected/interface/main.go +++ b/instrgen/driver/testdata/expected/interface/main.go @@ -5,11 +5,12 @@ package main import ( - . "go.opentelemetry.io/contrib/instrgen/testdata/interface/app" - __atel_otel "go.opentelemetry.io/otel" __atel_context "context" - . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" + "go.opentelemetry.io/contrib/instrgen/rtlib" + . "go.opentelemetry.io/contrib/instrgen/testdata/interface/app" + . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" + __atel_otel "go.opentelemetry.io/otel" ) func main() { diff --git a/instrgen/driver/testdata/expected/interface/serializer/interface.go b/instrgen/driver/testdata/expected/interface/serializer/interface.go index 09f6c131b13..828c84e58ec 100644 --- a/instrgen/driver/testdata/expected/interface/serializer/interface.go +++ b/instrgen/driver/testdata/expected/interface/serializer/interface.go @@ -7,5 +7,5 @@ package serializer import __atel_context "context" type Serializer interface { - Serialize(__atel_tracing_ctx __atel_context.Context,) + Serialize(__atel_tracing_ctx __atel_context.Context) } diff --git a/instrgen/driver/testdata/expected/selector/main.go b/instrgen/driver/testdata/expected/selector/main.go index 74d8c092fd9..0ba1d7c6ded 100644 --- a/instrgen/driver/testdata/expected/selector/main.go +++ b/instrgen/driver/testdata/expected/selector/main.go @@ -5,9 +5,10 @@ package main import ( + __atel_context "context" + "go.opentelemetry.io/contrib/instrgen/rtlib" __atel_otel "go.opentelemetry.io/otel" - __atel_context "context" ) type Driver interface { diff --git a/instrgen/driver/testdata/interface/main.go b/instrgen/driver/testdata/interface/main.go index 88bb4607957..61de3696f2a 100644 --- a/instrgen/driver/testdata/interface/main.go +++ b/instrgen/driver/testdata/interface/main.go @@ -5,9 +5,9 @@ package main import ( + "go.opentelemetry.io/contrib/instrgen/rtlib" . "go.opentelemetry.io/contrib/instrgen/testdata/interface/app" . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" - "go.opentelemetry.io/contrib/instrgen/rtlib" ) func main() { diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go b/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go index e6ec648f829..d44fd569c34 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go @@ -5,20 +5,25 @@ Package api is a generated protocol buffer package. It is generated from these files: + hello-service.proto It has these top-level messages: + HelloRequest HelloResponse */ package api -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - import ( + fmt "fmt" + + proto "github.com/golang/protobuf/proto" + + math "math" + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" ) diff --git a/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go index 5e6714ce30f..0d290f4f082 100644 --- a/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go +++ b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go @@ -6,12 +6,13 @@ package api_v2 import ( encoding_binary "encoding/binary" fmt "fmt" - _ "github.com/gogo/protobuf/gogoproto" - proto "github.com/gogo/protobuf/proto" - _ "google.golang.org/genproto/googleapis/api/annotations" io "io" math "math" math_bits "math/bits" + + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/tools/tools.go b/tools/tools.go index 9e04608cf60..b66e2b6a8e4 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -12,11 +12,12 @@ import ( _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/jcchavezs/porto/cmd/porto" _ "github.com/wadey/gocovmerge" + _ "golang.org/x/exp/cmd/gorelease" + _ "golang.org/x/tools/cmd/stringer" + _ "golang.org/x/vuln/cmd/govulncheck" + _ "go.opentelemetry.io/build-tools/crosslink" _ "go.opentelemetry.io/build-tools/dbotconf" _ "go.opentelemetry.io/build-tools/gotmpl" _ "go.opentelemetry.io/build-tools/multimod" - _ "golang.org/x/exp/cmd/gorelease" - _ "golang.org/x/tools/cmd/stringer" - _ "golang.org/x/vuln/cmd/govulncheck" ) From 26a63c0482ae0e89b0b87da3087ad38fef1820b6 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:00:02 +1200 Subject: [PATCH 04/21] bump deps --- bridges/otellogr/go.mod | 10 +++++----- bridges/otellogr/go.sum | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index 9afd4cbd411..585be8a6087 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -5,16 +5,16 @@ go 1.21 require ( github.com/go-logr/logr v1.4.1 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 + go.opentelemetry.io/otel/log v0.1.0-alpha + go.opentelemetry.io/otel/sdk v1.25.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.24.0 // indirect - go.opentelemetry.io/otel/metric v1.24.0 // indirect - go.opentelemetry.io/otel/trace v1.24.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/metric v1.25.0 // indirect + go.opentelemetry.io/otel/trace v1.25.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 472ab1bec59..47bbf9aa80d 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -13,14 +13,24 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT 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 v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= 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/log v0.1.0-alpha h1:CDbo8tCcR2ACXH4YkJnyLM9URo79LLxxAL1TG2AoACw= +go.opentelemetry.io/otel/log v0.1.0-alpha/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= 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/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= 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/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= 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= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= 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 046f6e0d64e96208080f7de747b86e0ecd8deff5 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:07:52 +1200 Subject: [PATCH 05/21] revert lint other files --- config/generated_config.go | 8 +++----- instrgen/driver/testdata/expected/basic/fib.go | 5 ++--- .../driver/testdata/expected/basic/goroutines.go | 5 ++--- instrgen/driver/testdata/expected/basic/main.go | 2 +- instrgen/driver/testdata/expected/basic/methods.go | 3 +-- instrgen/driver/testdata/expected/basic/package.go | 5 ++--- .../driver/testdata/expected/interface/app/impl.go | 5 ++--- instrgen/driver/testdata/expected/interface/main.go | 7 +++---- .../expected/interface/serializer/interface.go | 2 +- instrgen/driver/testdata/expected/selector/main.go | 3 +-- instrgen/driver/testdata/interface/main.go | 2 +- .../grpc/otelgrpc/example/api/hello-service.pb.go | 13 ++++--------- .../jaeger-idl/proto/api_v2/sampling.pb.go | 7 +++---- tools/tools.go | 7 +++---- 14 files changed, 29 insertions(+), 45 deletions(-) diff --git a/config/generated_config.go b/config/generated_config.go index c84f52b28a3..68f8a3f9e07 100644 --- a/config/generated_config.go +++ b/config/generated_config.go @@ -2,11 +2,9 @@ package config -import ( - "encoding/json" - "fmt" - "reflect" -) +import "encoding/json" +import "fmt" +import "reflect" type AttributeLimits struct { // AttributeCountLimit corresponds to the JSON schema field diff --git a/instrgen/driver/testdata/expected/basic/fib.go b/instrgen/driver/testdata/expected/basic/fib.go index 7e3eb31908c..f5ea42c5251 100644 --- a/instrgen/driver/testdata/expected/basic/fib.go +++ b/instrgen/driver/testdata/expected/basic/fib.go @@ -5,13 +5,12 @@ package main import ( - __atel_context "context" "fmt" - + __atel_context "context" __atel_otel "go.opentelemetry.io/otel" ) -func foo(__atel_tracing_ctx __atel_context.Context) { +func foo(__atel_tracing_ctx __atel_context.Context,) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("foo").Start(__atel_tracing_ctx, "foo") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/goroutines.go b/instrgen/driver/testdata/expected/basic/goroutines.go index 749f9c31b62..513874d215f 100644 --- a/instrgen/driver/testdata/expected/basic/goroutines.go +++ b/instrgen/driver/testdata/expected/basic/goroutines.go @@ -5,13 +5,12 @@ package main import ( - __atel_context "context" "fmt" - + __atel_context "context" __atel_otel "go.opentelemetry.io/otel" ) -func goroutines(__atel_tracing_ctx __atel_context.Context) { +func goroutines(__atel_tracing_ctx __atel_context.Context,) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("goroutines").Start(__atel_tracing_ctx, "goroutines") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/main.go b/instrgen/driver/testdata/expected/basic/main.go index e9074d9076d..bd6cf7f951a 100644 --- a/instrgen/driver/testdata/expected/basic/main.go +++ b/instrgen/driver/testdata/expected/basic/main.go @@ -5,8 +5,8 @@ package main import ( - __atel_context "context" "fmt" + __atel_context "context" "go.opentelemetry.io/contrib/instrgen/rtlib" __atel_otel "go.opentelemetry.io/otel" diff --git a/instrgen/driver/testdata/expected/basic/methods.go b/instrgen/driver/testdata/expected/basic/methods.go index 130cac65ad5..081d85a2fde 100644 --- a/instrgen/driver/testdata/expected/basic/methods.go +++ b/instrgen/driver/testdata/expected/basic/methods.go @@ -6,7 +6,6 @@ package main import ( __atel_context "context" - __atel_otel "go.opentelemetry.io/otel" ) @@ -47,7 +46,7 @@ func (e element) get(__atel_tracing_ctx __atel_context.Context, a int) { defer __atel_span.End() } -func methods(__atel_tracing_ctx __atel_context.Context) { +func methods(__atel_tracing_ctx __atel_context.Context,) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("methods").Start(__atel_tracing_ctx, "methods") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/basic/package.go b/instrgen/driver/testdata/expected/basic/package.go index f118b41deb0..1e2e34024d2 100644 --- a/instrgen/driver/testdata/expected/basic/package.go +++ b/instrgen/driver/testdata/expected/basic/package.go @@ -5,9 +5,8 @@ package main import ( - __atel_context "context" "os" - + __atel_context "context" __atel_otel "go.opentelemetry.io/otel" ) @@ -15,7 +14,7 @@ func Close() error { return nil } -func pack(__atel_tracing_ctx __atel_context.Context) { +func pack(__atel_tracing_ctx __atel_context.Context,) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("pack").Start(__atel_tracing_ctx, "pack") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/interface/app/impl.go b/instrgen/driver/testdata/expected/interface/app/impl.go index f5b0c7946a1..9b7bb311626 100644 --- a/instrgen/driver/testdata/expected/interface/app/impl.go +++ b/instrgen/driver/testdata/expected/interface/app/impl.go @@ -5,16 +5,15 @@ package app import ( - __atel_context "context" "fmt" - + __atel_context "context" __atel_otel "go.opentelemetry.io/otel" ) type BasicSerializer struct { } -func (b BasicSerializer) Serialize(__atel_tracing_ctx __atel_context.Context) { +func (b BasicSerializer) Serialize(__atel_tracing_ctx __atel_context.Context,) { __atel_child_tracing_ctx, __atel_span := __atel_otel.Tracer("Serialize").Start(__atel_tracing_ctx, "Serialize") _ = __atel_child_tracing_ctx defer __atel_span.End() diff --git a/instrgen/driver/testdata/expected/interface/main.go b/instrgen/driver/testdata/expected/interface/main.go index f3a2417e0de..0d33e3e08d7 100644 --- a/instrgen/driver/testdata/expected/interface/main.go +++ b/instrgen/driver/testdata/expected/interface/main.go @@ -5,12 +5,11 @@ package main import ( - __atel_context "context" - - "go.opentelemetry.io/contrib/instrgen/rtlib" . "go.opentelemetry.io/contrib/instrgen/testdata/interface/app" - . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" __atel_otel "go.opentelemetry.io/otel" + __atel_context "context" + . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" + "go.opentelemetry.io/contrib/instrgen/rtlib" ) func main() { diff --git a/instrgen/driver/testdata/expected/interface/serializer/interface.go b/instrgen/driver/testdata/expected/interface/serializer/interface.go index 828c84e58ec..09f6c131b13 100644 --- a/instrgen/driver/testdata/expected/interface/serializer/interface.go +++ b/instrgen/driver/testdata/expected/interface/serializer/interface.go @@ -7,5 +7,5 @@ package serializer import __atel_context "context" type Serializer interface { - Serialize(__atel_tracing_ctx __atel_context.Context) + Serialize(__atel_tracing_ctx __atel_context.Context,) } diff --git a/instrgen/driver/testdata/expected/selector/main.go b/instrgen/driver/testdata/expected/selector/main.go index 0ba1d7c6ded..74d8c092fd9 100644 --- a/instrgen/driver/testdata/expected/selector/main.go +++ b/instrgen/driver/testdata/expected/selector/main.go @@ -5,10 +5,9 @@ package main import ( - __atel_context "context" - "go.opentelemetry.io/contrib/instrgen/rtlib" __atel_otel "go.opentelemetry.io/otel" + __atel_context "context" ) type Driver interface { diff --git a/instrgen/driver/testdata/interface/main.go b/instrgen/driver/testdata/interface/main.go index 61de3696f2a..88bb4607957 100644 --- a/instrgen/driver/testdata/interface/main.go +++ b/instrgen/driver/testdata/interface/main.go @@ -5,9 +5,9 @@ package main import ( - "go.opentelemetry.io/contrib/instrgen/rtlib" . "go.opentelemetry.io/contrib/instrgen/testdata/interface/app" . "go.opentelemetry.io/contrib/instrgen/testdata/interface/serializer" + "go.opentelemetry.io/contrib/instrgen/rtlib" ) func main() { diff --git a/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go b/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go index d44fd569c34..e6ec648f829 100644 --- a/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go +++ b/instrumentation/google.golang.org/grpc/otelgrpc/example/api/hello-service.pb.go @@ -5,25 +5,20 @@ Package api is a generated protocol buffer package. It is generated from these files: - hello-service.proto It has these top-level messages: - HelloRequest HelloResponse */ package api -import ( - fmt "fmt" - - proto "github.com/golang/protobuf/proto" - - math "math" +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import ( context "golang.org/x/net/context" - grpc "google.golang.org/grpc" ) diff --git a/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go index 0d290f4f082..5e6714ce30f 100644 --- a/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go +++ b/samplers/jaegerremote/internal/proto-gen/jaeger-idl/proto/api_v2/sampling.pb.go @@ -6,13 +6,12 @@ package api_v2 import ( encoding_binary "encoding/binary" fmt "fmt" - io "io" - math "math" - math_bits "math/bits" - _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" _ "google.golang.org/genproto/googleapis/api/annotations" + io "io" + math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. diff --git a/tools/tools.go b/tools/tools.go index b66e2b6a8e4..9e04608cf60 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -12,12 +12,11 @@ import ( _ "github.com/golangci/golangci-lint/cmd/golangci-lint" _ "github.com/jcchavezs/porto/cmd/porto" _ "github.com/wadey/gocovmerge" - _ "golang.org/x/exp/cmd/gorelease" - _ "golang.org/x/tools/cmd/stringer" - _ "golang.org/x/vuln/cmd/govulncheck" - _ "go.opentelemetry.io/build-tools/crosslink" _ "go.opentelemetry.io/build-tools/dbotconf" _ "go.opentelemetry.io/build-tools/gotmpl" _ "go.opentelemetry.io/build-tools/multimod" + _ "golang.org/x/exp/cmd/gorelease" + _ "golang.org/x/tools/cmd/stringer" + _ "golang.org/x/vuln/cmd/govulncheck" ) From 55371c64744367bb989547c847deea356cb302d7 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:10:58 +1200 Subject: [PATCH 06/21] Add changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b79826cdb05..dfcc289de16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added - Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108) +- The `go.opentelemetry.io/contrib/bridges/otellogr` module. + This module provides an OpenTelemetry logging bridge for "github.com/go-logr/logr". (#5357) ## [1.25.0/0.50.0/0.19.0/0.5.0/0.0.1] - 2024-04-05 From 2ea0523171465918a2a851a01a77339ecf624db5 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:20:56 +1200 Subject: [PATCH 07/21] fix lint go.sum + yml --- .github/dependabot.yml | 4 ++-- bridges/otellogr/go.sum | 10 ---------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 3e6cbd5f723..bdc1e73c3fb 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -101,7 +101,7 @@ updates: interval: weekly day: sunday - package-ecosystem: gomod - directory: /bridges/otelslog + directory: /bridges/otellogr labels: - dependencies - go @@ -110,7 +110,7 @@ updates: interval: weekly day: sunday - package-ecosystem: gomod - directory: /bridges/otellogr + directory: /bridges/otelslog labels: - dependencies - go diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 47bbf9aa80d..33b2d50bd65 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -11,24 +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.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= -go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -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/log v0.1.0-alpha h1:CDbo8tCcR2ACXH4YkJnyLM9URo79LLxxAL1TG2AoACw= go.opentelemetry.io/otel/log v0.1.0-alpha/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= -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/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -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/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -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= go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= From 099241e74010de92222194217ad73e691f669953 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:28:04 +1200 Subject: [PATCH 08/21] fix comments and add to versions.yaml --- bridges/otellogr/example_test.go | 3 +-- bridges/otellogr/version.go | 2 +- versions.yaml | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/otellogr/example_test.go b/bridges/otellogr/example_test.go index d7d9309e477..8812c94dedd 100644 --- a/bridges/otellogr/example_test.go +++ b/bridges/otellogr/example_test.go @@ -5,7 +5,6 @@ package otellogr_test import ( "github.com/go-logr/logr" - "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/noop" @@ -16,7 +15,7 @@ func Example() { // Use a working LoggerProvider implementation instead e.g. using go.opentelemetry.io/otel/sdk/log. provider := noop.NewLoggerProvider() - // Create an *slog.Logger with *otelslog.Handler and use it in your application. + // Create an logr.Logger with *otellogr.LogSink and use it in your application. logr.New(otellogr.NewLogSink( otellogr.WithLoggerProvider(provider), // Optionally, set the log level severity mapping. diff --git a/bridges/otellogr/version.go b/bridges/otellogr/version.go index c0931956f91..f37bce04c32 100644 --- a/bridges/otellogr/version.go +++ b/bridges/otellogr/version.go @@ -3,5 +3,5 @@ package otellogr // import "go.opentelemetry.io/contrib/bridges/otellogr" -// version is the current release version of otelslog in use. +// version is the current release version of otellogr in use. const version = "0.0.1-alpha" diff --git a/versions.yaml b/versions.yaml index 0513ed39d41..520fff43c4b 100644 --- a/versions.yaml +++ b/versions.yaml @@ -76,6 +76,7 @@ module-sets: experimental-bridge: version: v0.0.1 modules: + - go.opentelemetry.io/contrib/bridges/otellogr - go.opentelemetry.io/contrib/bridges/otelslog excluded-modules: - go.opentelemetry.io/contrib/instrgen From 53d1fc71ff99e94608ed30749f1740079687327e Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:33:10 +1200 Subject: [PATCH 09/21] fix goimports --- bridges/otellogr/example_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/otellogr/example_test.go b/bridges/otellogr/example_test.go index 8812c94dedd..a8cb8d66cf8 100644 --- a/bridges/otellogr/example_test.go +++ b/bridges/otellogr/example_test.go @@ -5,6 +5,7 @@ package otellogr_test import ( "github.com/go-logr/logr" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/noop" From 232a4fb35b1918ccbfd29829d5a98f79221584e9 Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 03:39:23 +1200 Subject: [PATCH 10/21] Update CODEOWNERS --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 01ae4b69a99..c1b347ce5bb 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,6 +22,7 @@ CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole +bridges/otellogr/ @open-telemetry/go-approvers bridges/prometheus/ @open-telemetry/go-approvers @dashpole config/ @open-telemetry/go-approvers @MadVikingGod @pellared @codeboten From 24246ebbccbc86341ea6c9fa31b68396a68637cd Mon Sep 17 00:00:00 2001 From: sync-common-bot Date: Wed, 10 Apr 2024 11:19:55 +1200 Subject: [PATCH 11/21] fix levelSeverity logic --- bridges/otellogr/logsink.go | 9 ++------- bridges/otellogr/logsink_test.go | 5 +++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 660f774af12..582ace70129 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -246,17 +246,12 @@ func (l *LogSink) Enabled(level int) bool { // Error logs an error, with the given message and key/value pairs. func (l *LogSink) Error(err error, msg string, keysAndValues ...any) { - const severity = log.SeverityError - - l.log(err, msg, severity, keysAndValues...) + l.log(err, msg, log.SeverityError, keysAndValues...) } // Info logs a non-error message with the given key/value pairs. func (l *LogSink) Info(level int, msg string, keysAndValues ...any) { - const sevOffset = int(log.SeverityInfo) - severity := log.Severity(sevOffset + level) - - l.log(nil, msg, severity, keysAndValues...) + l.log(nil, msg, l.levelSeverity(level), keysAndValues...) } // Init receives optional information about the logr library this diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index a21501616d1..008bf370e02 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -90,10 +90,15 @@ func TestNewLogSinkConfiguration(t *testing.T) { ls = NewLogSink( WithLoggerProvider(r), WithInstrumentationScope(wantScope), + WithLevelSeverity(func(i int) log.Severity { + return log.SeverityFatal + }), ) }) assert.NotNil(t, ls) require.IsType(t, &recorder{}, ls.logger) + assert.NotNil(t, ls.levelSeverity) + assert.Equal(t, log.SeverityFatal, ls.levelSeverity(0)) l := ls.logger.(*recorder) assert.Equal(t, wantScope, l.Scope) From 14ba92171769605c07313bbce7b11840a1f0d1ad Mon Sep 17 00:00:00 2001 From: CZ Date: Fri, 12 Apr 2024 03:39:22 +1200 Subject: [PATCH 12/21] use logtest pkg for testing --- bridges/otellogr/go.mod | 2 +- bridges/otellogr/go.sum | 4 +- bridges/otellogr/logsink.go | 3 +- bridges/otellogr/logsink_test.go | 92 +++++++++++--------------------- bridges/otelslog/example_test.go | 1 + 5 files changed, 37 insertions(+), 65 deletions(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index 585be8a6087..cdb82278cb5 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/go-logr/logr v1.4.1 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel/log v0.1.0-alpha + go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158 go.opentelemetry.io/otel/sdk v1.25.0 ) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 33b2d50bd65..42b5c84c8d8 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -13,8 +13,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/log v0.1.0-alpha h1:CDbo8tCcR2ACXH4YkJnyLM9URo79LLxxAL1TG2AoACw= -go.opentelemetry.io/otel/log v0.1.0-alpha/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= +go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158 h1:D0WQp5qfyLmPp9xY1s83pJcFDHTxIKo74AaAYEdwW4U= +go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 582ace70129..312b5eeff2b 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -238,8 +238,7 @@ func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...a // verbosity and disable some info logs. func (l *LogSink) Enabled(level int) bool { var record log.Record - const sevOffset = int(log.SeverityDebug) - record.SetSeverity(log.Severity(level + sevOffset)) + record.SetSeverity(l.levelSeverity(level)) ctx := context.Background() return l.logger.Enabled(ctx, record) } diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index 008bf370e02..ca18d01812c 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -15,50 +15,11 @@ import ( "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/log/logtest" "go.opentelemetry.io/otel/sdk/instrumentation" ) -// 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. - - // Records are the records emitted. - Records []log.Record - - // 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...) - - r.Scope = instrumentation.Scope{ - Name: name, - Version: cfg.InstrumentationVersion(), - SchemaURL: cfg.SchemaURL(), - } - return r -} - -func (r *recorder) Enabled(ctx context.Context, record log.Record) bool { - return record.Severity() >= r.MinSeverity -} - -func (r *recorder) Emit(_ context.Context, record log.Record) { - r.Records = append(r.Records, record) -} - type expectedRecord struct { Body log.Value Severity log.Severity @@ -69,26 +30,26 @@ var now = time.Now() func TestNewLogSinkConfiguration(t *testing.T) { t.Run("default", func(t *testing.T) { - r := new(recorder) - global.SetLoggerProvider(r) + rec := logtest.NewRecorder() + global.SetLoggerProvider(rec) var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink() }) assert.NotNil(t, ls) - require.IsType(t, &recorder{}, ls.logger) + require.IsType(t, &logtest.Recorder{}, ls.logger) - l := ls.logger.(*recorder) - want := instrumentation.Scope{Name: bridgeName, Version: version} - assert.Equal(t, want, l.Scope) + l := ls.logger.(*logtest.Recorder) + assert.Equal(t, version, l.Result()[0].Version) + assert.Equal(t, bridgeName, l.Result()[0].Name) }) t.Run("with_options", func(t *testing.T) { - r := new(recorder) + rec := logtest.NewRecorder() wantScope := instrumentation.Scope{Name: "name", Version: "ver", SchemaURL: "url"} var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink( - WithLoggerProvider(r), + WithLoggerProvider(rec), WithInstrumentationScope(wantScope), WithLevelSeverity(func(i int) log.Severity { return log.SeverityFatal @@ -96,12 +57,9 @@ func TestNewLogSinkConfiguration(t *testing.T) { ) }) assert.NotNil(t, ls) - require.IsType(t, &recorder{}, ls.logger) + require.IsType(t, &logtest.Recorder{}, ls.logger) assert.NotNil(t, ls.levelSeverity) assert.Equal(t, log.SeverityFatal, ls.levelSeverity(0)) - - l := ls.logger.(*recorder) - assert.Equal(t, wantScope, l.Scope) }) } @@ -264,13 +222,14 @@ func TestLogSink(t *testing.T) { }, } { t.Run(tt.name, func(t *testing.T) { - r := new(recorder) - ls := NewLogSink(WithLoggerProvider(r)) + rec := logtest.NewRecorder() + ls := NewLogSink(WithLoggerProvider(rec)) l := logr.New(ls) tt.f(&l) - assert.Len(t, r.Records, len(tt.expectedRecords)) - for i, record := range r.Records { + require.Len(t, rec.Result(), 2) + assert.Len(t, rec.Result()[1].Records, len(tt.expectedRecords)) + for i, record := range rec.Result()[1].Records { assert.Equal(t, tt.expectedRecords[i].Body, record.Body()) assert.Equal(t, tt.expectedRecords[i].Severity, record.Severity()) @@ -286,12 +245,25 @@ func TestLogSink(t *testing.T) { } func TestLogSinkEnabled(t *testing.T) { - r := new(recorder) - ls := NewLogSink(WithLoggerProvider(r)) + rec := logtest.NewRecorder( + logtest.WithEnabledFunc(func(ctx context.Context, record log.Record) bool { + return record.Severity() == log.SeverityInfo + }), + ) + ls := NewLogSink( + WithLoggerProvider(rec), + WithLevelSeverity(func(i int) log.Severity { + switch i { + case 0: + return log.SeverityDebug + default: + return log.SeverityInfo + } + }), + ) + assert.False(t, ls.Enabled(0)) assert.True(t, ls.Enabled(1)) - assert.True(t, ls.Enabled(0)) - assert.False(t, ls.Enabled(-10)) } func TestConvertKVs(t *testing.T) { diff --git a/bridges/otelslog/example_test.go b/bridges/otelslog/example_test.go index b260ee3b168..c4dc1636b06 100644 --- a/bridges/otelslog/example_test.go +++ b/bridges/otelslog/example_test.go @@ -5,6 +5,7 @@ package otelslog_test import ( "go.opentelemetry.io/contrib/bridges/otelslog" + "go.opentelemetry.io/otel/log/noop" ) From c7a8258fe3ef508c98781844770ac8d33126b691 Mon Sep 17 00:00:00 2001 From: CZ Date: Sun, 14 Apr 2024 17:47:49 +1200 Subject: [PATCH 13/21] Update bridges/otelslog/example_test.go Co-authored-by: Damien Mathieu <42@dmathieu.com> --- bridges/otelslog/example_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/bridges/otelslog/example_test.go b/bridges/otelslog/example_test.go index c4dc1636b06..b260ee3b168 100644 --- a/bridges/otelslog/example_test.go +++ b/bridges/otelslog/example_test.go @@ -5,7 +5,6 @@ package otelslog_test import ( "go.opentelemetry.io/contrib/bridges/otelslog" - "go.opentelemetry.io/otel/log/noop" ) From 174a92c6590fc6f90b89cb3ad2f9c8c32985353b Mon Sep 17 00:00:00 2001 From: CZ Date: Thu, 25 Apr 2024 02:00:00 +1200 Subject: [PATCH 14/21] Fix WithName, ctx propagation and errorKey --- bridges/otellogr/logsink.go | 48 +++++++++++++++++++++----------- bridges/otellogr/logsink_test.go | 34 ++++++++++++++++++++-- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 312b5eeff2b..2f156a90f4a 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -19,8 +19,10 @@ // - Error is always logged as an additional attribute with the key "err" and // with the severity [log.SeverityError]. // - Name is logged as an additional attribute with the key "logger". -// - Context is not propagated to the OpenTelemetry log record, as logr does -// not provide a way to access it. +// - The [context.Context] value in KeyAndValues is propagated to OpenTelemetry +// log record. All non-nested [context.Context] values are ignored and not +// added as attributes. If there are multiple [context.Context] the last one +// is used. // // The Level is transformed by using the [WithLevelSeverity] option. If this is // not provided it would default to a function that adds an offset to the logr @@ -77,8 +79,8 @@ const ( bridgeName = "go.opentelemetry.io/contrib/bridges/otellogr" // nameKey is used to log the `WithName` values as an additional attribute. nameKey = "logger" - // errKey is used to log the error parameter of Error as an additional attribute. - errKey = "err" + // errorKey is used to log the error parameter of Error as an additional attribute. + errorKey = "error" ) type config struct { @@ -215,7 +217,7 @@ func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...a if err != nil { record.AddAttributes(log.KeyValue{ - Key: errKey, + Key: errorKey, Value: convertValue(err), }) } @@ -224,12 +226,11 @@ func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...a record.AddAttributes(l.values...) } - kv := convertKVs(kvList) + ctx, kv := convertKVs(kvList) if len(kv) > 0 { record.AddAttributes(kv...) } - ctx := context.Background() l.logger.Emit(ctx, record) } @@ -263,23 +264,30 @@ func (l *LogSink) Init(info logr.RuntimeInfo) { // WithName returns a new LogSink with the specified name appended. func (l LogSink) WithName(name string) logr.LogSink { - if len(l.name) > 0 { - l.name += "/" + newLogger := l + + if len(newLogger.name) > 0 { + newLogger.name += "/" } - l.name += name - return &l + newLogger.name += name + + return &newLogger } // WithValues returns a new LogSink with additional key/value pairs. func (l LogSink) WithValues(keysAndValues ...any) logr.LogSink { - attrs := convertKVs(keysAndValues) + _, attrs := convertKVs(keysAndValues) l.values = append(l.values, attrs...) return &l } -func convertKVs(keysAndValues []any) []log.KeyValue { +// convertKVs converts a list of key-value pairs to a list of [log.KeyValue]. +// The last [context.Context] value is returned as the context. +func convertKVs(keysAndValues []any) (context.Context, []log.KeyValue) { + ctx := context.Background() + if len(keysAndValues) == 0 { - return nil + return ctx, nil } if len(keysAndValues)%2 != 0 { // Ensure an odd number of items here does not corrupt the list @@ -293,12 +301,20 @@ func convertKVs(keysAndValues []any) []log.KeyValue { // Ensure that the key is a string k = fmt.Sprintf("%v", keysAndValues[i]) } + + v := keysAndValues[i+1] + if vCtx, ok := v.(context.Context); ok { + // Special case when a field is of context.Context type. + ctx = vCtx + continue + } + kv = append(kv, log.KeyValue{ Key: k, - Value: convertValue(keysAndValues[i+1]), + Value: convertValue(v), }) } - return kv + return ctx, kv } func convertValue(v any) log.Value { diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index ca18d01812c..4827115f623 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -122,7 +122,7 @@ func TestLogSink(t *testing.T) { Body: log.StringValue("error message"), Severity: log.SeverityError, Attributes: []log.KeyValue{ - log.String(errKey, "test error"), + log.String(errorKey, "test error"), }, }, }, @@ -146,7 +146,7 @@ func TestLogSink(t *testing.T) { Body: log.StringValue("msg"), Severity: log.SeverityError, Attributes: []log.KeyValue{ - log.String(errKey, "error"), + log.String(errorKey, "error"), log.String("struct", "{data:1}"), log.Bool("bool", true), log.Int64("duration", 60_000_000_000), @@ -244,6 +244,15 @@ func TestLogSink(t *testing.T) { } } +func TestLogSinkWithName(t *testing.T) { + rec := logtest.NewRecorder() + ls := NewLogSink(WithLoggerProvider(rec)) + lsWithName := ls.WithName("test") + require.NotEqual(t, ls, lsWithName) + + require.NotEqual(t, lsWithName, ls.WithName("test")) +} + func TestLogSinkEnabled(t *testing.T) { rec := logtest.NewRecorder( logtest.WithEnabledFunc(func(ctx context.Context, record log.Record) bool { @@ -267,11 +276,14 @@ func TestLogSinkEnabled(t *testing.T) { } func TestConvertKVs(t *testing.T) { + ctx := context.WithValue(context.Background(), "key", "value") // nolint: revive,staticcheck // test context + for _, tt := range []struct { name string kvs []any expectedKVs []log.KeyValue + expectedCtx context.Context }{ { name: "empty", @@ -307,10 +319,26 @@ func TestConvertKVs(t *testing.T) { log.String("42", "value"), }, }, + { + name: "context", + kvs: []any{"ctx", ctx, "key", "value"}, + expectedKVs: []log.KeyValue{log.String("key", "value")}, + expectedCtx: ctx, + }, + { + name: "last_context", + kvs: []any{"key", context.Background(), "ctx", ctx}, + expectedKVs: []log.KeyValue{}, + expectedCtx: ctx, + }, } { t.Run(tt.name, func(t *testing.T) { - kvs := convertKVs(tt.kvs) + ctx, kvs := convertKVs(tt.kvs) assert.Equal(t, tt.expectedKVs, kvs) + + if tt.expectedCtx != nil { + assert.Equal(t, tt.expectedCtx, ctx) + } }) } } From 3a25ec9a167f863f9b77079f294f837d38361d66 Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 24 Jun 2024 14:36:21 +1200 Subject: [PATCH 15/21] attempt to add new config --- CODEOWNERS | 2 +- bridges/otellogr/logsink.go | 23 +++++++++-------------- bridges/otellogr/logsink_test.go | 13 ++++++------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index c1b347ce5bb..c85d7c87154 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,7 +22,7 @@ CODEOWNERS @MrAlias @MadVikingGod @pellared @dashpole -bridges/otellogr/ @open-telemetry/go-approvers +bridges/otellogr/ @open-telemetry/go-approvers @scorpionknifes bridges/prometheus/ @open-telemetry/go-approvers @dashpole config/ @open-telemetry/go-approvers @MadVikingGod @pellared @codeboten diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index 2f156a90f4a..a0c8484a0da 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -69,7 +69,6 @@ import ( "time" "github.com/go-logr/logr" - "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/sdk/instrumentation" @@ -77,8 +76,6 @@ import ( const ( bridgeName = "go.opentelemetry.io/contrib/bridges/otellogr" - // nameKey is used to log the `WithName` values as an additional attribute. - nameKey = "logger" // errorKey is used to log the error parameter of Error as an additional attribute. errorKey = "error" ) @@ -184,6 +181,7 @@ func WithLevelSeverity(f func(int) log.Severity) Option { func NewLogSink(options ...Option) *LogSink { c := newConfig(options) return &LogSink{ + config: c, logger: c.logger(), levelSeverity: c.levelSeverity, } @@ -195,7 +193,7 @@ type LogSink struct { // Ensure forward compatibility by explicitly making this not comparable. noCmp [0]func() //nolint: unused // This is indeed used. - name string + config config logger log.Logger levelSeverity func(int) log.Severity values []log.KeyValue @@ -211,10 +209,6 @@ func (l *LogSink) log(err error, msg string, serverity log.Severity, kvList ...a record.SetBody(log.StringValue(msg)) record.SetSeverity(serverity) - if l.name != "" { - record.AddAttributes(log.String(nameKey, l.name)) - } - if err != nil { record.AddAttributes(log.KeyValue{ Key: errorKey, @@ -264,14 +258,15 @@ func (l *LogSink) Init(info logr.RuntimeInfo) { // WithName returns a new LogSink with the specified name appended. func (l LogSink) WithName(name string) logr.LogSink { - newLogger := l + newConfig := l.config + newConfig.scope.Name = fmt.Sprintf("%s/%s", l.config.scope.Name, name) - if len(newLogger.name) > 0 { - newLogger.name += "/" + return &LogSink{ + config: newConfig, + logger: newConfig.logger(), + levelSeverity: newConfig.levelSeverity, + values: l.values, } - newLogger.name += name - - return &newLogger } // WithValues returns a new LogSink with additional key/value pairs. diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index 4827115f623..e907a8df2a0 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -13,7 +13,6 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/logtest" @@ -168,9 +167,9 @@ func TestLogSink(t *testing.T) { { Body: log.StringValue("info message with name"), Severity: log.SeverityInfo, - Attributes: []log.KeyValue{ - log.String(nameKey, "test"), - }, + // Attributes: []log.KeyValue{ + // log.String(nameKey, "test"), + // }, }, }, }, @@ -183,9 +182,9 @@ func TestLogSink(t *testing.T) { { Body: log.StringValue("info message with name"), Severity: log.SeverityInfo, - Attributes: []log.KeyValue{ - log.String(nameKey, "test/test"), - }, + // Attributes: []log.KeyValue{ + // log.String(nameKey, "test/test"), + // }, }, }, }, From 49dbb635d2416e22ba7a7c8c02d19bb2c90aa13a Mon Sep 17 00:00:00 2001 From: CZ Date: Sun, 30 Jun 2024 17:12:50 +1200 Subject: [PATCH 16/21] Make WithName create new logger --- bridges/otellogr/logsink_test.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index e907a8df2a0..fd8327e0526 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -64,15 +64,17 @@ func TestNewLogSinkConfiguration(t *testing.T) { func TestLogSink(t *testing.T) { for _, tt := range []struct { - name string - f func(*logr.Logger) - expectedRecords []expectedRecord + name string + f func(*logr.Logger) + expectedLoggerCount int + expectedRecords []expectedRecord }{ { name: "info", f: func(l *logr.Logger) { l.Info("info message") }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message"), @@ -94,6 +96,7 @@ func TestLogSink(t *testing.T) { "uint64", uint64(3), ) }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("msg"), @@ -116,6 +119,7 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.Error(errors.New("test error"), "error message") }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("error message"), @@ -140,6 +144,7 @@ func TestLogSink(t *testing.T) { "uint64", uint64(3), ) }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("msg"), @@ -163,13 +168,11 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithName("test").Info("info message with name") }, + expectedLoggerCount: 2, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with name"), Severity: log.SeverityInfo, - // Attributes: []log.KeyValue{ - // log.String(nameKey, "test"), - // }, }, }, }, @@ -178,13 +181,11 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithName("test").WithName("test").Info("info message with name") }, + expectedLoggerCount: 3, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with name"), Severity: log.SeverityInfo, - // Attributes: []log.KeyValue{ - // log.String(nameKey, "test/test"), - // }, }, }, }, @@ -193,6 +194,7 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithValues("key", "value").Info("info message with attrs") }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with attrs"), @@ -208,6 +210,7 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithValues("key1", "value1").Info("info message with attrs", "key2", "value2") }, + expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with attrs"), @@ -226,9 +229,10 @@ func TestLogSink(t *testing.T) { l := logr.New(ls) tt.f(&l) - require.Len(t, rec.Result(), 2) - assert.Len(t, rec.Result()[1].Records, len(tt.expectedRecords)) - for i, record := range rec.Result()[1].Records { + require.Len(t, rec.Result(), tt.expectedLoggerCount+1) + + assert.Len(t, rec.Result()[tt.expectedLoggerCount].Records, len(tt.expectedRecords)) + for i, record := range rec.Result()[tt.expectedLoggerCount].Records { assert.Equal(t, tt.expectedRecords[i].Body, record.Body()) assert.Equal(t, tt.expectedRecords[i].Severity, record.Severity()) From cfa60ac0d521374bbeacafbd133d8d27d5e14c81 Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 1 Jul 2024 08:29:11 +1000 Subject: [PATCH 17/21] Fix tests --- bridges/otellogr/logsink.go | 1 + bridges/otellogr/logsink_test.go | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/bridges/otellogr/logsink.go b/bridges/otellogr/logsink.go index a0c8484a0da..1b545c696eb 100644 --- a/bridges/otellogr/logsink.go +++ b/bridges/otellogr/logsink.go @@ -69,6 +69,7 @@ import ( "time" "github.com/go-logr/logr" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/sdk/instrumentation" diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index fd8327e0526..1a60c2f1ef9 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -13,6 +13,7 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/logtest" @@ -35,11 +36,6 @@ func TestNewLogSinkConfiguration(t *testing.T) { var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink() }) assert.NotNil(t, ls) - require.IsType(t, &logtest.Recorder{}, ls.logger) - - l := ls.logger.(*logtest.Recorder) - assert.Equal(t, version, l.Result()[0].Version) - assert.Equal(t, bridgeName, l.Result()[0].Name) }) t.Run("with_options", func(t *testing.T) { @@ -56,7 +52,6 @@ func TestNewLogSinkConfiguration(t *testing.T) { ) }) assert.NotNil(t, ls) - require.IsType(t, &logtest.Recorder{}, ls.logger) assert.NotNil(t, ls.levelSeverity) assert.Equal(t, log.SeverityFatal, ls.levelSeverity(0)) }) @@ -229,10 +224,10 @@ func TestLogSink(t *testing.T) { l := logr.New(ls) tt.f(&l) - require.Len(t, rec.Result(), tt.expectedLoggerCount+1) + require.Len(t, rec.Result(), tt.expectedLoggerCount) - assert.Len(t, rec.Result()[tt.expectedLoggerCount].Records, len(tt.expectedRecords)) - for i, record := range rec.Result()[tt.expectedLoggerCount].Records { + assert.Len(t, rec.Result()[tt.expectedLoggerCount-1].Records, len(tt.expectedRecords)) + for i, record := range rec.Result()[tt.expectedLoggerCount-1].Records { assert.Equal(t, tt.expectedRecords[i].Body, record.Body()) assert.Equal(t, tt.expectedRecords[i].Severity, record.Severity()) From d0aba474d7e76fbf50cd9aefed385bc47c8995f8 Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 1 Jul 2024 08:39:22 +1000 Subject: [PATCH 18/21] fix tests by removing global logger --- bridges/otellogr/logsink_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index 1a60c2f1ef9..74973fe2c3f 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -13,9 +13,7 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel/log" - "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/logtest" "go.opentelemetry.io/otel/sdk/instrumentation" ) @@ -30,9 +28,6 @@ var now = time.Now() func TestNewLogSinkConfiguration(t *testing.T) { t.Run("default", func(t *testing.T) { - rec := logtest.NewRecorder() - global.SetLoggerProvider(rec) - var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink() }) assert.NotNil(t, ls) From df429b12ce0820a6d918533e772f22533967938f Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 1 Jul 2024 08:40:03 +1000 Subject: [PATCH 19/21] fix lint --- bridges/otellogr/logsink_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index 74973fe2c3f..045c2358945 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -13,6 +13,7 @@ import ( "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/log" "go.opentelemetry.io/otel/log/logtest" "go.opentelemetry.io/otel/sdk/instrumentation" From d280f94372e4d35369e45f702f871a17214e1a6f Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 1 Jul 2024 08:57:31 +1000 Subject: [PATCH 20/21] Fix tests and add with_name test --- bridges/otellogr/logsink_test.go | 54 ++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/bridges/otellogr/logsink_test.go b/bridges/otellogr/logsink_test.go index 045c2358945..8661b858acd 100644 --- a/bridges/otellogr/logsink_test.go +++ b/bridges/otellogr/logsink_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel/log" + "go.opentelemetry.io/otel/log/global" "go.opentelemetry.io/otel/log/logtest" "go.opentelemetry.io/otel/sdk/instrumentation" ) @@ -29,19 +30,30 @@ var now = time.Now() func TestNewLogSinkConfiguration(t *testing.T) { t.Run("default", func(t *testing.T) { + rec := logtest.NewRecorder() + prev := global.GetLoggerProvider() + defer global.SetLoggerProvider(prev) + global.SetLoggerProvider(rec) + var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink() }) assert.NotNil(t, ls) + want := &logtest.ScopeRecords{ + Name: bridgeName, + Version: version, + } + got := rec.Result()[0] + assert.Equal(t, want, got) }) t.Run("with_options", func(t *testing.T) { rec := logtest.NewRecorder() - wantScope := instrumentation.Scope{Name: "name", Version: "ver", SchemaURL: "url"} + scope := instrumentation.Scope{Name: "name", Version: "ver", SchemaURL: "url"} var ls *LogSink assert.NotPanics(t, func() { ls = NewLogSink( WithLoggerProvider(rec), - WithInstrumentationScope(wantScope), + WithInstrumentationScope(scope), WithLevelSeverity(func(i int) log.Severity { return log.SeverityFatal }), @@ -50,6 +62,30 @@ func TestNewLogSinkConfiguration(t *testing.T) { assert.NotNil(t, ls) assert.NotNil(t, ls.levelSeverity) assert.Equal(t, log.SeverityFatal, ls.levelSeverity(0)) + + want := &logtest.ScopeRecords{ + Name: "name", + Version: "ver", + SchemaURL: "url", + } + got := rec.Result()[0] + assert.Equal(t, want, got) + }) + + t.Run("with_name", func(t *testing.T) { + rec := logtest.NewRecorder() + assert.NotPanics(t, func() { + NewLogSink( + WithLoggerProvider(rec), + ).WithName("test") + }) + + want := &logtest.ScopeRecords{ + Name: bridgeName + "/test", + Version: version, + } + got := rec.Result()[1] + assert.Equal(t, want, got) }) } @@ -65,7 +101,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.Info("info message") }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message"), @@ -87,7 +122,6 @@ func TestLogSink(t *testing.T) { "uint64", uint64(3), ) }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("msg"), @@ -110,7 +144,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.Error(errors.New("test error"), "error message") }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("error message"), @@ -135,7 +168,6 @@ func TestLogSink(t *testing.T) { "uint64", uint64(3), ) }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("msg"), @@ -159,7 +191,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithName("test").Info("info message with name") }, - expectedLoggerCount: 2, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with name"), @@ -172,7 +203,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithName("test").WithName("test").Info("info message with name") }, - expectedLoggerCount: 3, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with name"), @@ -185,7 +215,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithValues("key", "value").Info("info message with attrs") }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with attrs"), @@ -201,7 +230,6 @@ func TestLogSink(t *testing.T) { f: func(l *logr.Logger) { l.WithValues("key1", "value1").Info("info message with attrs", "key2", "value2") }, - expectedLoggerCount: 1, expectedRecords: []expectedRecord{ { Body: log.StringValue("info message with attrs"), @@ -220,10 +248,10 @@ func TestLogSink(t *testing.T) { l := logr.New(ls) tt.f(&l) - require.Len(t, rec.Result(), tt.expectedLoggerCount) + last := len(rec.Result()) - 1 - assert.Len(t, rec.Result()[tt.expectedLoggerCount-1].Records, len(tt.expectedRecords)) - for i, record := range rec.Result()[tt.expectedLoggerCount-1].Records { + assert.Len(t, rec.Result()[last].Records, len(tt.expectedRecords)) + for i, record := range rec.Result()[last].Records { assert.Equal(t, tt.expectedRecords[i].Body, record.Body()) assert.Equal(t, tt.expectedRecords[i].Severity, record.Severity()) From 41b18957f367a9fe80a6a080178fced1a12073c8 Mon Sep 17 00:00:00 2001 From: CZ Date: Mon, 1 Jul 2024 09:24:44 +1000 Subject: [PATCH 21/21] bump dependencies --- bridges/otellogr/go.mod | 12 ++++++------ bridges/otellogr/go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/bridges/otellogr/go.mod b/bridges/otellogr/go.mod index cdb82278cb5..772c1b10bc2 100644 --- a/bridges/otellogr/go.mod +++ b/bridges/otellogr/go.mod @@ -3,18 +3,18 @@ module go.opentelemetry.io/contrib/bridges/otellogr go 1.21 require ( - github.com/go-logr/logr v1.4.1 + github.com/go-logr/logr v1.4.2 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158 - go.opentelemetry.io/otel/sdk v1.25.0 + go.opentelemetry.io/otel/log v0.3.0 + go.opentelemetry.io/otel/sdk v1.27.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.25.0 // indirect - go.opentelemetry.io/otel/metric v1.25.0 // indirect - go.opentelemetry.io/otel/trace v1.25.0 // indirect + go.opentelemetry.io/otel v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/bridges/otellogr/go.sum b/bridges/otellogr/go.sum index 42b5c84c8d8..8c405763b58 100644 --- a/bridges/otellogr/go.sum +++ b/bridges/otellogr/go.sum @@ -1,8 +1,8 @@ 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/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= @@ -11,16 +11,16 @@ 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.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= -go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= -go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158 h1:D0WQp5qfyLmPp9xY1s83pJcFDHTxIKo74AaAYEdwW4U= -go.opentelemetry.io/otel/log v0.1.0-alpha.0.20240409140646-648b40eae158/go.mod h1:B4xaNmGRNECaOqny/Z7lym0i/p/apNGF8e/9KStHWp0= -go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= -go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= -go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= -go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= -go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= -go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs= +go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= 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=