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

Make level configurable for NewStdLog. (#439) #487

Merged
merged 2 commits into from
Sep 21, 2017
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
46 changes: 38 additions & 8 deletions global.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ package zap

import (
"bytes"
"fmt"
"log"
"os"
"sync"

"go.uber.org/zap/zapcore"
)

const (
_stdLogDefaultDepth = 2
_loggerWriterDepth = 1
_loggerWriterDepth = 2
)

var (
Expand Down Expand Up @@ -71,9 +74,35 @@ func ReplaceGlobals(logger *Logger) func() {
// InfoLevel. To redirect the standard library's package-global logging
// functions, use RedirectStdLog instead.
func NewStdLog(l *Logger) *log.Logger {
return log.New(&loggerWriter{l.WithOptions(
AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth),
)}, "" /* prefix */, 0 /* flags */)
logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
f := logger.Info
return log.New(&loggerWriter{f}, "" /* prefix */, 0 /* flags */)
}

// NewStdLogAt returns *log.Logger which writes to supplied zap logger at
// required level.
func NewStdLogAt(l *Logger, level zapcore.Level) (*log.Logger, error) {
logger := l.WithOptions(AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth))
var logFunc func(string, ...zapcore.Field)
switch level {
case DebugLevel:
logFunc = logger.Debug
case InfoLevel:
logFunc = logger.Info
case WarnLevel:
logFunc = logger.Warn
case ErrorLevel:
logFunc = logger.Error
case DPanicLevel:
logFunc = logger.DPanic
case PanicLevel:
logFunc = logger.Panic
case FatalLevel:
logFunc = logger.Fatal
default:
return nil, fmt.Errorf("unrecognized level: %q", level)
}
return log.New(&loggerWriter{logFunc}, "" /* prefix */, 0 /* flags */), nil
}

// RedirectStdLog redirects output from the standard library's package-global
Expand All @@ -88,9 +117,10 @@ func RedirectStdLog(l *Logger) func() {
prefix := log.Prefix()
log.SetFlags(0)
log.SetPrefix("")
log.SetOutput(&loggerWriter{l.WithOptions(
logFunc := l.WithOptions(
AddCallerSkip(_stdLogDefaultDepth + _loggerWriterDepth),
)})
).Info
log.SetOutput(&loggerWriter{logFunc})
return func() {
log.SetFlags(flags)
log.SetPrefix(prefix)
Expand All @@ -99,11 +129,11 @@ func RedirectStdLog(l *Logger) func() {
}

type loggerWriter struct {
logger *Logger
logFunc func(msg string, fields ...zapcore.Field)
}

func (l *loggerWriter) Write(p []byte) (int, error) {
p = bytes.TrimSpace(p)
l.logger.Info(string(p))
l.logFunc(string(p))
return len(p), nil
}
70 changes: 60 additions & 10 deletions global_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import (
"testing"
"time"

"go.uber.org/zap/internal/exit"

"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest"
"go.uber.org/zap/zaptest/observer"
Expand Down Expand Up @@ -96,20 +98,55 @@ func TestNewStdLog(t *testing.T) {
withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
std := NewStdLog(l)
std.Print("redirected")
checkStdLogMessage(t, "redirected", logs)
})
}

require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged.")
entry := logs.AllUntimed()[0]
assert.Equal(t, []zapcore.Field{}, entry.Context, "Unexpected entry context.")
assert.Equal(t, "redirected", entry.Entry.Message, "Unexpected entry message.")
assert.Regexp(
t,
`go.uber.org/zap/global_test.go:\d+$`,
entry.Entry.Caller.String(),
"Unexpected caller annotation.",
)
func TestNewStdLogAt(t *testing.T) {
// include DPanicLevel here, but do not include Development in options
levels := []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel}
for _, level := range levels {
withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
std, err := NewStdLogAt(l, level)
require.NoError(t, err, "Unexpected error.")
std.Print("redirected")
checkStdLogMessage(t, "redirected", logs)
})
}
}

func TestNewStdLogAtPanics(t *testing.T) {
// include DPanicLevel here and enable Development in options
levels := []zapcore.Level{DPanicLevel, PanicLevel}
for _, level := range levels {
withLogger(t, DebugLevel, []Option{AddCaller(), Development()}, func(l *Logger, logs *observer.ObservedLogs) {
std, err := NewStdLogAt(l, level)
require.NoError(t, err, "Unexpected error")
assert.Panics(t, func() { std.Print("redirected") }, "Expected log to panic.")
checkStdLogMessage(t, "redirected", logs)
})
}
}

func TestNewStdLogAtFatal(t *testing.T) {
withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
stub := exit.WithStub(func() {
std, err := NewStdLogAt(l, FatalLevel)
require.NoError(t, err, "Unexpected error.")
std.Print("redirected")
checkStdLogMessage(t, "redirected", logs)
})
assert.True(t, true, stub.Exited, "Expected Fatal logger call to terminate process.")
stub.Unstub()
})
}

func TestNewStdLogAtInvalid(t *testing.T) {
_, err := NewStdLogAt(NewNop(), zapcore.Level(99))
assert.Error(t, err, "Expected to get error.")
assert.Contains(t, err.Error(), "99", "Expected level code in error message")
}

func TestRedirectStdLog(t *testing.T) {
initialFlags := log.Flags()
initialPrefix := log.Prefix()
Expand Down Expand Up @@ -137,3 +174,16 @@ func TestRedirectStdLogCaller(t *testing.T) {
assert.Contains(t, entries[0].Entry.Caller.File, "global_test.go", "Unexpected caller annotation.")
})
}

func checkStdLogMessage(t *testing.T, msg string, logs *observer.ObservedLogs) {
require.Equal(t, 1, logs.Len(), "Expected exactly one entry to be logged")
entry := logs.AllUntimed()[0]
assert.Equal(t, []zapcore.Field{}, entry.Context, "Unexpected entry context.")
assert.Equal(t, "redirected", entry.Entry.Message, "Unexpected entry message.")
assert.Regexp(
t,
`go.uber.org/zap/global_test.go:\d+$`,
entry.Entry.Caller.String(),
"Unexpected caller annotation.",
)
}