diff --git a/zapcore/write_syncer.go b/zapcore/write_syncer.go index d2292cb02..dadc25f36 100644 --- a/zapcore/write_syncer.go +++ b/zapcore/write_syncer.go @@ -40,12 +40,26 @@ type WriteSyncer interface { func AddSync(w io.Writer) WriteSyncer { switch w := w.(type) { case WriteSyncer: + if !checkSync(w.(WriteSyncer)) { + return writerWrapper{w} + } return w default: return writerWrapper{w} } } +// checkSync executes a Sync() on the WriteSyncer to check whether it operates +// cleanly. In the particular case of the WriteSyncer being a special file, +// calling Sync will return an `os.ErrInvalid` error. If, for whatever reason, +// the call of `Sync()` returns an error, this will return a `false`. +func checkSync(w WriteSyncer) bool { + if err := w.Sync(); err != nil { + return false + } + return true +} + type lockedWriteSyncer struct { sync.Mutex ws WriteSyncer diff --git a/zapcore/write_syncer_test.go b/zapcore/write_syncer_test.go index 386f13013..b67230531 100644 --- a/zapcore/write_syncer_test.go +++ b/zapcore/write_syncer_test.go @@ -23,6 +23,8 @@ package zapcore import ( "bytes" "errors" + "io/ioutil" + "os" "testing" "io" @@ -65,6 +67,20 @@ func TestAddSyncWriter(t *testing.T) { assert.NoError(t, ws.Sync(), "Unexpected error calling a no-op Sync method.") } +func TestCheckSync(t *testing.T) { + assert.False(t, checkSync(os.Stdout), "Expected os.Stdout to fail sync check") + assert.False(t, checkSync(os.Stderr), "Expected os.Stderr to fail sync check") + + // Test a real file, which should pass + tmpFile, err := ioutil.TempFile("/tmp", "zapcore-test") + if err != nil { + t.SkipNow() + return + } + defer tmpFile.Close() + assert.True(t, checkSync(tmpFile), "Expected os.Stderr to fail sync check") +} + func TestMultiWriteSyncerWritesBoth(t *testing.T) { first := &bytes.Buffer{} second := &bytes.Buffer{}