diff --git a/writer.go b/writer.go index 67b478c50..121db9826 100644 --- a/writer.go +++ b/writer.go @@ -36,15 +36,13 @@ import ( // Passing no paths returns a no-op WriteSyncer. The special paths "stdout" and // "stderr" are interpreted as os.Stdout and os.Stderr, respectively. func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { - if len(paths) == 0 { - return zapcore.AddSync(ioutil.Discard), func() {}, nil - } - writers, close, err := open(paths) - if len(writers) == 1 { - return zapcore.Lock(writers[0]), close, err + if err != nil { + return nil, nil, err } - return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)), close, err + + writer := CombineWriteSyncers(writers...) + return writer, close, nil } func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { @@ -76,3 +74,12 @@ func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { } return writers, close, errs.AsError() } + +// CombineWriteSyncers combines the passed set of WriteSyncer objects into a +// locked WriteSyncer. +func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { + if len(writers) == 0 { + return zapcore.AddSync(ioutil.Discard) + } + return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) +} diff --git a/writer_test.go b/writer_test.go index 0890f8eb5..9cc3e775d 100644 --- a/writer_test.go +++ b/writer_test.go @@ -81,3 +81,23 @@ func TestOpen(t *testing.T) { assert.Equal(t, tt.filenames, names, "Opened unexpected files given paths %v.", tt.paths) } } + +type testWriter struct { + expected string + t testing.TB +} + +func (w *testWriter) Write(actual []byte) (int, error) { + assert.Equal(w.t, []byte(w.expected), actual, "Unexpected write error.") + return len(actual), nil +} + +func (w *testWriter) Sync() error { + return nil +} + +func TestCombineWriteSyncers(t *testing.T) { + tw := &testWriter{"test", t} + w := CombineWriteSyncers(tw) + w.Write([]byte("test")) +} diff --git a/zapcore/write_syncer.go b/zapcore/write_syncer.go index d2292cb02..2a9237640 100644 --- a/zapcore/write_syncer.go +++ b/zapcore/write_syncer.go @@ -88,6 +88,9 @@ type multiWriteSyncer []WriteSyncer // NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes // and sync calls, much like io.MultiWriter. func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { + if len(ws) == 1 { + return ws[0] + } // Copy to protect against https://github.com/golang/go/issues/7809 return multiWriteSyncer(append([]WriteSyncer(nil), ws...)) } diff --git a/zapcore/write_syncer_test.go b/zapcore/write_syncer_test.go index 386f13013..43c28cef1 100644 --- a/zapcore/write_syncer_test.go +++ b/zapcore/write_syncer_test.go @@ -65,6 +65,16 @@ func TestAddSyncWriter(t *testing.T) { assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.") } +func TestNewMultiWriteSyncerWorksForSingleWriter(t *testing.T) { + w := &testutils.Buffer{} + + ws := NewMultiWriteSyncer(w) + assert.Equal(t, w, ws, "Expected NewMultiWriteSyncer to return the same WriteSyncer object for a single argument.") + + ws.Sync() + assert.True(t, w.Called(), "Expected Sync to be called on the created WriteSyncer") +} + func TestMultiWriteSyncerWritesBoth(t *testing.T) { first := &bytes.Buffer{} second := &bytes.Buffer{}