Skip to content

Commit 643f225

Browse files
author
Akshay Shah
committedFeb 15, 2017
Finish sugared logger, update tests & benchmarks
1 parent 6cd19fe commit 643f225

File tree

5 files changed

+310
-238
lines changed

5 files changed

+310
-238
lines changed
 

‎common_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ func withLogger(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(Logg
4141
f(log, &logs)
4242
}
4343

44+
func withSugar(t testing.TB, e zapcore.LevelEnabler, opts []Option, f func(*SugaredLogger, *zapcore.ObservedLogs)) {
45+
withLogger(t, e, opts, func(logger Logger, logs *zapcore.ObservedLogs) { f(Sugar(logger), logs) })
46+
}
47+
4448
func runConcurrently(goroutines, iterations int, wg *sync.WaitGroup, f func()) {
4549
wg.Add(goroutines)
4650
for g := 0; g < goroutines; g++ {

‎field.go

+50
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,53 @@ func Reflect(key string, val interface{}) zapcore.Field {
156156
func Nest(key string, fields ...zapcore.Field) zapcore.Field {
157157
return zapcore.Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: zapcore.Fields(fields)}
158158
}
159+
160+
// Any takes a key and an arbitrary value and chooses the best way to represent
161+
// them as a field, falling back to a reflection-based approach only if
162+
// necessary.
163+
func Any(key string, value interface{}) zapcore.Field {
164+
switch val := value.(type) {
165+
case zapcore.ObjectMarshaler:
166+
return Object(key, val)
167+
case bool:
168+
return Bool(key, val)
169+
case float64:
170+
return Float64(key, val)
171+
case float32:
172+
return Float64(key, float64(val))
173+
case int:
174+
return Int(key, val)
175+
case int64:
176+
return Int64(key, val)
177+
case int32:
178+
return Int64(key, int64(val))
179+
case int16:
180+
return Int64(key, int64(val))
181+
case int8:
182+
return Int64(key, int64(val))
183+
case uint:
184+
return Uint(key, val)
185+
case uint64:
186+
return Uint64(key, val)
187+
case uint32:
188+
return Uint64(key, uint64(val))
189+
case uint16:
190+
return Uint64(key, uint64(val))
191+
case uint8:
192+
return Uint64(key, uint64(val))
193+
case uintptr:
194+
return Uintptr(key, val)
195+
case string:
196+
return String(key, val)
197+
case error:
198+
return String(key, val.Error())
199+
case time.Time:
200+
return Time(key, val)
201+
case time.Duration:
202+
return Duration(key, val)
203+
case fmt.Stringer:
204+
return String(key, val.String())
205+
default:
206+
return Reflect(key, val)
207+
}
208+
}

‎sugar.go

+71-86
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,12 @@ package zap
2222

2323
import (
2424
"fmt"
25-
"time"
25+
26+
"go.uber.org/zap/zapcore"
2627
)
2728

29+
const oddNumberErrMsg = "Passed an odd number of keys and values to SugaredLogger, ignoring last."
30+
2831
// A SugaredLogger wraps the core Logger functionality in a slower, but less
2932
// verbose, API.
3033
type SugaredLogger struct {
@@ -33,19 +36,20 @@ type SugaredLogger struct {
3336

3437
// Sugar converts a Logger to a SugaredLogger.
3538
func Sugar(core Logger) *SugaredLogger {
39+
// TODO: increment caller skip.
3640
return &SugaredLogger{core}
3741
}
3842

3943
// Desugar unwraps a SugaredLogger, exposing the original Logger.
4044
func Desugar(s *SugaredLogger) Logger {
45+
// TODO: decrement caller skip.
4146
return s.core
4247
}
4348

4449
// With adds a variadic number of key-value pairs to the logging context.
4550
// Even-indexed arguments are treated as keys, and are converted to strings
4651
// (with fmt.Sprint) if necessary. The keys are then zipped with the
47-
// odd-indexed values into typed fields, falling back to a reflection-based
48-
// encoder if necessary.
52+
// odd-indexed values into typed fields using the Any field constructor.
4953
//
5054
// For example,
5155
// sugaredLogger.With(
@@ -65,165 +69,146 @@ func (s *SugaredLogger) With(args ...interface{}) *SugaredLogger {
6569
return &SugaredLogger{core: s.core.With(sweetenFields(args, s.core)...)}
6670
}
6771

68-
// WithStack adds a complete stack trace to the logger's context, using the key
69-
// "stacktrace".
70-
func (s *SugaredLogger) WithStack() *SugaredLogger {
71-
return &SugaredLogger{core: s.core.With(Stack())}
72+
// WithFields adds structured fields to the logger's context, just like the
73+
// standard Logger's With method.
74+
func (s *SugaredLogger) WithFields(fs ...zapcore.Field) *SugaredLogger {
75+
return &SugaredLogger{core: s.core.With(fs...)}
7276
}
7377

7478
// Debug logs a message and some key-value pairs at DebugLevel. Keys and values
7579
// are treated as they are in the With method.
7680
func (s *SugaredLogger) Debug(msg interface{}, keysAndValues ...interface{}) {
77-
s.core.Debug(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
81+
if ce := s.core.Check(DebugLevel, sweetenMsg(msg)); ce != nil {
82+
ce.Write(sweetenFields(keysAndValues, s.core)...)
83+
}
7884
}
7985

8086
// Debugf uses fmt.Sprintf to construct a dynamic message and logs it at
8187
// DebugLevel. It doesn't add to the message's structured context.
8288
func (s *SugaredLogger) Debugf(template string, args ...interface{}) {
83-
s.Debug(fmt.Sprintf(template, args...))
89+
if ce := s.core.Check(DebugLevel, fmt.Sprintf(template, args...)); ce != nil {
90+
ce.Write()
91+
}
8492
}
8593

8694
// Info logs a message and some key-value pairs at InfoLevel. Keys and values
8795
// are treated as they are in the With method.
8896
func (s *SugaredLogger) Info(msg interface{}, keysAndValues ...interface{}) {
89-
s.core.Info(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
97+
if ce := s.core.Check(InfoLevel, sweetenMsg(msg)); ce != nil {
98+
ce.Write(sweetenFields(keysAndValues, s.core)...)
99+
}
90100
}
91101

92102
// Infof uses fmt.Sprintf to construct a dynamic message and logs it at
93103
// InfoLevel. It doesn't add to the message's structured context.
94104
func (s *SugaredLogger) Infof(template string, args ...interface{}) {
95-
s.Info(fmt.Sprintf(template, args...))
105+
if ce := s.core.Check(InfoLevel, fmt.Sprintf(template, args...)); ce != nil {
106+
ce.Write()
107+
}
96108
}
97109

98110
// Warn logs a message and some key-value pairs at WarnLevel. Keys and values
99111
// are treated as they are in the With method.
100112
func (s *SugaredLogger) Warn(msg interface{}, keysAndValues ...interface{}) {
101-
s.core.Warn(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
113+
if ce := s.core.Check(WarnLevel, sweetenMsg(msg)); ce != nil {
114+
ce.Write(sweetenFields(keysAndValues, s.core)...)
115+
}
102116
}
103117

104118
// Warnf uses fmt.Sprintf to construct a dynamic message and logs it at
105119
// WarnLevel. It doesn't add to the message's structured context.
106120
func (s *SugaredLogger) Warnf(template string, args ...interface{}) {
107-
s.Warn(fmt.Sprintf(template, args...))
121+
if ce := s.core.Check(WarnLevel, fmt.Sprintf(template, args...)); ce != nil {
122+
ce.Write()
123+
}
108124
}
109125

110126
// Error logs a message and some key-value pairs at ErrorLevel. Keys and values
111127
// are treated as they are in the With method.
112128
func (s *SugaredLogger) Error(msg interface{}, keysAndValues ...interface{}) {
113-
s.core.Error(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
129+
if ce := s.core.Check(ErrorLevel, sweetenMsg(msg)); ce != nil {
130+
ce.Write(sweetenFields(keysAndValues, s.core)...)
131+
}
114132
}
115133

116134
// Errorf uses fmt.Sprintf to construct a dynamic message and logs it at
117135
// ErrorLevel. It doesn't add to the message's structured context.
118136
func (s *SugaredLogger) Errorf(template string, args ...interface{}) {
119-
s.Error(fmt.Sprintf(template, args...))
137+
if ce := s.core.Check(ErrorLevel, fmt.Sprintf(template, args...)); ce != nil {
138+
ce.Write()
139+
}
140+
}
141+
142+
// DPanic logs a message and some key-value pairs using the underlying logger's
143+
// DPanic method. Keys and values are treated as they are in the With
144+
// method. (See Logger.DPanic for details.)
145+
func (s *SugaredLogger) DPanic(msg interface{}, keysAndValues ...interface{}) {
146+
if ce := s.core.Check(DPanicLevel, sweetenMsg(msg)); ce != nil {
147+
ce.Write(sweetenFields(keysAndValues, s.core)...)
148+
}
149+
}
150+
151+
// DPanicf uses fmt.Sprintf to construct a dynamic message, which is passed to
152+
// the underlying Logger's DPanic method. (See Logger.DPanic for details.) It
153+
// doesn't add to the message's structured context.
154+
func (s *SugaredLogger) DPanicf(template string, args ...interface{}) {
155+
if ce := s.core.Check(DPanicLevel, fmt.Sprintf(template, args...)); ce != nil {
156+
ce.Write()
157+
}
120158
}
121159

122160
// Panic logs a message and some key-value pairs at PanicLevel, then panics.
123161
// Keys and values are treated as they are in the With method.
124162
func (s *SugaredLogger) Panic(msg interface{}, keysAndValues ...interface{}) {
125-
s.core.Panic(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
163+
if ce := s.core.Check(PanicLevel, sweetenMsg(msg)); ce != nil {
164+
ce.Write(sweetenFields(keysAndValues, s.core)...)
165+
}
126166
}
127167

128168
// Panicf uses fmt.Sprintf to construct a dynamic message and logs it at
129169
// PanicLevel, then panics. It doesn't add to the message's structured context.
130170
func (s *SugaredLogger) Panicf(template string, args ...interface{}) {
131-
s.Panic(fmt.Sprintf(template, args...))
171+
if ce := s.core.Check(PanicLevel, fmt.Sprintf(template, args...)); ce != nil {
172+
ce.Write()
173+
}
132174
}
133175

134176
// Fatal logs a message and some key-value pairs at FatalLevel, then calls
135177
// os.Exit(1). Keys and values are treated as they are in the With method.
136178
func (s *SugaredLogger) Fatal(msg interface{}, keysAndValues ...interface{}) {
137-
s.core.Fatal(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
179+
if ce := s.core.Check(FatalLevel, sweetenMsg(msg)); ce != nil {
180+
ce.Write(sweetenFields(keysAndValues, s.core)...)
181+
}
138182
}
139183

140184
// Fatalf uses fmt.Sprintf to construct a dynamic message and logs it at
141185
// FatalLevel, then calls os.Exit(1). It doesn't add to the message's
142186
// structured context.
143187
func (s *SugaredLogger) Fatalf(template string, args ...interface{}) {
144-
s.Fatal(fmt.Sprintf(template, args...))
145-
}
146-
147-
// DFatal logs a message and some key-value pairs using the underlying logger's
148-
// DFatal method. Keys and values are treated as they are in the With
149-
// method. (See Logger.DFatal for details.)
150-
func (s *SugaredLogger) DFatal(msg interface{}, keysAndValues ...interface{}) {
151-
s.core.DFatal(sweetenMsg(msg), sweetenFields(keysAndValues, s.core)...)
152-
}
153-
154-
// DFatalf uses fmt.Sprintf to construct a dynamic message, which is passed to
155-
// the underlying Logger's DFatal method. (See Logger.DFatal for details.) It
156-
// doesn't add to the message's structured context.
157-
func (s *SugaredLogger) DFatalf(template string, args ...interface{}) {
158-
s.DFatal(fmt.Sprintf(template, args...))
188+
if ce := s.core.Check(FatalLevel, fmt.Sprintf(template, args...)); ce != nil {
189+
ce.Write()
190+
}
159191
}
160192

161-
func sweetenFields(args []interface{}, errLogger Logger) []Field {
193+
func sweetenFields(args []interface{}, errLogger Logger) []zapcore.Field {
162194
if len(args) == 0 {
163195
return nil
164196
}
165197
if len(args)%2 == 1 {
166-
errLogger.DFatal(
167-
"Passed an odd number of keys and values to SugaredLogger, ignoring last.",
168-
Object("ignored", args[len(args)-1]),
169-
)
198+
errLogger.DPanic(oddNumberErrMsg, Any("ignored", args[len(args)-1]))
170199
}
171200

172-
fields := make([]Field, len(args)/2)
201+
fields := make([]zapcore.Field, len(args)/2)
173202
for i := range fields {
174203
key := sweetenMsg(args[2*i])
175-
176-
switch val := args[2*i+1].(type) {
177-
case LogMarshaler:
178-
fields[i] = Marshaler(key, val)
179-
case bool:
180-
fields[i] = Bool(key, val)
181-
case float64:
182-
fields[i] = Float64(key, val)
183-
case float32:
184-
fields[i] = Float64(key, float64(val))
185-
case int:
186-
fields[i] = Int(key, val)
187-
case int64:
188-
fields[i] = Int64(key, val)
189-
case int32:
190-
fields[i] = Int64(key, int64(val))
191-
case int16:
192-
fields[i] = Int64(key, int64(val))
193-
case int8:
194-
fields[i] = Int64(key, int64(val))
195-
case uint:
196-
fields[i] = Uint(key, val)
197-
case uint64:
198-
fields[i] = Uint64(key, val)
199-
case uint32:
200-
fields[i] = Uint64(key, uint64(val))
201-
case uint16:
202-
fields[i] = Uint64(key, uint64(val))
203-
case uint8:
204-
fields[i] = Uint64(key, uint64(val))
205-
case uintptr:
206-
fields[i] = Uintptr(key, val)
207-
case string:
208-
fields[i] = String(key, val)
209-
case time.Time:
210-
fields[i] = Time(key, val)
211-
case time.Duration:
212-
fields[i] = Duration(key, val)
213-
case error:
214-
fields[i] = String(key, val.Error())
215-
case fmt.Stringer:
216-
fields[i] = String(key, val.String())
217-
default:
218-
fields[i] = Object(key, val)
219-
}
204+
fields[i] = Any(key, args[2*i+1])
220205
}
221206
return fields
222207
}
223208

224209
func sweetenMsg(msg interface{}) string {
225-
if m, ok := msg.(string); ok {
226-
return m
210+
if str, ok := msg.(string); ok {
211+
return str
227212
}
228213
return fmt.Sprint(msg)
229214
}

‎sugar_bench_test.go

+9-5
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@
2020

2121
package zap
2222

23-
import "testing"
23+
import (
24+
"testing"
25+
26+
"go.uber.org/zap/testutils"
27+
"go.uber.org/zap/zapcore"
28+
)
2429

2530
func withBenchedSugar(b *testing.B, f func(*SugaredLogger)) {
26-
logger := Sugar(New(
27-
NewJSONEncoder(),
31+
logger := Sugar(New(zapcore.WriterFacility(zapcore.NewJSONEncoder(defaultEncoderConfig()),
32+
&testutils.Discarder{},
2833
DebugLevel,
29-
DiscardOutput,
30-
))
34+
)))
3135
b.ResetTimer()
3236
b.RunParallel(func(pb *testing.PB) {
3337
for pb.Next() {

0 commit comments

Comments
 (0)
Please sign in to comment.