Skip to content

Commit

Permalink
Merge branch 'main' into otelmongo-bump-semconv
Browse files Browse the repository at this point in the history
  • Loading branch information
MrAlias authored Oct 28, 2024
2 parents 4b30d5d + 1379d74 commit 6ba0d60
Show file tree
Hide file tree
Showing 24 changed files with 249 additions and 62 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
For example, `[]string{"foo", "bar"}` attribute value is now transformed to `log.SliceValue(log.StringValue("foo"), log.StringValue("bar"))` instead of `log.String("[foo bar"])`. (#6254)
- Add the `WithSource` option to the `go.opentelemetry.io/contrib/bridges/otelslog` log bridge to set the `code.*` attributes in the log record that includes the source location where the record was emitted. (#6253)
- Add `ContextWithStartTime` and `StartTimeFromContext` to `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`, which allows setting the start time using go context. (#6137)
- Set the `code.*` attributes in `go.opentelemetry.io/contrib/bridges/otelzap` if the `zap.Logger` was created with the `AddCaller` or `AddStacktrace` option. (#6268)

### Changed

Expand Down
16 changes: 14 additions & 2 deletions bridges/otellogr/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/go-logr/logr"

"go.opentelemetry.io/contrib/bridges/otellogr"
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/noop"
)

Expand All @@ -17,6 +18,17 @@ func Example() {
// Create an logr.Logger with *otellogr.LogSink and use it in your application.
logr.New(otellogr.NewLogSink(
"my/pkg/name",
otellogr.WithLoggerProvider(provider)),
)
otellogr.WithLoggerProvider(provider),
// Optionally, set the log level severity mapping.
otellogr.WithLevelSeverity(func(level int) log.Severity {
switch level {
case 0:
return log.SeverityInfo
case 1:
return log.SeverityDebug
default:
return log.SeverityTrace
}
}),
))
}
71 changes: 57 additions & 14 deletions bridges/otellogr/logsink.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,21 @@
// way:
//
// - Message is set as the Body using a [log.StringValue].
// - TODO: Level
// - Level is transformed and set as the Severity. The SeverityText is not
// set.
// - KeyAndValues are transformed and set as Attributes.
// - 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 V-level is transformed by using the [WithLevelSeverity] option. If option is
// not provided then V-level is transformed in the following way:
//
// - logr.Info and logr.V(0) are transformed to [log.SeverityInfo].
// - logr.V(1) is transformed to [log.SeverityDebug].
// - logr.V(2) and higher are transformed to [log.SeverityTrace].
//
// KeysAndValues values are transformed based on their type. The following types are
// supported:
//
Expand Down Expand Up @@ -57,6 +65,8 @@ type config struct {
provider log.LoggerProvider
version string
schemaURL string

levelSeverity func(int) log.Severity
}

func newConfig(options []Option) config {
Expand All @@ -69,6 +79,19 @@ func newConfig(options []Option) config {
c.provider = global.GetLoggerProvider()
}

if c.levelSeverity == nil {
c.levelSeverity = func(level int) log.Severity {
switch level {
case 0:
return log.SeverityInfo
case 1:
return log.SeverityDebug
default:
return log.SeverityTrace
}
}
}

return c
}

Expand Down Expand Up @@ -113,6 +136,22 @@ func WithLoggerProvider(provider log.LoggerProvider) Option {
})
}

// 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 that transforms in the following way:
//
// - logr.Info and logr.V(0) are transformed to [log.SeverityInfo].
// - logr.V(1) is transformed to [log.SeverityDebug].
// - logr.V(2) and higher are transformed to [log.SeverityTrace].
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
Expand All @@ -129,10 +168,11 @@ func NewLogSink(name string, options ...Option) *LogSink {
}

return &LogSink{
name: name,
provider: c.provider,
logger: c.provider.Logger(name, opts...),
opts: opts,
name: name,
provider: c.provider,
logger: c.provider.Logger(name, opts...),
levelSeverity: c.levelSeverity,
opts: opts,
}
}

