From fb0853dd96e057ab0af671d9790651af44dcc128 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 3 Jul 2017 22:16:12 +0300 Subject: [PATCH 1/5] added support for providing custom logger name encoders --- zapcore/console_encoder.go | 4 +- zapcore/encoder.go | 35 ++- zapcore/encoder_test.go | 472 +++++++++++++++++++++---------------- zapcore/json_encoder.go | 8 +- 4 files changed, 315 insertions(+), 204 deletions(-) diff --git a/zapcore/console_encoder.go b/zapcore/console_encoder.go index 9fc89e924..f62b48dc9 100644 --- a/zapcore/console_encoder.go +++ b/zapcore/console_encoder.go @@ -79,8 +79,8 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if c.LevelKey != "" && c.EncodeLevel != nil { c.EncodeLevel(ent.Level, arr) } - if ent.LoggerName != "" && c.NameKey != "" { - arr.AppendString(ent.LoggerName) + if ent.LoggerName != "" && c.NameKey != "" && c.EncodeLoggerName != nil { + c.EncodeLoggerName(ent.LoggerName, arr) } if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil { c.EncodeCaller(ent.Caller, arr) diff --git a/zapcore/encoder.go b/zapcore/encoder.go index a026f4679..1a29f442f 100644 --- a/zapcore/encoder.go +++ b/zapcore/encoder.go @@ -21,6 +21,7 @@ package zapcore import ( + "strings" "time" "go.uber.org/zap/buffer" @@ -196,6 +197,31 @@ func (e *CallerEncoder) UnmarshalText(text []byte) error { return nil } +// A LoggerNameEncoder serializes a LoggerName to a primitive type. +type LoggerNameEncoder func(string, PrimitiveArrayEncoder) + +// FullLoggerNameEncoder serializes a logger name as is +func FullLoggerNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(loggerName) +} + +// CapitalLoggerNameEncoder serializes a logger name in all caps +func CapitalLoggerNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(strings.ToUpper(loggerName)) +} + +// UnmarshalText unmarshals text to a LoggerNameEncoder. "capital" is unmarshaled to +// CapitalLoggerNameEncoder and anything else is unmarshaled to FullLoggerNameEncoder. +func (e *LoggerNameEncoder) UnmarshalText(text []byte) error { + switch string(text) { + case "capital": + *e = CapitalLoggerNameEncoder + default: + *e = FullLoggerNameEncoder + } + return nil +} + // An EncoderConfig allows users to configure the concrete encoders supplied by // zapcore. type EncoderConfig struct { @@ -211,10 +237,11 @@ type EncoderConfig struct { // Configure the primitive representations of common complex types. For // example, some users may want all time.Times serialized as floating-point // seconds since epoch, while others may prefer ISO8601 strings. - EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` - EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` - EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` - EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` + EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` + EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` + EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` + EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` + EncodeLoggerName LoggerNameEncoder `json:"nameEncoder" yaml:"nameEncoder"` } // ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a diff --git a/zapcore/encoder_test.go b/zapcore/encoder_test.go index 0708902fa..b8f344624 100644 --- a/zapcore/encoder_test.go +++ b/zapcore/encoder_test.go @@ -44,17 +44,18 @@ var ( func testEncoderConfig() EncoderConfig { return EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - NameKey: "name", - TimeKey: "ts", - CallerKey: "caller", - StacktraceKey: "stacktrace", - LineEnding: "\n", - EncodeTime: EpochTimeEncoder, - EncodeLevel: LowercaseLevelEncoder, - EncodeDuration: SecondsDurationEncoder, - EncodeCaller: ShortCallerEncoder, + MessageKey: "msg", + LevelKey: "level", + NameKey: "name", + TimeKey: "ts", + CallerKey: "caller", + StacktraceKey: "stacktrace", + LineEnding: "\n", + EncodeTime: EpochTimeEncoder, + EncodeLevel: LowercaseLevelEncoder, + EncodeDuration: SecondsDurationEncoder, + EncodeCaller: ShortCallerEncoder, + EncodeLoggerName: FullLoggerNameEncoder, } } @@ -98,17 +99,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use custom entry keys in JSON output and ignore them in console output", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -116,17 +118,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip level if LevelKey is omitted", cfg: EncoderConfig{ - LevelKey: "", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -134,17 +137,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip timestamp if TimeKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -152,17 +156,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip message if MessageKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack\n", @@ -170,17 +175,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip name if NameKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", @@ -188,17 +194,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip caller if CallerKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", @@ -206,17 +213,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip stacktrace if StacktraceKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n", @@ -224,17 +232,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeTime, for both the entry and any times added", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) }, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) }, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, extra: func(enc Encoder) { enc.AddTime("extra", _epoch) @@ -251,17 +260,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeDuration for any durations added", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: StringDurationEncoder, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: StringDurationEncoder, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, extra: func(enc Encoder) { enc.AddDuration("extra", time.Second) @@ -278,35 +288,56 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeLevel", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: CapitalLevelEncoder, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: CapitalLevelEncoder, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack\n", }, + { + desc: "use the supplied EncodeLoggerName", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: CapitalLoggerNameEncoder, + }, + expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\thello\nfake-stack\n", + }, { desc: "close all open namespaces", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, extra: func(enc Encoder) { enc.OpenNamespace("outer") @@ -322,17 +353,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeTime", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: func(time.Time, PrimitiveArrayEncoder) {}, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(time.Time, PrimitiveArrayEncoder) {}, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n", @@ -341,17 +373,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeDuration", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {}, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {}, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n", @@ -360,17 +393,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeLevel", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: func(Level, PrimitiveArrayEncoder) {}, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: func(Level, PrimitiveArrayEncoder) {}, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -378,35 +412,56 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeCaller", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {}, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {}, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", }, + { + desc: "handle no-op EncodeLoggerName", + cfg: EncoderConfig{ + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: func(string, PrimitiveArrayEncoder) {}, + }, + expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", + expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", + }, { desc: "use custom line separator", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: "\r\n", - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: "\r\n", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\r\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\r\n", @@ -414,16 +469,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "omit line separator definition - fall back to default", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeLoggerName: base.EncodeLoggerName, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + DefaultLineEnding, expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack" + DefaultLineEnding, @@ -559,6 +615,28 @@ func TestCallerEncoders(t *testing.T) { } } +func TestLoggerNameEncoders(t *testing.T) { + tests := []struct { + name string + expected interface{} // output of encoding InfoLevel + }{ + {"capital", "MAIN"}, + {"", "main"}, + {"something-random", "main"}, + } + + for _, tt := range tests { + var lne LoggerNameEncoder + require.NoError(t, lne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + assertAppended( + t, + tt.expected, + func(arr ArrayEncoder) { lne("main", arr) }, + "Unexpected output serializing logger name with %q.", tt.name, + ) + } +} + func assertAppended(t testing.TB, expected interface{}, f func(ArrayEncoder), msgAndArgs ...interface{}) { mem := NewMapObjectEncoder() mem.AddArray("k", ArrayMarshalerFunc(func(arr ArrayEncoder) error { diff --git a/zapcore/json_encoder.go b/zapcore/json_encoder.go index 7d4a22c8d..7a1690bda 100644 --- a/zapcore/json_encoder.go +++ b/zapcore/json_encoder.go @@ -302,7 +302,13 @@ func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, } if ent.LoggerName != "" && final.NameKey != "" { final.addKey(final.NameKey) - final.AppendString(ent.LoggerName) + cur := final.buf.Len() + final.EncodeLoggerName(ent.LoggerName, final) + if cur == final.buf.Len() { + // User-supplied EncodeLoggerName was a no-op. Fall back to strings to + // keep output JSON valid. + final.AppendString(ent.LoggerName) + } } if ent.Caller.Defined && final.CallerKey != "" { final.addKey(final.CallerKey) From 3075347a9cb559b69d098fff2820bafb7af24ac7 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 10 Jul 2017 16:30:36 +0300 Subject: [PATCH 2/5] addressed comments and backwards compatibility --- zapcore/console_encoder.go | 12 +- zapcore/encoder.go | 38 ++- zapcore/encoder_test.go | 458 ++++++++++++++++++------------------- zapcore/json_encoder.go | 12 +- 4 files changed, 258 insertions(+), 262 deletions(-) diff --git a/zapcore/console_encoder.go b/zapcore/console_encoder.go index f62b48dc9..57a652690 100644 --- a/zapcore/console_encoder.go +++ b/zapcore/console_encoder.go @@ -79,8 +79,16 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if c.LevelKey != "" && c.EncodeLevel != nil { c.EncodeLevel(ent.Level, arr) } - if ent.LoggerName != "" && c.NameKey != "" && c.EncodeLoggerName != nil { - c.EncodeLoggerName(ent.LoggerName, arr) + if ent.LoggerName != "" && c.NameKey != "" { + nameEncoder := c.EncodeName + + // if no name encoder provided, fall back to FullNameEncoder for backwards + // compatibility + if nameEncoder == nil { + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, arr) } if ent.Caller.Defined && c.CallerKey != "" && c.EncodeCaller != nil { c.EncodeCaller(ent.Caller, arr) diff --git a/zapcore/encoder.go b/zapcore/encoder.go index 1a29f442f..766a237f2 100644 --- a/zapcore/encoder.go +++ b/zapcore/encoder.go @@ -21,7 +21,6 @@ package zapcore import ( - "strings" "time" "go.uber.org/zap/buffer" @@ -197,27 +196,20 @@ func (e *CallerEncoder) UnmarshalText(text []byte) error { return nil } -// A LoggerNameEncoder serializes a LoggerName to a primitive type. -type LoggerNameEncoder func(string, PrimitiveArrayEncoder) +// A NameEncoder serializes a LoggerName to a primitive type. +type NameEncoder func(string, PrimitiveArrayEncoder) -// FullLoggerNameEncoder serializes a logger name as is -func FullLoggerNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { +// FullNameEncoder serializes a logger name as is +func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { enc.AppendString(loggerName) } -// CapitalLoggerNameEncoder serializes a logger name in all caps -func CapitalLoggerNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { - enc.AppendString(strings.ToUpper(loggerName)) -} - -// UnmarshalText unmarshals text to a LoggerNameEncoder. "capital" is unmarshaled to -// CapitalLoggerNameEncoder and anything else is unmarshaled to FullLoggerNameEncoder. -func (e *LoggerNameEncoder) UnmarshalText(text []byte) error { +// UnmarshalText unmarshals text to a NameEncoder. "capital" is unmarshaled to +// CapitalNameEncoder and anything else is unmarshaled to FullNameEncoder. +func (e *NameEncoder) UnmarshalText(text []byte) error { switch string(text) { - case "capital": - *e = CapitalLoggerNameEncoder default: - *e = FullLoggerNameEncoder + *e = FullNameEncoder } return nil } @@ -236,12 +228,14 @@ type EncoderConfig struct { LineEnding string `json:"lineEnding" yaml:"lineEnding"` // Configure the primitive representations of common complex types. For // example, some users may want all time.Times serialized as floating-point - // seconds since epoch, while others may prefer ISO8601 strings. - EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` - EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` - EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` - EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` - EncodeLoggerName LoggerNameEncoder `json:"nameEncoder" yaml:"nameEncoder"` + // seconds since epoch, while others may prefer ISO8601 strings. EncodeName + // is optional - initializing it to nil will fall back to printing the logger + // name as is (using FullNameEncoder) + EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` + EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` + EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` + EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` + EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` } // ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a diff --git a/zapcore/encoder_test.go b/zapcore/encoder_test.go index b8f344624..72c66284b 100644 --- a/zapcore/encoder_test.go +++ b/zapcore/encoder_test.go @@ -21,6 +21,7 @@ package zapcore_test import ( + "strings" "testing" "time" @@ -44,18 +45,17 @@ var ( func testEncoderConfig() EncoderConfig { return EncoderConfig{ - MessageKey: "msg", - LevelKey: "level", - NameKey: "name", - TimeKey: "ts", - CallerKey: "caller", - StacktraceKey: "stacktrace", - LineEnding: "\n", - EncodeTime: EpochTimeEncoder, - EncodeLevel: LowercaseLevelEncoder, - EncodeDuration: SecondsDurationEncoder, - EncodeCaller: ShortCallerEncoder, - EncodeLoggerName: FullLoggerNameEncoder, + MessageKey: "msg", + LevelKey: "level", + NameKey: "name", + TimeKey: "ts", + CallerKey: "caller", + StacktraceKey: "stacktrace", + LineEnding: "\n", + EncodeTime: EpochTimeEncoder, + EncodeLevel: LowercaseLevelEncoder, + EncodeDuration: SecondsDurationEncoder, + EncodeCaller: ShortCallerEncoder, } } @@ -75,6 +75,11 @@ func withConsoleEncoder(f func(Encoder)) { f(NewConsoleEncoder(humanEncoderConfig())) } +// CapitalNameEncoder serializes a logger name in all caps +func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { + enc.AppendString(strings.ToUpper(loggerName)) +} + func TestEncoderConfiguration(t *testing.T) { base := testEncoderConfig() @@ -99,18 +104,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use custom entry keys in JSON output and ignore them in console output", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -118,18 +122,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip level if LevelKey is omitted", cfg: EncoderConfig{ - LevelKey: "", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -137,18 +140,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip timestamp if TimeKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -156,18 +158,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip message if MessageKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack\n", @@ -175,18 +176,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip name if NameKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", @@ -194,18 +194,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip caller if CallerKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", @@ -213,18 +212,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "skip stacktrace if StacktraceKey is omitted", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n", @@ -232,18 +230,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeTime, for both the entry and any times added", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) }, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(t time.Time, enc PrimitiveArrayEncoder) { enc.AppendString(t.String()) }, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, extra: func(enc Encoder) { enc.AddTime("extra", _epoch) @@ -260,18 +257,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeDuration for any durations added", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: StringDurationEncoder, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: StringDurationEncoder, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, extra: func(enc Encoder) { enc.AddDuration("extra", time.Second) @@ -288,37 +284,36 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use the supplied EncodeLevel", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: CapitalLevelEncoder, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: CapitalLevelEncoder, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"INFO","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack\n", }, { - desc: "use the supplied EncodeLoggerName", + desc: "use the supplied EncodeName", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: CapitalLoggerNameEncoder, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeName: capitalNameEncoder, }, expectedJSON: `{"L":"info","T":0,"N":"MAIN","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tMAIN\tfoo.go:42\thello\nfake-stack\n", @@ -326,18 +321,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "close all open namespaces", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, extra: func(enc Encoder) { enc.OpenNamespace("outer") @@ -353,18 +347,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeTime", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: func(time.Time, PrimitiveArrayEncoder) {}, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: func(time.Time, PrimitiveArrayEncoder) {}, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, extra: func(enc Encoder) { enc.AddTime("sometime", time.Unix(0, 100)) }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n", @@ -373,18 +366,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeDuration", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {}, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: func(time.Duration, PrimitiveArrayEncoder) {}, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, extra: func(enc Encoder) { enc.AddDuration("someduration", time.Microsecond) }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n", @@ -393,18 +385,17 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeLevel", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: func(Level, PrimitiveArrayEncoder) {}, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: func(Level, PrimitiveArrayEncoder) {}, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack\n", @@ -412,24 +403,23 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeCaller", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {}, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: func(EntryCaller, PrimitiveArrayEncoder) {}, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n", }, { - desc: "handle no-op EncodeLoggerName", + desc: "handle no-op EncodeName", cfg: EncoderConfig{ LevelKey: "L", TimeKey: "T", @@ -442,7 +432,7 @@ func TestEncoderConfiguration(t *testing.T) { EncodeDuration: base.EncodeDuration, EncodeLevel: base.EncodeLevel, EncodeCaller: base.EncodeCaller, - EncodeLoggerName: func(string, PrimitiveArrayEncoder) {}, + EncodeName: func(string, PrimitiveArrayEncoder) {}, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", @@ -450,36 +440,33 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "use custom line separator", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: "\r\n", - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: "\r\n", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\r\n", expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack\r\n", }, { desc: "omit line separator definition - fall back to default", - cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeLoggerName: base.EncodeLoggerName, + cfg: EncoderConfig{ LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + DefaultLineEnding, expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack" + DefaultLineEnding, @@ -615,23 +602,22 @@ func TestCallerEncoders(t *testing.T) { } } -func TestLoggerNameEncoders(t *testing.T) { +func TestNameEncoders(t *testing.T) { tests := []struct { name string expected interface{} // output of encoding InfoLevel }{ - {"capital", "MAIN"}, {"", "main"}, {"something-random", "main"}, } for _, tt := range tests { - var lne LoggerNameEncoder - require.NoError(t, lne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) + var ne NameEncoder + require.NoError(t, ne.UnmarshalText([]byte(tt.name)), "Unexpected error unmarshaling %q.", tt.name) assertAppended( t, tt.expected, - func(arr ArrayEncoder) { lne("main", arr) }, + func(arr ArrayEncoder) { ne("main", arr) }, "Unexpected output serializing logger name with %q.", tt.name, ) } diff --git a/zapcore/json_encoder.go b/zapcore/json_encoder.go index 7a1690bda..1006ba2b1 100644 --- a/zapcore/json_encoder.go +++ b/zapcore/json_encoder.go @@ -303,9 +303,17 @@ func (enc *jsonEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if ent.LoggerName != "" && final.NameKey != "" { final.addKey(final.NameKey) cur := final.buf.Len() - final.EncodeLoggerName(ent.LoggerName, final) + nameEncoder := final.EncodeName + + // if no name encoder provided, fall back to FullNameEncoder for backwards + // compatibility + if nameEncoder == nil { + nameEncoder = FullNameEncoder + } + + nameEncoder(ent.LoggerName, final) if cur == final.buf.Len() { - // User-supplied EncodeLoggerName was a no-op. Fall back to strings to + // User-supplied EncodeName was a no-op. Fall back to strings to // keep output JSON valid. final.AppendString(ent.LoggerName) } From 3cbbe1adadf7aeff33ce8ec838730290939c2cc6 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 10 Jul 2017 16:34:27 +0300 Subject: [PATCH 3/5] fixed linting --- zapcore/encoder_test.go | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/zapcore/encoder_test.go b/zapcore/encoder_test.go index 72c66284b..83a94532f 100644 --- a/zapcore/encoder_test.go +++ b/zapcore/encoder_test.go @@ -421,18 +421,18 @@ func TestEncoderConfiguration(t *testing.T) { { desc: "handle no-op EncodeName", cfg: EncoderConfig{ - LevelKey: "L", - TimeKey: "T", - MessageKey: "M", - NameKey: "N", - CallerKey: "C", - StacktraceKey: "S", - LineEnding: base.LineEnding, - EncodeTime: base.EncodeTime, - EncodeDuration: base.EncodeDuration, - EncodeLevel: base.EncodeLevel, - EncodeCaller: base.EncodeCaller, - EncodeName: func(string, PrimitiveArrayEncoder) {}, + LevelKey: "L", + TimeKey: "T", + MessageKey: "M", + NameKey: "N", + CallerKey: "C", + StacktraceKey: "S", + LineEnding: base.LineEnding, + EncodeTime: base.EncodeTime, + EncodeDuration: base.EncodeDuration, + EncodeLevel: base.EncodeLevel, + EncodeCaller: base.EncodeCaller, + EncodeName: func(string, PrimitiveArrayEncoder) {}, }, expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","S":"fake-stack"}` + "\n", expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack\n", @@ -457,7 +457,8 @@ func TestEncoderConfiguration(t *testing.T) { }, { desc: "omit line separator definition - fall back to default", - cfg: EncoderConfig{ LevelKey: "L", + cfg: EncoderConfig{ + LevelKey: "L", TimeKey: "T", MessageKey: "M", NameKey: "N", From c5de738d281ba47df3fc63842c0f25819eb4fb78 Mon Sep 17 00:00:00 2001 From: Eran Date: Mon, 10 Jul 2017 16:39:47 +0300 Subject: [PATCH 4/5] minor comment fixes --- zapcore/encoder.go | 4 ++-- zapcore/encoder_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zapcore/encoder.go b/zapcore/encoder.go index 766a237f2..c171a96d1 100644 --- a/zapcore/encoder.go +++ b/zapcore/encoder.go @@ -204,8 +204,8 @@ func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { enc.AppendString(loggerName) } -// UnmarshalText unmarshals text to a NameEncoder. "capital" is unmarshaled to -// CapitalNameEncoder and anything else is unmarshaled to FullNameEncoder. +// UnmarshalText unmarshals text to a NameEncoder. everything is unmarshaled to +// FullNameEncoder. func (e *NameEncoder) UnmarshalText(text []byte) error { switch string(text) { default: diff --git a/zapcore/encoder_test.go b/zapcore/encoder_test.go index 83a94532f..d18de9d12 100644 --- a/zapcore/encoder_test.go +++ b/zapcore/encoder_test.go @@ -75,7 +75,7 @@ func withConsoleEncoder(f func(Encoder)) { f(NewConsoleEncoder(humanEncoderConfig())) } -// CapitalNameEncoder serializes a logger name in all caps +// capitalNameEncoder serializes a logger name in all caps func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { enc.AppendString(strings.ToUpper(loggerName)) } From 10be78c13ae654d91466225a24e6084b37cc5e90 Mon Sep 17 00:00:00 2001 From: Akshay Shah Date: Sat, 22 Jul 2017 12:00:56 -0700 Subject: [PATCH 5/5] Small documentation cleanups --- zapcore/console_encoder.go | 3 +-- zapcore/encoder.go | 19 +++++++++++-------- zapcore/encoder_test.go | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/zapcore/console_encoder.go b/zapcore/console_encoder.go index 57a652690..b7875966f 100644 --- a/zapcore/console_encoder.go +++ b/zapcore/console_encoder.go @@ -82,9 +82,8 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if ent.LoggerName != "" && c.NameKey != "" { nameEncoder := c.EncodeName - // if no name encoder provided, fall back to FullNameEncoder for backwards - // compatibility if nameEncoder == nil { + // Fall back to FullNameEncoder for backward compatibility. nameEncoder = FullNameEncoder } diff --git a/zapcore/encoder.go b/zapcore/encoder.go index c171a96d1..f0509522b 100644 --- a/zapcore/encoder.go +++ b/zapcore/encoder.go @@ -196,18 +196,21 @@ func (e *CallerEncoder) UnmarshalText(text []byte) error { return nil } -// A NameEncoder serializes a LoggerName to a primitive type. +// A NameEncoder serializes a period-separated logger name to a primitive +// type. type NameEncoder func(string, PrimitiveArrayEncoder) -// FullNameEncoder serializes a logger name as is +// FullNameEncoder serializes the logger name as-is. func FullNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { enc.AppendString(loggerName) } -// UnmarshalText unmarshals text to a NameEncoder. everything is unmarshaled to -// FullNameEncoder. +// UnmarshalText unmarshals text to a NameEncoder. Currently, everything is +// unmarshaled to FullNameEncoder. func (e *NameEncoder) UnmarshalText(text []byte) error { switch string(text) { + case "full": + *e = FullNameEncoder default: *e = FullNameEncoder } @@ -228,14 +231,14 @@ type EncoderConfig struct { LineEnding string `json:"lineEnding" yaml:"lineEnding"` // Configure the primitive representations of common complex types. For // example, some users may want all time.Times serialized as floating-point - // seconds since epoch, while others may prefer ISO8601 strings. EncodeName - // is optional - initializing it to nil will fall back to printing the logger - // name as is (using FullNameEncoder) + // seconds since epoch, while others may prefer ISO8601 strings. EncodeLevel LevelEncoder `json:"levelEncoder" yaml:"levelEncoder"` EncodeTime TimeEncoder `json:"timeEncoder" yaml:"timeEncoder"` EncodeDuration DurationEncoder `json:"durationEncoder" yaml:"durationEncoder"` EncodeCaller CallerEncoder `json:"callerEncoder" yaml:"callerEncoder"` - EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` + // Unlike the other primitive type encoders, EncodeName is optional. The + // zero value falls back to FullNameEncoder. + EncodeName NameEncoder `json:"nameEncoder" yaml:"nameEncoder"` } // ObjectEncoder is a strongly-typed, encoding-agnostic interface for adding a diff --git a/zapcore/encoder_test.go b/zapcore/encoder_test.go index d18de9d12..04641678c 100644 --- a/zapcore/encoder_test.go +++ b/zapcore/encoder_test.go @@ -75,7 +75,6 @@ func withConsoleEncoder(f func(Encoder)) { f(NewConsoleEncoder(humanEncoderConfig())) } -// capitalNameEncoder serializes a logger name in all caps func capitalNameEncoder(loggerName string, enc PrimitiveArrayEncoder) { enc.AppendString(strings.ToUpper(loggerName)) } @@ -609,6 +608,7 @@ func TestNameEncoders(t *testing.T) { expected interface{} // output of encoding InfoLevel }{ {"", "main"}, + {"full", "main"}, {"something-random", "main"}, }