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 line endings configurable #424

Merged
merged 2 commits into from
May 5, 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
2 changes: 2 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ func NewProductionEncoderConfig() zapcore.EncoderConfig {
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.LowercaseLevelEncoder,
EncodeTime: zapcore.EpochTimeEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
Expand Down Expand Up @@ -124,6 +125,7 @@ func NewDevelopmentEncoderConfig() zapcore.EncoderConfig {
CallerKey: "C",
MessageKey: "M",
StacktraceKey: "S",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeDuration: zapcore.StringDurationEncoder,
Expand Down
6 changes: 5 additions & 1 deletion zapcore/console_encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer,
line.AppendString(ent.Stack)
}

line.AppendByte('\n')
if c.LineEnding != "" {
line.AppendString(c.LineEnding)
} else {
line.AppendString(DefaultLineEnding)
}
return line, nil
}

Expand Down
6 changes: 6 additions & 0 deletions zapcore/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ import (
"go.uber.org/zap/buffer"
)

// DefaultLineEnding defines the default line ending when writing logs.
// Alternate line endings specified in EncoderConfig can override this
// behavior.
const DefaultLineEnding = "\n"

// A LevelEncoder serializes a Level to a primitive type.
type LevelEncoder func(Level, PrimitiveArrayEncoder)

