Skip to content

Commit e9ba478

Browse files
otellogr: Implement LevelSeverity
1 parent aaa6d68 commit e9ba478

File tree

3 files changed

+117
-21
lines changed

3 files changed

+117
-21
lines changed

bridges/otellogr/example_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/go-logr/logr"
88

99
"go.opentelemetry.io/contrib/bridges/otellogr"
10+
"go.opentelemetry.io/otel/log"
1011
"go.opentelemetry.io/otel/log/noop"
1112
)
1213

@@ -17,6 +18,17 @@ func Example() {
1718
// Create an logr.Logger with *otellogr.LogSink and use it in your application.
1819
logr.New(otellogr.NewLogSink(
1920
"my/pkg/name",
20-
otellogr.WithLoggerProvider(provider)),
21-
)
21+
otellogr.WithLoggerProvider(provider),
22+
// Optionally, set the log level severity mapping.
23+
otellogr.WithLevelSeverity(func(i int) log.Severity {
24+
switch i {
25+
case 0:
26+
return log.SeverityInfo
27+
case 1:
28+
return log.SeverityWarn
29+
default:
30+
return log.SeverityFatal
31+
}
32+
}),
33+
))
2234
}

bridges/otellogr/logsink.go

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,26 @@
1010
// way:
1111
//
1212
// - Message is set as the Body using a [log.StringValue].
13-
// - TODO: Level
13+
// - Level is transformed and set as the Severity. The SeverityText is not
14+
// set.
1415
// - KeyAndValues are transformed and set as Attributes.
1516
// - The [context.Context] value in KeyAndValues is propagated to OpenTelemetry
1617
// log record. All non-nested [context.Context] values are ignored and not
1718
// added as attributes. If there are multiple [context.Context] the last one
1819
// is used.
1920
//
21+
// The Level is transformed by using the [WithLevelSeverity] option. If this is
22+
// not provided it would default to a function that adds an offset to the logr
23+
// such that [logr.Info] is transformed to [log.SeverityInfo]. For example:
24+
//
25+
// - [logr.Info] is transformed to [log.SeverityInfo].
26+
// - [logr.V(0)] is transformed to [log.SeverityInfo].
27+
// - [logr.V(1)] is transformed to [log.SeverityInfo2].
28+
// - [logr.V(2)] is transformed to [log.SeverityInfo3].
29+
// - ...
30+
// - [logr.V(15)] is transformed to [log.SeverityFatal4].
31+
// - [logr.Error] is transformed to [log.SeverityError].
32+
//
2033
// KeysAndValues values are transformed based on their type. The following types are
2134
// supported:
2235
//
@@ -56,6 +69,8 @@ type config struct {
5669
provider log.LoggerProvider
5770
version string
5871
schemaURL string
72+
73+
levelSeverity func(int) log.Severity
5974
}
6075

6176
func newConfig(options []Option) config {
@@ -68,6 +83,13 @@ func newConfig(options []Option) config {
6883
c.provider = global.GetLoggerProvider()
6984
}
7085

86+
if c.levelSeverity == nil {
87+
c.levelSeverity = func(level int) log.Severity {
88+
const sevOffset = int(log.SeverityInfo)
89+
return log.Severity(level + sevOffset)
90+
}
91+
}
92+
7193
return c
7294
}
7395

@@ -112,6 +134,20 @@ func WithLoggerProvider(provider log.LoggerProvider) Option {
112134
})
113135
}
114136

137+
// WithLevelSeverity returns an [Option] that configures the function used to
138+
// convert logr levels to OpenTelemetry log severities.
139+
//
140+
// By default if this Option is not provided, the LogSink will use a default
141+
// conversion function which adds an offset to the logr level to get the
142+
// OpenTelemetry severity. The offset is such that logr.Info("message")
143+
// converts to OpenTelemetry [log.SeverityInfo].
144+
func WithLevelSeverity(f func(int) log.Severity) Option {
145+
return optFunc(func(c config) config {
146+
c.levelSeverity = f
147+
return c
148+
})
149+
}
150+
115151
// NewLogSink returns a new [LogSink] to be used as a [logr.LogSink].
116152
//
117153
// If [WithLoggerProvider] is not provided, the returned [LogSink] will use the
@@ -128,10 +164,11 @@ func NewLogSink(name string, options ...Option) *LogSink {
128164
}
129165

130166
return &LogSink{
131-
name: name,
132-
provider: c.provider,
133-
logger: c.provider.Logger(name, opts...),
134-
opts: opts,
167+
name: name,
168+
provider: c.provider,
169+
logger: c.provider.Logger(name, opts...),
170+
levelSeverity: c.levelSeverity,
171+
opts: opts,
135172
}
136173
}
137174