Expand All @@ -142,12 +182,13 @@ type LogSink struct {
// Ensure forward compatibility by explicitly making this not comparable.
noCmp [0]func() //nolint: unused // This is indeed used.

name string
provider log.LoggerProvider
logger log.Logger
opts []log.LoggerOption
attr []log.KeyValue
ctx context.Context
name string
provider log.LoggerProvider
logger log.Logger
levelSeverity func(int) log.Severity
opts []log.LoggerOption
attr []log.KeyValue
ctx context.Context
}

// Compile-time check *Handler implements logr.LogSink.
Expand All @@ -157,8 +198,10 @@ var _ logr.LogSink = (*LogSink)(nil)
// For example, commandline flags might be used to set the logging
// verbosity and disable some info logs.
func (l *LogSink) Enabled(level int) bool {
// TODO
return true
var param log.EnabledParameters
param.SetSeverity(l.levelSeverity(level))
ctx := context.Background()
return l.logger.Enabled(ctx, param)
}

// Error logs an error, with the given message and key/value pairs.
Expand All @@ -170,7 +213,7 @@ func (l *LogSink) Error(err error, msg string, keysAndValues ...any) {
func (l *LogSink) Info(level int, msg string, keysAndValues ...any) {
var record log.Record
record.SetBody(log.StringValue(msg))
record.SetSeverity(log.SeverityInfo) // TODO: level
record.SetSeverity(l.levelSeverity(level))

record.AddAttributes(l.attr...)

Expand Down
85 changes: 80 additions & 5 deletions bridges/otellogr/logsink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ func TestNewConfig(t *testing.T) {
},
} {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.wantConfig, newConfig(tt.options))
config := newConfig(tt.options)
config.levelSeverity = nil // Ignore asserting level severity function, assert.Equal does not support function comparison
assert.Equal(t, tt.wantConfig, config)
})
}
}
Expand Down Expand Up @@ -117,9 +119,10 @@ func TestLogSink(t *testing.T) {
const name = "name"

for _, tt := range []struct {
name string
f func(*logr.Logger)
wantRecords map[string][]log.Record
name string
f func(*logr.Logger)
levelSeverity func(int) log.Severity
wantRecords map[string][]log.Record
}{
{
name: "no_log",
Expand All @@ -139,6 +142,48 @@ func TestLogSink(t *testing.T) {
},
},
},
{
name: "info_with_level_severity",
f: func(l *logr.Logger) {
l.V(0).Info("msg")
l.V(1).Info("msg")
l.V(2).Info("msg")
l.V(3).Info("msg")
},
wantRecords: map[string][]log.Record{
name: {
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityInfo, nil),
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityDebug, nil),
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityTrace, nil),
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityTrace, nil),
},
},
},
{
name: "info_with_custom_level_severity",
f: func(l *logr.Logger) {
l.Info("msg")
l.V(1).Info("msg")
l.V(2).Info("msg")
},
levelSeverity: func(level int) log.Severity {
switch level {
case 1:
return log.SeverityError
case 2:
return log.SeverityWarn
default:
return log.SeverityInfo
}
},
wantRecords: map[string][]log.Record{
name: {
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityInfo, nil),
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityError, nil),
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityWarn, nil),
},
},
},
{
name: "info_multi_attrs",
f: func(l *logr.Logger) {
Expand Down Expand Up @@ -235,7 +280,10 @@ func TestLogSink(t *testing.T) {
} {
t.Run(tt.name, func(t *testing.T) {
rec := logtest.NewRecorder()
ls := NewLogSink(name, WithLoggerProvider(rec))
ls := NewLogSink(name,
WithLoggerProvider(rec),
WithLevelSeverity(tt.levelSeverity),
)
l := logr.New(ls)
tt.f(&l)

Expand All @@ -260,6 +308,33 @@ func TestLogSink(t *testing.T) {
}
}

func TestLogSinkEnabled(t *testing.T) {
enabledFunc := func(ctx context.Context, param log.EnabledParameters) bool {
lvl, ok := param.Severity()
if !ok {
return true
}
return lvl == log.SeverityInfo
}

rec := logtest.NewRecorder(logtest.WithEnabledFunc(enabledFunc))
ls := NewLogSink(
"name",
WithLoggerProvider(rec),
WithLevelSeverity(func(i int) log.Severity {
switch i {
case 0:
return log.SeverityInfo
default:
return log.SeverityDebug
}
}),
)

assert.True(t, ls.Enabled(0))
assert.False(t, ls.Enabled(1))
}

func buildRecord(body log.Value, timestamp time.Time, severity log.Severity, attrs []log.KeyValue) log.Record {
var record log.Record
record.SetBody(body)
Expand Down
11 changes: 11 additions & 0 deletions bridges/otelzap/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (

"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/global"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

type config struct {
Expand Down Expand Up @@ -200,6 +201,16 @@ func (o *Core) Write(ent zapcore.Entry, fields []zapcore.Field) error {
r.SetSeverityText(ent.Level.String())

r.AddAttributes(o.attr...)
if ent.Caller.Defined {
r.AddAttributes(
log.String(string(semconv.CodeFilepathKey), ent.Caller.File),
log.Int(string(semconv.CodeLineNumberKey), ent.Caller.Line),
log.String(string(semconv.CodeFunctionKey), ent.Caller.Function),
)
}
if ent.Stack != "" {
r.AddAttributes(log.String(string(semconv.CodeStacktraceKey), ent.Stack))
}
if len(fields) > 0 {
ctx, attrbuf := convertField(fields)
if ctx != nil {
Expand Down
45 changes: 45 additions & 0 deletions bridges/otelzap/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/global"
"go.opentelemetry.io/otel/log/logtest"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
)

var (
Expand Down Expand Up @@ -164,6 +165,50 @@ func TestCoreEnabled(t *testing.T) {
assert.Equal(t, zap.InfoLevel.String(), got.SeverityText())
}

func TestCoreWithCaller(t *testing.T) {
rec := logtest.NewRecorder()
zc := NewCore(loggerName, WithLoggerProvider(rec))
logger := zap.New(zc, zap.AddCaller())

logger.Info(testMessage)
got := rec.Result()[0].Records[0]
assert.Equal(t, testMessage, got.Body().AsString())
assert.Equal(t, log.SeverityInfo, got.Severity())
assert.Equal(t, zap.InfoLevel.String(), got.SeverityText())
assert.Equal(t, 3, got.AttributesLen())
got.WalkAttributes(func(kv log.KeyValue) bool {
switch kv.Key {
case string(semconv.CodeFilepathKey):
assert.Contains(t, kv.Value.AsString(), "core_test.go")
case string(semconv.CodeLineNumberKey):
assert.Positive(t, kv.Value.AsInt64())
case string(semconv.CodeFunctionKey):
assert.Contains(t, kv.Value.AsString(), "TestCoreWithCaller")
default:
assert.Fail(t, "unexpected attribute key", kv.Key)
}
return true
})
}

func TestCoreWithStacktrace(t *testing.T) {
rec := logtest.NewRecorder()
zc := NewCore(loggerName, WithLoggerProvider(rec))
logger := zap.New(zc, zap.AddStacktrace(zapcore.ErrorLevel))

logger.Error(testMessage)
got := rec.Result()[0].Records[0]
assert.Equal(t, testMessage, got.Body().AsString())
assert.Equal(t, log.SeverityError, got.Severity())
assert.Equal(t, zap.ErrorLevel.String(), got.SeverityText())
assert.Equal(t, 1, got.AttributesLen())
got.WalkAttributes(func(kv log.KeyValue) bool {
assert.Equal(t, string(semconv.CodeStacktraceKey), kv.Key)
assert.NotEmpty(t, kv.Value.AsString())
return true
})
}

func TestNewCoreConfiguration(t *testing.T) {
t.Run("Default", func(t *testing.T) {
r := logtest.NewRecorder()
Expand Down
2 changes: 1 addition & 1 deletion bridges/otelzap/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.22

require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/log v0.7.0
go.uber.org/zap v1.27.0
)
Expand All @@ -13,7 +14,6 @@ require (
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
Expand Down
2 changes: 1 addition & 1 deletion bridges/prometheus/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/kr/text v0.2.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.60.0 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
Expand Down
Loading

0 comments on commit 6ba0d60

Please sign in to comment.