Expand Down Expand Up @@ -202,6 +207,7 @@ type EncoderConfig struct {
NameKey string `json:"nameKey" yaml:"nameKey"`
CallerKey string `json:"callerKey" yaml:"callerKey"`
StacktraceKey string `json:"stacktraceKey" yaml:"stacktraceKey"`
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.
Expand Down
119 changes: 85 additions & 34 deletions zapcore/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func testEncoderConfig() EncoderConfig {
TimeKey: "ts",
CallerKey: "caller",
StacktraceKey: "stacktrace",
LineEnding: "\n",
EncodeTime: EpochTimeEncoder,
EncodeLevel: LowercaseLevelEncoder,
EncodeDuration: SecondsDurationEncoder,
Expand Down Expand Up @@ -91,8 +92,8 @@ func TestEncoderConfiguration(t *testing.T) {
ent.Message = `hello\`
return ent
},
expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","msg":"hello\\","stacktrace":"fake-stack"}`,
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\\\nfake-stack",
expectedJSON: `{"level":"info","ts":0,"name":"main","caller":"foo.go:42","msg":"hello\\","stacktrace":"fake-stack"}` + "\n",
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\\\nfake-stack\n",
},
{
desc: "use custom entry keys in JSON output and ignore them in console output",
Expand All @@ -103,13 +104,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\nfake-stack",
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: "skip level if LevelKey is omitted",
Expand All @@ -120,13 +122,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack",
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",
},
{
desc: "skip timestamp if TimeKey is omitted",
Expand All @@ -137,13 +140,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "info\tmain\tfoo.go:42\thello\nfake-stack",
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",
},
{
desc: "skip message if MessageKey is omitted",
Expand All @@ -154,13 +158,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\tfoo.go:42\nfake-stack",
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",
},
{
desc: "skip name if NameKey is omitted",
Expand All @@ -171,13 +176,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tfoo.go:42\thello\nfake-stack",
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",
},
{
desc: "skip caller if CallerKey is omitted",
Expand All @@ -188,13 +194,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\thello\nfake-stack",
expectedJSON: `{"L":"info","T":0,"N":"main","M":"hello","S":"fake-stack"}` + "\n",
expectedConsole: "0\tinfo\tmain\thello\nfake-stack\n",
},
{
desc: "skip stacktrace if StacktraceKey is omitted",
Expand All @@ -205,13 +212,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello",
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello"}` + "\n",
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\n",
},
{
desc: "use the supplied EncodeTime, for both the entry and any times added",
Expand All @@ -222,6 +230,7 @@ func TestEncoderConfiguration(t *testing.T) {
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,
Expand All @@ -234,10 +243,10 @@ func TestEncoderConfiguration(t *testing.T) {
return nil
}))
},
expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}`,
expectedJSON: `{"L":"info","T":"1970-01-01 00:00:00 +0000 UTC","N":"main","C":"foo.go:42","M":"hello","extra":"1970-01-01 00:00:00 +0000 UTC","extras":["1970-01-01 00:00:00 +0000 UTC"],"S":"fake-stack"}` + "\n",
expectedConsole: "1970-01-01 00:00:00 +0000 UTC\tinfo\tmain\tfoo.go:42\thello\t" + // plain-text preamble
`{"extra": "1970-01-01 00:00:00 +0000 UTC", "extras": ["1970-01-01 00:00:00 +0000 UTC"]}` + // JSON context
"\nfake-stack", // stacktrace after newline
"\nfake-stack\n", // stacktrace after newline
},
{
desc: "use the supplied EncodeDuration for any durations added",
Expand All @@ -248,6 +257,7 @@ func TestEncoderConfiguration(t *testing.T) {
NameKey: "N",
CallerKey: "C",
StacktraceKey: "S",
LineEnding: base.LineEnding,
EncodeTime: base.EncodeTime,
EncodeDuration: StringDurationEncoder,
EncodeLevel: base.EncodeLevel,
Expand All @@ -260,10 +270,10 @@ func TestEncoderConfiguration(t *testing.T) {
return nil
}))
},
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}`,
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","extra":"1s","extras":["1m0s"],"S":"fake-stack"}` + "\n",
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + // preamble
`{"extra": "1s", "extras": ["1m0s"]}` + // context
"\nfake-stack", // stacktrace
"\nfake-stack\n", // stacktrace
},
{
desc: "use the supplied EncodeLevel",
Expand All @@ -274,13 +284,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tINFO\tmain\tfoo.go:42\thello\nfake-stack",
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",
Expand All @@ -291,6 +302,7 @@ func TestEncoderConfiguration(t *testing.T) {
NameKey: "N",
CallerKey: "C",
StacktraceKey: "S",
LineEnding: base.LineEnding,
EncodeTime: base.EncodeTime,
EncodeDuration: base.EncodeDuration,
EncodeLevel: base.EncodeLevel,
Expand All @@ -302,10 +314,10 @@ func TestEncoderConfiguration(t *testing.T) {
enc.AddString("foo", "bar")
enc.OpenNamespace("innermost")
},
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}`,
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","outer":{"inner":{"foo":"bar","innermost":{}}},"S":"fake-stack"}` + "\n",
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" +
`{"outer": {"inner": {"foo": "bar", "innermost": {}}}}` +
"\nfake-stack",
"\nfake-stack\n",
},
{
desc: "handle no-op EncodeTime",
Expand All @@ -316,14 +328,15 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "info\tmain\tfoo.go:42\thello\t" + `{"sometime": 100}` + "\nfake-stack",
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","sometime":100,"S":"fake-stack"}` + "\n",
expectedConsole: "info\tmain\tfoo.go:42\thello\t" + `{"sometime": 100}` + "\nfake-stack\n",
},
{
desc: "handle no-op EncodeDuration",
Expand All @@ -334,14 +347,15 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + `{"someduration": 1000}` + "\nfake-stack",
expectedJSON: `{"L":"info","T":0,"N":"main","C":"foo.go:42","M":"hello","someduration":1000,"S":"fake-stack"}` + "\n",
expectedConsole: "0\tinfo\tmain\tfoo.go:42\thello\t" + `{"someduration": 1000}` + "\nfake-stack\n",
},
{
desc: "handle no-op EncodeLevel",
Expand All @@ -352,13 +366,14 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tmain\tfoo.go:42\thello\nfake-stack",
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",
},
{
desc: "handle no-op EncodeCaller",
Expand All @@ -369,13 +384,49 @@ func TestEncoderConfiguration(t *testing.T) {
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"}`,
expectedConsole: "0\tinfo\tmain\thello\nfake-stack",
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: "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,
},
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,
},
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,
},
}

Expand All @@ -394,7 +445,7 @@ func TestEncoderConfiguration(t *testing.T) {
if assert.NoError(t, jsonErr, "Unexpected error JSON-encoding entry in case #%d.", i) {
assert.Equal(
t,
tt.expectedJSON+"\n",
tt.expectedJSON,
jsonOut.String(),
"Unexpected JSON output: expected to %v.", tt.desc,
)
Expand All @@ -403,7 +454,7 @@ func TestEncoderConfiguration(t *testing.T) {
if assert.NoError(t, consoleErr, "Unexpected error console-encoding entry in case #%d.", i) {
assert.Equal(
t,
tt.expectedConsole+"\n",
tt.expectedConsole,
consoleOut.String(),
"Unexpected console output: expected to %v.", tt.desc,
)
Expand Down
Loading