@@ -141,12 +178,13 @@ type LogSink struct {
141178
// Ensure forward compatibility by explicitly making this not comparable.
142179
noCmp [0]func() //nolint: unused // This is indeed used.
143180

144-
name string
145-
provider log.LoggerProvider
146-
logger log.Logger
147-
opts []log.LoggerOption
148-
attr []log.KeyValue
149-
ctx context.Context
181+
name string
182+
provider log.LoggerProvider
183+
logger log.Logger
184+
levelSeverity func(int) log.Severity
185+
opts []log.LoggerOption
186+
attr []log.KeyValue
187+
ctx context.Context
150188
}
151189

152190
// Compile-time check *Handler implements logr.LogSink.
@@ -156,8 +194,10 @@ var _ logr.LogSink = (*LogSink)(nil)
156194
// For example, commandline flags might be used to set the logging
157195
// verbosity and disable some info logs.
158196
func (l *LogSink) Enabled(level int) bool {
159-
// TODO
160-
return true
197+
var param log.EnabledParameters
198+
param.SetSeverity(l.levelSeverity(level))
199+
ctx := context.Background()
200+
return l.logger.Enabled(ctx, param)
161201
}
162202

163203
// Error logs an error, with the given message and key/value pairs.
@@ -169,7 +209,7 @@ func (l *LogSink) Error(err error, msg string, keysAndValues ...any) {
169209
func (l *LogSink) Info(level int, msg string, keysAndValues ...any) {
170210
var record log.Record
171211
record.SetBody(log.StringValue(msg))
172-
record.SetSeverity(log.SeverityInfo) // TODO: level
212+
record.SetSeverity(l.levelSeverity(level))
173213

174214
record.AddAttributes(l.attr...)
175215

bridges/otellogr/logsink_test.go

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ func TestNewConfig(t *testing.T) {
6363
},
6464
} {
6565
t.Run(tt.name, func(t *testing.T) {
66-
assert.Equal(t, tt.wantConfig, newConfig(tt.options))
66+
config := newConfig(tt.options)
67+
config.levelSeverity = nil // Ignore asserting level severity function, assert.Equal does not support function comparison
68+
assert.Equal(t, tt.wantConfig, config)
6769
})
6870
}
6971
}
@@ -116,9 +118,10 @@ func TestLogSink(t *testing.T) {
116118
const name = "name"
117119

118120
for _, tt := range []struct {
119-
name string
120-
f func(*logr.Logger)
121-
wantRecords map[string][]log.Record
121+
name string
122+
f func(*logr.Logger)
123+
levelSeverity func(int) log.Severity
124+
wantRecords map[string][]log.Record
122125
}{
123126
{
124127
name: "no_log",
@@ -138,6 +141,44 @@ func TestLogSink(t *testing.T) {
138141
},
139142
},
140143
},
144+
{
145+
name: "info_with_level_severity",
146+
f: func(l *logr.Logger) {
147+
l.V(1).Info("msg")
148+
l.V(4).Info("msg")
149+
},
150+
wantRecords: map[string][]log.Record{
151+
name: {
152+
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityInfo2, nil),
153+
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityWarn, nil),
154+
},
155+
},
156+
},
157+
{
158+
name: "info_with_custom_level_severity",
159+
f: func(l *logr.Logger) {
160+
l.Info("msg")
161+
l.V(1).Info("msg")
162+
l.V(2).Info("msg")
163+
},
164+
levelSeverity: func(level int) log.Severity {
165+
switch level {
166+
case 1:
167+
return log.SeverityError
168+
case 2:
169+
return log.SeverityWarn
170+
default:
171+
return log.SeverityInfo
172+
}
173+
},
174+
wantRecords: map[string][]log.Record{
175+
name: {
176+
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityInfo, nil),
177+
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityError, nil),
178+
buildRecord(log.StringValue("msg"), time.Time{}, log.SeverityWarn, nil),
179+
},
180+
},
181+
},
141182
{
142183
name: "info_multi_attrs",
143184
f: func(l *logr.Logger) {
@@ -219,7 +260,10 @@ func TestLogSink(t *testing.T) {
219260
} {
220261
t.Run(tt.name, func(t *testing.T) {
221262
rec := logtest.NewRecorder()
222-
ls := NewLogSink(name, WithLoggerProvider(rec))
263+
ls := NewLogSink(name,
264+
WithLoggerProvider(rec),
265+
WithLevelSeverity(tt.levelSeverity),
266+
)
223267
l := logr.New(ls)
224268
tt.f(&l)
225269

0 commit comments

Comments
 (0)