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

Add *ln variants of methods to the SugaredLogger #1080

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
72 changes: 68 additions & 4 deletions sugar.go
Expand Up @@ -38,10 +38,19 @@ const (
// method.
//
// Unlike the Logger, the SugaredLogger doesn't insist on structured logging.
// For each log level, it exposes three methods: one for loosely-typed
// structured logging, one for println-style formatting, and one for
// printf-style formatting. For example, SugaredLoggers can produce InfoLevel
// output with Infow ("info with" structured context), Info, or Infof.
// For each log level, it exposes four methods:
//
// - methods named after the log level for log.Print-style logging
// - methods ending in "w" for loosely-typed structured logging
// - methods ending in "f" for log.Printf-style logging
// - methods ending in "ln" for log.Println-style logging
//
// For example, the methods for InfoLevel are:
//
// Info(...any) Print-style logging
// Infow(...any) Structured logging (read as "info with")
// Infof(string, ...any) Printf-style logging
// Infoln(...any) Println-style logging
type SugaredLogger struct {
base *Logger
}
Expand Down Expand Up @@ -220,11 +229,48 @@ func (s *SugaredLogger) Fatalw(msg string, keysAndValues ...interface{}) {
s.log(FatalLevel, msg, nil, keysAndValues)
}

// Debugln uses fmt.Sprintln to construct and log a message.
func (s *SugaredLogger) Debugln(args ...interface{}) {
s.logln(DebugLevel, "", args, nil)
}

// Infoln uses fmt.Sprintln to construct and log a message.
func (s *SugaredLogger) Infoln(args ...interface{}) {
s.logln(InfoLevel, "", args, nil)
}

// Warnln uses fmt.Sprintln to construct and log a message.
func (s *SugaredLogger) Warnln(args ...interface{}) {
s.logln(WarnLevel, "", args, nil)
}

// Errorln uses fmt.Sprintln to construct and log a message.
func (s *SugaredLogger) Errorln(args ...interface{}) {
s.logln(ErrorLevel, "", args, nil)
}

// DPanicln uses fmt.Sprintln to construct and log a message. In development, the
// logger then panics. (See DPanicLevel for details.)
func (s *SugaredLogger) DPanicln(args ...interface{}) {
s.logln(DPanicLevel, "", args, nil)
}

// Panicln uses fmt.Sprintln to construct and log a message, then panics.
func (s *SugaredLogger) Panicln(args ...interface{}) {
s.logln(PanicLevel, "", args, nil)
}

// Fatalln uses fmt.Sprintln to construct and log a message, then calls os.Exit.
func (s *SugaredLogger) Fatalln(args ...interface{}) {
s.logln(FatalLevel, "", args, nil)
}

// Sync flushes any buffered log entries.
func (s *SugaredLogger) Sync() error {
return s.base.Sync()
}

// log message with Sprint, Sprintf, or neither.
func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
// If logging at this level is completely disabled, skip the overhead of
// string formatting.
Expand All @@ -238,6 +284,18 @@ func (s *SugaredLogger) log(lvl zapcore.Level, template string, fmtArgs []interf
}
}

// logln message with Sprintln
func (s *SugaredLogger) logln(lvl zapcore.Level, template string, fmtArgs []interface{}, context []interface{}) {
if lvl < DPanicLevel && !s.base.Core().Enabled(lvl) {
return
}

msg := getMessageln(fmtArgs)
if ce := s.base.Check(lvl, msg); ce != nil {
ce.Write(s.sweetenFields(context)...)
}
}

