Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

log: add WithStacktrace #6

Merged
merged 1 commit into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ import (
// New returns a new logger based on the logr interface and the zap logging
// library.
func New(w io.Writer, opts ...option) logr.Logger {
options := options{
clock: zapcore.DefaultClock,
}
options := defaults()
for _, o := range opts {
o.apply(&options)
}
Expand All @@ -55,16 +53,21 @@ func New(w io.Writer, opts ...option) logr.Logger {
}
}

var writer zapcore.WriteSyncer = zapcore.Lock(zapcore.AddSync(w))

var levelEnabler zapcore.LevelEnabler = zap.NewAtomicLevelAt(zapcore.Level(-options.level))
core := zapcore.NewCore(
encoder,
zapcore.Lock(zapcore.AddSync(w)),
zap.NewAtomicLevelAt(zapcore.Level(-options.level)),
)

logger := zap.New(
zapcore.NewCore(encoder, writer, levelEnabler),
zapOpts := []zap.Option{
zap.WithCaller(true),
zap.AddStacktrace(zap.ErrorLevel),
zap.WithClock(options.clock),
).Named(options.name)
}
if options.addStack {
zapOpts = append(zapOpts, zap.AddStacktrace(zap.ErrorLevel))
}

logger := zap.New(core, zapOpts...).Named(options.name)

return zapr.NewLogger(logger)
}
Expand Down
39 changes: 34 additions & 5 deletions log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,35 @@ func TestNew(t *testing.T) {
})
})

t.Run("Logs errors including stack qtrace", func(t *testing.T) {
t.Run("Logs errors including stacktrace", func(t *testing.T) {
var b bytes.Buffer

logger := log.New(&b)
logger.Error(io.EOF, "End of file.", "foo", "bar")

assertErrorRecord(t, b, map[string]interface{}{
assertErrorRecordWithStacktrace(t, b, map[string]interface{}{
"level": "2",
"caller": "log/log_test.go:59",
"error": "EOF",
"msg": "End of file.",
"foo": "bar",
})
})

t.Run("Logs errors without stacktrace", func(t *testing.T) {
var b bytes.Buffer

logger := log.New(&b, log.WithStacktrace(false))
logger.Error(io.EOF, "End of file.", "foo", "bar")

assertErrorRecordWithoutStacktrace(t, b, map[string]interface{}{
"level": "2",
"caller": "log/log_test.go:74",
"error": "EOF",
"msg": "End of file.",
"foo": "bar",
})
})
}

func assertInfoRecord(t *testing.T, b bytes.Buffer, keysAndValues map[string]interface{}) {
Expand All @@ -77,16 +92,30 @@ func assertInfoRecord(t *testing.T, b bytes.Buffer, keysAndValues map[string]int
assert.DeepEqual(t, entry, keysAndValues, ignoreTimestampField)
}

func assertErrorRecord(t *testing.T, b bytes.Buffer, keysAndValues map[string]interface{}) {
func assertErrorRecordWithoutStacktrace(t *testing.T, b bytes.Buffer, keysAndValues map[string]interface{}) {
t.Helper()

entry := map[string]interface{}{}
err := json.Unmarshal(b.Bytes(), &entry)
assert.NilError(t, err)

// Must not include a stacktrace.
_, ok := entry["stacktrace"]
assert.Assert(t, !ok, "Stacktrace is included.")

assert.DeepEqual(t, entry, keysAndValues, ignoreTimestampField)
}

func assertErrorRecordWithStacktrace(t *testing.T, b bytes.Buffer, keysAndValues map[string]interface{}) {
t.Helper()

entry := map[string]interface{}{}
err := json.Unmarshal(b.Bytes(), &entry)
assert.NilError(t, err)

// Must include a stack trace.
// Must include a stacktrace.
st, ok := entry["stacktrace"]
assert.Assert(t, ok, "Stack trace is missing.")
assert.Assert(t, ok, "Stacktrace is missing.")
assert.Assert(t, cmp.Contains(st, "testing.tRunner"))
delete(entry, "stacktrace")

Expand Down
28 changes: 24 additions & 4 deletions log/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ package log
import "go.uber.org/zap/zapcore"

type options struct {
name string
level int
debug bool
clock zapcore.Clock
name string
level int
debug bool
clock zapcore.Clock
addStack bool
}

func defaults() options {
return options{
clock: zapcore.DefaultClock,
addStack: true,
}
}

type option interface {
Expand Down Expand Up @@ -64,3 +72,15 @@ func (o clockOption) apply(opts *options) {
func WithClock(clock zapcore.Clock) option {
return clockOption{clock}
}

type addStackOption bool

func (o addStackOption) apply(opts *options) {
opts.addStack = bool(o)
}

// WithStacktrace configures the logger to record error stacktraces.
// It is enabled by default.
func WithStacktrace(enabled bool) option {
return addStackOption(enabled)
}