diff --git a/writer.go b/writer.go index 381b0f6..98c932e 100644 --- a/writer.go +++ b/writer.go @@ -56,30 +56,30 @@ type multiLevelWriter struct { func (t multiLevelWriter) Write(p []byte) (n int, err error) { for _, w := range t.writers { - n, err = w.Write(p) - if err != nil { - return - } - if n != len(p) { - err = io.ErrShortWrite - return + if _n, _err := w.Write(p); err == nil { + n = _n + if _err != nil { + err = _err + } else if _n != len(p) { + err = io.ErrShortWrite + } } } - return len(p), nil + return n, err } func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) { for _, w := range t.writers { - n, err = w.WriteLevel(l, p) - if err != nil { - return - } - if n != len(p) { - err = io.ErrShortWrite - return + if _n, _err := w.WriteLevel(l, p); err == nil { + n = _n + if _err != nil { + err = _err + } else if _n != len(p) { + err = io.ErrShortWrite + } } } - return len(p), nil + return n, err } // MultiLevelWriter creates a writer that duplicates its writes to all the diff --git a/writer_test.go b/writer_test.go index 7d06e4b..6d55ad1 100644 --- a/writer_test.go +++ b/writer_test.go @@ -4,6 +4,8 @@ package zerolog import ( + "errors" + "io" "reflect" "testing" ) @@ -27,3 +29,85 @@ func TestMultiSyslogWriter(t *testing.T) { t.Errorf("Invalid syslog message routing: want %v, got %v", want, got) } } + +var writeCalls int + +type mockedWriter struct { + wantErr bool +} + +func (c mockedWriter) Write(p []byte) (int, error) { + writeCalls++ + + if c.wantErr { + return -1, errors.New("Expected error") + } + + return len(p), nil +} + +// Tests that a new writer is only used if it actually works. +func TestResilientMultiWriter(t *testing.T) { + tests := []struct { + name string + writers []io.Writer + }{ + { + name: "All valid writers", + writers: []io.Writer{ + mockedWriter { + wantErr: false, + }, + mockedWriter { + wantErr: false, + }, + }, + }, + { + name: "All invalid writers", + writers: []io.Writer{ + mockedWriter { + wantErr: true, + }, + mockedWriter { + wantErr: true, + }, + }, + }, + { + name: "First invalid writer", + writers: []io.Writer{ + mockedWriter { + wantErr: true, + }, + mockedWriter { + wantErr: false, + }, + }, + }, + { + name: "First valid writer", + writers: []io.Writer{ + mockedWriter { + wantErr: false, + }, + mockedWriter { + wantErr: true, + }, + }, + }, + } + + for _, tt := range tests { + writers := tt.writers + multiWriter := MultiLevelWriter(writers...) + + logger := New(multiWriter).With().Timestamp().Logger().Level(InfoLevel) + logger.Info().Msg("Test msg") + + if len(writers) != writeCalls { + t.Errorf("Expected %d writers to have been called but only %d were.", len(writers), writeCalls) + } + writeCalls = 0 + } +} \ No newline at end of file