-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
move logtest into a recorder within the api
- Loading branch information
Showing
8 changed files
with
266 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Log Test | ||
|
||
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/log/logtest)](https://pkg.go.dev/go.opentelemetry.io/otel/log/logtest) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package logtest // import "go.opentelemetry.io/otel/log/logtest" | ||
|
||
import ( | ||
"go.opentelemetry.io/otel/log" | ||
) | ||
|
||
type config struct { | ||
minSeverity log.Severity | ||
} | ||
|
||
func newConfig(options []Option) config { | ||
var c config | ||
for _, opt := range options { | ||
c = opt.apply(c) | ||
} | ||
|
||
return c | ||
} | ||
|
||
// Option configures a [Hook]. | ||
type Option interface { | ||
apply(config) config | ||
} | ||
|
||
type optFunc func(config) config | ||
|
||
func (f optFunc) apply(c config) config { return f(c) } | ||
|
||
// WithMinSeverity returns an [Option] that configures the minimum severity the | ||
// recorder will return true for when Enabled is called. | ||
// | ||
// By default, the recorder will be enabled for all levels. | ||
func WithMinSeverity(l log.Severity) Option { | ||
return optFunc(func(c config) config { | ||
c.minSeverity = l | ||
return c | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// Package logtest is a testing helper package. User can retrieve an in-memory | ||
// logger to verify the behavior of their integrations. | ||
package logtest // import "go.opentelemetry.io/otel/log/logtest" | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
|
||
"go.opentelemetry.io/otel/log" | ||
"go.opentelemetry.io/otel/log/embedded" | ||
) | ||
|
||
// 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. | ||
|
||
type enablerKey uint | ||
|
||
var enableKey enablerKey | ||
|
||
// NewInMemoryRecorder returns a new InMemoryRecorder. | ||
func NewInMemoryRecorder(options ...Option) *InMemoryRecorder { | ||
cfg := newConfig(options) | ||
return &InMemoryRecorder{ | ||
minSeverity: cfg.minSeverity, | ||
} | ||
} | ||
|
||
// Scope represents the instrumentation scope. | ||
type Scope struct { | ||
// Name is the name of the instrumentation scope. | ||
Name string | ||
// Version is the version of the instrumentation scope. | ||
Version string | ||
// SchemaURL of the telemetry emitted by the scope. | ||
SchemaURL string | ||
} | ||
|
||
// InMemoryRecorder is a recorder that stores all received log records | ||
// in-memory. | ||
type InMemoryRecorder struct { | ||
embedded.LoggerProvider | ||
embeddedLogger // nolint:unused // Used to embed embedded.Logger. | ||
|
||
mu sync.Mutex | ||
|
||
records []log.Record | ||
|
||
// Scope is the Logger scope recorder received when Logger was called. | ||
Scope Scope | ||
|
||
// minSeverity is the minimum severity the recorder will return true for | ||
// when Enabled is called (unless enableKey is set). | ||
minSeverity log.Severity | ||
} | ||
|
||
// Logger retrieves acopy of InMemoryRecorder with the provided scope | ||
// information. | ||
func (i *InMemoryRecorder) Logger(name string, opts ...log.LoggerOption) log.Logger { | ||
cfg := log.NewLoggerConfig(opts...) | ||
|
||
i.Scope = Scope{ | ||
Name: name, | ||
Version: cfg.InstrumentationVersion(), | ||
SchemaURL: cfg.SchemaURL(), | ||
} | ||
|
||
return i | ||
} | ||
|
||
// Enabled indicates whether a specific record should be stored, according to | ||
// its severity, or context values. | ||
func (i *InMemoryRecorder) Enabled(ctx context.Context, record log.Record) bool { | ||
return ctx.Value(enableKey) != nil || record.Severity() >= i.minSeverity | ||
} | ||
|
||
// Emit stores the log record. | ||
func (i *InMemoryRecorder) Emit(_ context.Context, record log.Record) { | ||
i.mu.Lock() | ||
defer i.mu.Unlock() | ||
|
||
i.records = append(i.records, record) | ||
} | ||
|
||
// GetRecords returns the current in-memory recorder log records. | ||
func (i *InMemoryRecorder) GetRecords() []log.Record { | ||
i.mu.Lock() | ||
defer i.mu.Unlock() | ||
ret := make([]log.Record, len(i.records)) | ||
copy(ret, i.records) | ||
return ret | ||
} | ||
|
||
// Reset the current in-memory recorder log records. | ||
func (i *InMemoryRecorder) Reset() { | ||
i.mu.Lock() | ||
defer i.mu.Unlock() | ||
i.records = []log.Record{} | ||
} | ||
|
||
// ContextWithEnabledRecorder forces enabling the recorder, no matter the log | ||
// severity level. | ||
func ContextWithEnabledRecorder(ctx context.Context) context.Context { | ||
return context.WithValue(ctx, enableKey, true) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package logtest | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"go.opentelemetry.io/otel/log" | ||
) | ||
|
||
func TestInMemoryRecorderLogger(t *testing.T) { | ||
for _, tt := range []struct { | ||
name string | ||
options []Option | ||
|
||
loggerName string | ||
loggerOptions []log.LoggerOption | ||
|
||
expectedLogger log.Logger | ||
}{ | ||
{ | ||
name: "provides a default logger", | ||
|
||
expectedLogger: &InMemoryRecorder{}, | ||
}, | ||
{ | ||
name: "provides a logger with a configured scope", | ||
|
||
loggerName: "test", | ||
loggerOptions: []log.LoggerOption{ | ||
log.WithInstrumentationVersion("logtest v42"), | ||
log.WithSchemaURL("https://example.com"), | ||
}, | ||
|
||
expectedLogger: &InMemoryRecorder{ | ||
Scope: Scope{ | ||
Name: "test", | ||
Version: "logtest v42", | ||
SchemaURL: "https://example.com", | ||
}, | ||
}, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
l := NewInMemoryRecorder(tt.options...).Logger(tt.loggerName, tt.loggerOptions...) | ||
assert.Equal(t, tt.expectedLogger, l) | ||
}) | ||
} | ||
} | ||
|
||
func TestInMemoryRecorderEnabled(t *testing.T) { | ||
for _, tt := range []struct { | ||
name string | ||
options []Option | ||
ctx context.Context | ||
buildRecord func() log.Record | ||
|
||
isEnabled bool | ||
}{ | ||
{ | ||
name: "the default option enables unset levels", | ||
ctx: context.Background(), | ||
buildRecord: func() log.Record { | ||
return log.Record{} | ||
}, | ||
|
||
isEnabled: true, | ||
}, | ||
{ | ||
name: "with a minimum severity set disables", | ||
options: []Option{ | ||
WithMinSeverity(log.SeverityWarn1), | ||
}, | ||
ctx: context.Background(), | ||
buildRecord: func() log.Record { | ||
return log.Record{} | ||
}, | ||
|
||
isEnabled: false, | ||
}, | ||
{ | ||
name: "with a context that forces an enabled recorder", | ||
options: []Option{ | ||
WithMinSeverity(log.SeverityWarn1), | ||
}, | ||
ctx: ContextWithEnabledRecorder(context.Background()), | ||
buildRecord: func() log.Record { | ||
return log.Record{} | ||
}, | ||
|
||
isEnabled: true, | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
e := NewInMemoryRecorder(tt.options...).Enabled(tt.ctx, tt.buildRecord()) | ||
assert.Equal(t, tt.isEnabled, e) | ||
}) | ||
} | ||
} | ||
|
||
func TestInMemoryRecorderEmitAndReset(t *testing.T) { | ||
r := NewInMemoryRecorder() | ||
assert.Len(t, r.GetRecords(), 0) | ||
r.Emit(context.Background(), log.Record{}) | ||
assert.Len(t, r.GetRecords(), 1) | ||
|
||
r.Reset() | ||
assert.Len(t, r.GetRecords(), 0) | ||
} |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.