From 582007f21da46f136517edb419ed44931ae72b73 Mon Sep 17 00:00:00 2001 From: Felipe Gasper Date: Fri, 1 Mar 2024 19:52:31 -0500 Subject: [PATCH] Add a time.Location to ConsoleWriter. (This allows UTC timestamps.) (#531) * Add a time.Location to ConsoleWriter. (This allows UTC timestamps.) * add test --- console.go | 24 ++++++++++++++++-------- console_test.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/console.go b/console.go index cc6d623e..10c1a1c9 100644 --- a/console.go +++ b/console.go @@ -57,6 +57,10 @@ type ConsoleWriter struct { // TimeFormat specifies the format for timestamp in output. TimeFormat string + // TimeLocation tells ConsoleWriter’s default FormatTimestamp + // how to localize the time. + TimeLocation *time.Location + // PartsOrder defines the order of parts in output. PartsOrder []string @@ -83,9 +87,9 @@ type ConsoleWriter struct { // NewConsoleWriter creates and initializes a new ConsoleWriter. func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter { w := ConsoleWriter{ - Out: os.Stdout, - TimeFormat: consoleDefaultTimeFormat, - PartsOrder: consoleDefaultPartsOrder(), + Out: os.Stdout, + TimeFormat: consoleDefaultTimeFormat, + PartsOrder: consoleDefaultPartsOrder(), } for _, opt := range options { @@ -284,7 +288,7 @@ func (w ConsoleWriter) writePart(buf *bytes.Buffer, evt map[string]interface{}, } case TimestampFieldName: if w.FormatTimestamp == nil { - f = consoleDefaultFormatTimestamp(w.TimeFormat, w.NoColor) + f = consoleDefaultFormatTimestamp(w.TimeFormat, w.TimeLocation, w.NoColor) } else { f = w.FormatTimestamp } @@ -352,19 +356,23 @@ func consoleDefaultPartsOrder() []string { } } -func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter { +func consoleDefaultFormatTimestamp(timeFormat string, location *time.Location, noColor bool) Formatter { if timeFormat == "" { timeFormat = consoleDefaultTimeFormat } + if location == nil { + location = time.Local + } + return func(i interface{}) string { t := "" switch tt := i.(type) { case string: - ts, err := time.ParseInLocation(TimeFieldFormat, tt, time.Local) + ts, err := time.ParseInLocation(TimeFieldFormat, tt, location) if err != nil { t = tt } else { - t = ts.Local().Format(timeFormat) + t = ts.In(location).Format(timeFormat) } case json.Number: i, err := tt.Int64() @@ -385,7 +393,7 @@ func consoleDefaultFormatTimestamp(timeFormat string, noColor bool) Formatter { } ts := time.Unix(sec, nsec) - t = ts.Format(timeFormat) + t = ts.In(location).Format(timeFormat) } } return colorize(t, colorDarkGray, noColor) diff --git a/console_test.go b/console_test.go index acf1ccbf..08121d06 100644 --- a/console_test.go +++ b/console_test.go @@ -343,6 +343,35 @@ func TestConsoleWriterConfiguration(t *testing.T) { } }) + t.Run("Sets TimeFormat and TimeLocation", func(t *testing.T) { + locs := []*time.Location{ time.Local, time.UTC } + + for _, location := range locs { + buf := &bytes.Buffer{} + w := zerolog.ConsoleWriter{ + Out: buf, + NoColor: true, + TimeFormat: time.RFC3339, + TimeLocation: location, + } + + ts := time.Unix(0, 0) + d := ts.UTC().Format(time.RFC3339) + evt := `{"time": "` + d + `", "level": "info", "message": "Foobar"}` + + _, err := w.Write([]byte(evt)) + if err != nil { + t.Errorf("Unexpected error when writing output: %s", err) + } + + expectedOutput := ts.In(location).Format(time.RFC3339) + " INF Foobar\n" + actualOutput := buf.String() + if actualOutput != expectedOutput { + t.Errorf("Unexpected output %q, want: %q (location=%s)", actualOutput, expectedOutput, location) + } + } + }) + t.Run("Sets PartsOrder", func(t *testing.T) { buf := &bytes.Buffer{} w := zerolog.ConsoleWriter{Out: buf, NoColor: true, PartsOrder: []string{"message", "level"}}