Skip to content

Commit

Permalink
Make level configurable for NewStdLog. (#439)
Browse files Browse the repository at this point in the history
With this change new function is added called NewStdLogAt. It has same
behaviour as NewStdLog, but it allows providing log level to use by
returned *log.Logger.
  • Loading branch information
delicb committed Aug 2, 2017
1 parent e68420e commit 54cb0ed
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 18 deletions.
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_Regular(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.Nil(t, err, "Unexpected error.")
std.Print("redirected")
checkStdLogMessage(t, "redirected", logs)
})
}
}

func TestNewStdLogAt_Fatal(t *testing.T) {
withLogger(t, DebugLevel, []Option{AddCaller()}, func(l *Logger, logs *observer.ObservedLogs) {
stub := exit.WithStub(func() {
std, err := NewStdLogAt(l, FatalLevel)
require.Nil(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 TestNewStdLogAt_Panics(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.Nil(t, err, "Unexpected error")
assert.Panics(t, func() { std.Print("redirected") }, "Expected log to panic.")
checkStdLogMessage(t, "redirected", logs)
})
}
}

func TestNewStdLog_InvalidLevel(t *testing.T) {
_, err := NewStdLogAt(NewNop(), zapcore.Level(99))
assert.NotNil(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.",
)
}

0 comments on commit 54cb0ed

Please sign in to comment.