// getMessage format with Sprint, Sprintf, or neither.
func getMessage(template string, fmtArgs []interface{}) string {
if len(fmtArgs) == 0 {
Expand All @@ -256,6 +314,12 @@ func getMessage(template string, fmtArgs []interface{}) string {
return fmt.Sprint(fmtArgs...)
}

// getMessageln format with Sprintln.
func getMessageln(fmtArgs []interface{}) string {
msg := fmt.Sprintln(fmtArgs...)
return msg[:len(msg)-1]
}

func (s *SugaredLogger) sweetenFields(args []interface{}) []Field {
if len(args) == 0 {
return nil
Expand Down
73 changes: 73 additions & 0 deletions sugar_test.go
Expand Up @@ -261,6 +261,49 @@ func TestSugarTemplatedLogging(t *testing.T) {
}
}

func TestSugarLnLogging(t *testing.T) {
tests := []struct {
args []interface{}
expect string
}{
{nil, ""},
{[]interface{}{}, ""},
{[]interface{}{""}, ""},
{[]interface{}{"foo"}, "foo"},
{[]interface{}{"foo", "bar"}, "foo bar"},
}

// Common to all test cases.
context := []interface{}{"foo", "bar"}
expectedFields := []Field{String("foo", "bar")}

for _, tt := range tests {
withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
logger.With(context...).Debugln(tt.args...)
logger.With(context...).Infoln(tt.args...)
logger.With(context...).Warnln(tt.args...)
logger.With(context...).Errorln(tt.args...)
logger.With(context...).DPanicln(tt.args...)

expected := make([]observer.LoggedEntry, 5)
for i, lvl := range []zapcore.Level{DebugLevel, InfoLevel, WarnLevel, ErrorLevel, DPanicLevel} {
expected[i] = observer.LoggedEntry{
Entry: zapcore.Entry{Message: tt.expect, Level: lvl},
Context: expectedFields,
}
}
assert.Equal(t, expected, logs.AllUntimed(), "Unexpected log output.")
})
}
}

func TestSugarLnLoggingIgnored(t *testing.T) {
withSugar(t, WarnLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
logger.Infoln("hello")
assert.Zero(t, logs.Len(), "Expected zero log statements.")
})
}

func TestSugarPanicLogging(t *testing.T) {
tests := []struct {
loggerLevel zapcore.Level
Expand All @@ -276,6 +319,9 @@ func TestSugarPanicLogging(t *testing.T) {
{FatalLevel, func(s *SugaredLogger) { s.Panicw("foo") }, ""},
{PanicLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
{DebugLevel, func(s *SugaredLogger) { s.Panicw("foo") }, "foo"},
{FatalLevel, func(s *SugaredLogger) { s.Panicln("foo") }, ""},
{PanicLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"},
{DebugLevel, func(s *SugaredLogger) { s.Panicln("foo") }, "foo"},
}

for _, tt := range tests {
Expand Down Expand Up @@ -308,6 +354,9 @@ func TestSugarFatalLogging(t *testing.T) {
{FatalLevel + 1, func(s *SugaredLogger) { s.Fatalw("foo") }, ""},
{FatalLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
{DebugLevel, func(s *SugaredLogger) { s.Fatalw("foo") }, "foo"},
{FatalLevel + 1, func(s *SugaredLogger) { s.Fatalln("foo") }, ""},
{FatalLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"},
{DebugLevel, func(s *SugaredLogger) { s.Fatalln("foo") }, "foo"},
}

for _, tt := range tests {
Expand Down Expand Up @@ -385,10 +434,34 @@ func TestSugarWithOptionsIncreaseLevel(t *testing.T) {
})
}

func TestSugarLnWithOptionsIncreaseLevel(t *testing.T) {
withSugar(t, DebugLevel, nil, func(logger *SugaredLogger, logs *observer.ObservedLogs) {
logger = logger.WithOptions(IncreaseLevel(WarnLevel))
logger.Infoln("logger.Infoln")
logger.Warnln("logger.Warnln")
logger.Errorln("logger.Errorln")
require.Equal(t, 2, logs.Len(), "expected only warn + error logs due to IncreaseLevel.")
assert.Equal(
t,
logs.AllUntimed()[0].Message,
"logger.Warnln",
"Expected first logged message to be warn level message",
)
})
}

func BenchmarkSugarSingleStrArg(b *testing.B) {
withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {
for i := 0; i < b.N; i++ {
log.Info("hello world")
}
})
}

func BenchmarkLnSugarSingleStrArg(b *testing.B) {
withSugar(b, InfoLevel, nil /* opts* */, func(log *SugaredLogger, logs *observer.ObservedLogs) {
for i := 0; i < b.N; i++ {
log.Infoln("hello world")
}
})
}