Skip to content

Commit ebd0ade

Browse files
dmathieupellared
andauthored
Split log/logtest into a recorder and a logger (#5365)
The current logtest.Recorder implementation is wrong. We have a single `Recorder`, which acts as both a `LoggerProvider`, and a `Logger`, making it possible to emit a log entry with the root recorder, which shouldn't be possible with the API. This change introduces a new private struct, `logger` that acts as the recording logger, while `Recorder` becomes only a LoggerProvider and not a Logger anymore. Closes #5357. --------- Co-authored-by: Robert Pająk <pellared@hotmail.com>
1 parent 0d1e77c commit ebd0ade

File tree

3 files changed

+60
-54
lines changed

3 files changed

+60
-54
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
3737
- Fix the empty output of `go.opentelemetry.io/otel/log.Value` in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog`. (#5311)
3838
- Comparison of unordered maps in `go.opentelemetry.io/otel/log.KeyValue` and `go.opentelemetry.io/otel/log.Value`. (#5306)
3939
- Fix wrong package name of the error message when parsing endpoint URL in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#5371)
40+
- Split the behavior of `Recorder` in `go.opentelemetry.io/otel/log/logtest` so it behaves as a `LoggerProvider` only. (#5365)
4041

4142
## [1.26.0/0.48.0/0.2.0-alpha] 2024-04-24
4243

log/logtest/recorder.go

+46-42
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ import (
1111
"go.opentelemetry.io/otel/log/embedded"
1212
)
1313

14-
// embeddedLogger is a type alias so the embedded.Logger type doesn't conflict
15-
// with the Logger method of the Recorder when it is embedded.
16-
type embeddedLogger = embedded.Logger // nolint:unused // Used below.
17-
1814
type enabledFn func(context.Context, log.Record) bool
1915

2016
var defaultEnabledFunc = func(context.Context, log.Record) bool {
@@ -57,11 +53,8 @@ func WithEnabledFunc(fn func(context.Context, log.Record) bool) Option {
5753
func NewRecorder(options ...Option) *Recorder {
5854
cfg := newConfig(options)
5955

60-
sr := &ScopeRecords{}
61-
6256
return &Recorder{
63-
currentScopeRecord: sr,
64-
enabledFn: cfg.enabledFn,
57+
enabledFn: cfg.enabledFn,
6558
}
6659
}
6760

@@ -82,12 +75,9 @@ type ScopeRecords struct {
8275
// in-memory.
8376
type Recorder struct {
8477
embedded.LoggerProvider
85-
embeddedLogger // nolint:unused // Used to embed embedded.Logger.
86-
87-
mu sync.Mutex
8878

89-
loggers []*Recorder
90-
currentScopeRecord *ScopeRecords
79+
mu sync.Mutex
80+
loggers []*logger
9181

9282
// enabledFn decides whether the recorder should enable logging of a record or not
9383
enabledFn enabledFn
@@ -98,41 +88,24 @@ type Recorder struct {
9888
func (r *Recorder) Logger(name string, opts ...log.LoggerOption) log.Logger {
9989
cfg := log.NewLoggerConfig(opts...)
10090

101-
nr := &Recorder{
102-
currentScopeRecord: &ScopeRecords{
91+
nl := &logger{
92+
scopeRecord: &ScopeRecords{
10393
Name: name,
10494
Version: cfg.InstrumentationVersion(),
10595
SchemaURL: cfg.SchemaURL(),
10696
},
10797
enabledFn: r.enabledFn,
10898
}
109-
r.addChildLogger(nr)
110-
111-
return nr
112-
}
113-
114-
func (r *Recorder) addChildLogger(nr *Recorder) {
115-
r.mu.Lock()
116-
defer r.mu.Unlock()
117-
118-
r.loggers = append(r.loggers, nr)
119-
}
120-
121-
// Enabled indicates whether a specific record should be stored.
122-
func (r *Recorder) Enabled(ctx context.Context, record log.Record) bool {
123-
if r.enabledFn == nil {
124-
return defaultEnabledFunc(ctx, record)
125-
}
99+
r.addChildLogger(nl)
126100

127-
return r.enabledFn(ctx, record)
101+
return nl
128102
}
129103

130-
// Emit stores the log record.
131-
func (r *Recorder) Emit(_ context.Context, record log.Record) {
104+
func (r *Recorder) addChildLogger(nl *logger) {
132105
r.mu.Lock()
133106
defer r.mu.Unlock()
134107

135-
r.currentScopeRecord.Records = append(r.currentScopeRecord.Records, record)
108+
r.loggers = append(r.loggers, nl)
136109
}
137110

138111
// Result returns the current in-memory recorder log records.
@@ -141,22 +114,53 @@ func (r *Recorder) Result() []*ScopeRecords {
141114
defer r.mu.Unlock()
142115

143116
ret := []*ScopeRecords{}
144-
ret = append(ret, r.currentScopeRecord)
145117
for _, l := range r.loggers {
146-
ret = append(ret, l.Result()...)
118+
ret = append(ret, l.scopeRecord)
147119
}
148120
return ret
149121
}
150122

151-
// Reset clears the in-memory log records.
123+
// Reset clears the in-memory log records for all loggers.
152124
func (r *Recorder) Reset() {
153125
r.mu.Lock()
154126
defer r.mu.Unlock()
155127

156-
if r.currentScopeRecord != nil {
157-
r.currentScopeRecord.Records = nil
158-
}
159128
for _, l := range r.loggers {
160129
l.Reset()
161130
}
162131
}
132+
133+
type logger struct {
134+
embedded.Logger
135+
136+
mu sync.Mutex
137+
scopeRecord *ScopeRecords
138+
139+
// enabledFn decides whether the recorder should enable logging of a record or not.
140+
enabledFn enabledFn
141+
}
142+
143+
// Enabled indicates whether a specific record should be stored.
144+
func (l *logger) Enabled(ctx context.Context, record log.Record) bool {
145+
if l.enabledFn == nil {
146+
return defaultEnabledFunc(ctx, record)
147+
}
148+
149+
return l.enabledFn(ctx, record)
150+
}
151+
152+
// Emit stores the log record.
153+
func (l *logger) Emit(_ context.Context, record log.Record) {
154+
l.mu.Lock()
155+
defer l.mu.Unlock()
156+
157+
l.scopeRecord.Records = append(l.scopeRecord.Records, record)
158+
}
159+
160+
// Reset clears the in-memory log records.
161+
func (l *logger) Reset() {
162+
l.mu.Lock()
163+
defer l.mu.Unlock()
164+
165+
l.scopeRecord.Records = nil
166+
}

log/logtest/recorder_test.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ func TestRecorderLogger(t *testing.T) {
2626
{
2727
name: "provides a default logger",
2828

29-
wantLogger: &Recorder{
30-
currentScopeRecord: &ScopeRecords{},
29+
wantLogger: &logger{
30+
scopeRecord: &ScopeRecords{},
3131
},
3232
},
3333
{
@@ -39,8 +39,8 @@ func TestRecorderLogger(t *testing.T) {
3939
log.WithSchemaURL("https://example.com"),
4040
},
4141

42-
wantLogger: &Recorder{
43-
currentScopeRecord: &ScopeRecords{
42+
wantLogger: &logger{
43+
scopeRecord: &ScopeRecords{
4444
Name: "test",
4545
Version: "logtest v42",
4646
SchemaURL: "https://example.com",
@@ -51,7 +51,7 @@ func TestRecorderLogger(t *testing.T) {
5151
t.Run(tt.name, func(t *testing.T) {
5252
l := NewRecorder(tt.options...).Logger(tt.loggerName, tt.loggerOptions...)
5353
// unset enabledFn to allow comparison
54-
l.(*Recorder).enabledFn = nil
54+
l.(*logger).enabledFn = nil
5555

5656
assert.Equal(t, tt.wantLogger, l)
5757
})
@@ -63,7 +63,7 @@ func TestRecorderLoggerCreatesNewStruct(t *testing.T) {
6363
assert.NotEqual(t, r, r.Logger("test"))
6464
}
6565

66-
func TestRecorderEnabled(t *testing.T) {
66+
func TestLoggerEnabled(t *testing.T) {
6767
for _, tt := range []struct {
6868
name string
6969
options []Option
@@ -97,32 +97,33 @@ func TestRecorderEnabled(t *testing.T) {
9797
},
9898
} {
9999
t.Run(tt.name, func(t *testing.T) {
100-
e := NewRecorder(tt.options...).Enabled(tt.ctx, tt.buildRecord())
100+
e := NewRecorder(tt.options...).Logger("test").Enabled(tt.ctx, tt.buildRecord())
101101
assert.Equal(t, tt.isEnabled, e)
102102
})
103103
}
104104
}
105105

106-
func TestRecorderEnabledFnUnset(t *testing.T) {
107-
r := &Recorder{}
106+
func TestLoggerEnabledFnUnset(t *testing.T) {
107+
r := &logger{}
108108
assert.True(t, r.Enabled(context.Background(), log.Record{}))
109109
}
110110

111111
func TestRecorderEmitAndReset(t *testing.T) {
112112
r := NewRecorder()
113+
l := r.Logger("test")
113114
assert.Len(t, r.Result()[0].Records, 0)
114115

115116
r1 := log.Record{}
116117
r1.SetSeverity(log.SeverityInfo)
117-
r.Emit(context.Background(), r1)
118+
l.Emit(context.Background(), r1)
118119
assert.Equal(t, r.Result()[0].Records, []log.Record{r1})
119120

120-
l := r.Logger("test")
121+
nl := r.Logger("test")
121122
assert.Empty(t, r.Result()[1].Records)
122123

123124
r2 := log.Record{}
124125
r2.SetSeverity(log.SeverityError)
125-
l.Emit(context.Background(), r2)
126+
nl.Emit(context.Background(), r2)
126127
assert.Equal(t, r.Result()[0].Records, []log.Record{r1})
127128
assert.Equal(t, r.Result()[1].Records, []log.Record{r2})
128129

0 commit comments

Comments
 (0)