diff --git a/console_test.go b/console_test.go index 8490c9e..6b97f6c 100644 --- a/console_test.go +++ b/console_test.go @@ -2,9 +2,11 @@ package zerolog_test import ( "bytes" + "encoding/json" "fmt" "io" "os" + "path/filepath" "strings" "testing" "time" @@ -315,16 +317,33 @@ func TestConsoleWriter(t *testing.T) { ts := time.Unix(0, 0) d := ts.UTC().Format(time.RFC3339) - evt := `{"time": "` + d + `", "level": "debug", "message": "Foobar", "foo": "bar", "caller": "` + cwd + `/foo/bar.go"}` - // t.Log(evt) - _, err = w.Write([]byte(evt)) + fields := map[string]interface{}{ + "time": d, + "level": "debug", + "message": "Foobar", + "foo": "bar", + "caller": filepath.Join(cwd, "foo", "bar.go"), + } + + evt, err := json.Marshal(fields) + if err != nil { + t.Fatalf("Cannot marshal fields: %s", err) + } + + _, err = w.Write(evt) if err != nil { t.Errorf("Unexpected error when writing output: %s", err) } + // Define the expected output with forward slashes expectedOutput := ts.Format(time.Kitchen) + " DBG foo/bar.go > Foobar foo=bar\n" + + // Get the actual output and normalize path separators to forward slashes actualOutput := buf.String() + actualOutput = strings.ReplaceAll(actualOutput, string(os.PathSeparator), "/") + + // Compare the normalized actual output to the expected output if actualOutput != expectedOutput { t.Errorf("Unexpected output %q, want: %q", actualOutput, expectedOutput) } @@ -451,14 +470,14 @@ func TestConsoleWriterConfiguration(t *testing.T) { }) t.Run("Sets TimeFormat and TimeLocation", func(t *testing.T) { - locs := []*time.Location{ time.Local, time.UTC } + locs := []*time.Location{time.Local, time.UTC} for _, location := range locs { buf := &bytes.Buffer{} w := zerolog.ConsoleWriter{ - Out: buf, - NoColor: true, - TimeFormat: time.RFC3339, + Out: buf, + NoColor: true, + TimeFormat: time.RFC3339, TimeLocation: location, } diff --git a/diode/diode_test.go b/diode/diode_test.go index ab6b55e..473076c 100644 --- a/diode/diode_test.go +++ b/diode/diode_test.go @@ -76,6 +76,58 @@ func TestFatal(t *testing.T) { } } +type SlowWriter struct{} + +func (rw *SlowWriter) Write(p []byte) (n int, err error) { + time.Sleep(200 * time.Millisecond) + fmt.Print(string(p)) + return len(p), nil +} + +func TestFatalWithFilteredLevelWriter(t *testing.T) { + if os.Getenv("TEST_FATAL_SLOW") == "1" { + slowWriter := SlowWriter{} + diodeWriter := diode.NewWriter(&slowWriter, 500, 0, func(missed int) { + fmt.Printf("Missed %d logs\n", missed) + }) + leveledDiodeWriter := zerolog.LevelWriterAdapter{ + Writer: &diodeWriter, + } + filteredDiodeWriter := zerolog.FilteredLevelWriter{ + Writer: &leveledDiodeWriter, + Level: zerolog.InfoLevel, + } + logger := zerolog.New(&filteredDiodeWriter) + logger.Fatal().Msg("test") + return + } + + cmd := exec.Command(os.Args[0], "-test.run=TestFatalWithFilteredLevelWriter") + cmd.Env = append(os.Environ(), "TEST_FATAL_SLOW=1") + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + err = cmd.Start() + if err != nil { + t.Fatal(err) + } + slurp, err := io.ReadAll(stdout) + if err != nil { + t.Fatal(err) + } + err = cmd.Wait() + if err == nil { + t.Error("Expected log.Fatal to exit with non-zero status") + } + + got := cbor.DecodeIfBinaryToString(slurp) + want := "{\"level\":\"fatal\",\"message\":\"test\"}\n" + if got != want { + t.Errorf("Expected output %q, got: %q", want, got) + } +} + func Benchmark(b *testing.B) { log.SetOutput(io.Discard) defer log.SetOutput(os.Stderr) diff --git a/writer.go b/writer.go index 41b394d..0fc5ff5 100644 --- a/writer.go +++ b/writer.go @@ -213,6 +213,15 @@ func (w *FilteredLevelWriter) WriteLevel(level Level, p []byte) (int, error) { return len(p), nil } +// Call the underlying writer's Close method if it is an io.Closer. Otherwise +// does nothing. +func (w *FilteredLevelWriter) Close() error { + if closer, ok := w.Writer.(io.Closer); ok { + return closer.Close() + } + return nil +} + var triggerWriterPool = &sync.Pool{ New: func() interface{} { return bytes.NewBuffer(make([]byte, 0, 1024))