diff --git a/log/sync.go b/log/sync.go index 8ce0db8..2e84690 100644 --- a/log/sync.go +++ b/log/sync.go @@ -1,4 +1,4 @@ -package log +package log import ( "io" @@ -36,24 +36,59 @@ l.logger.Store(loggerStruct{logger}) } -// SyncWriter synchronizes concurrent writes to an io.Writer. -type SyncWriter struct { - mu sync.Mutex - w io.Writer +// NewSyncWriter returns a new writer that is safe for concurrent use by +// multiple goroutines. Writes to the returned writer are passed on to w. If +// another write is already in progress, the calling goroutine blocks until +// the writer is available. +// +// If w implements the following interface, so does the returned writer. +// +// interface { +// Fd() uintptr +// } +func NewSyncWriter(w io.Writer) io.Writer { + switch w := w.(type) { + case fdWriter: + return &fdSyncWriter{fdWriter: w} + default: + return &syncWriter{Writer: w} + } } -// NewSyncWriter returns a new SyncWriter. The returned writer is safe for -// concurrent use by multiple goroutines. -func NewSyncWriter(w io.Writer) *SyncWriter { - return &SyncWriter{w: w} +// syncWriter synchronizes concurrent writes to an io.Writer. +type syncWriter struct { + sync.Mutex + io.Writer } // Write writes p to the underlying io.Writer. If another write is already in -// progress, the calling goroutine blocks until the SyncWriter is available. -func (w *SyncWriter) Write(p []byte) (n int, err error) { - w.mu.Lock() - n, err = w.w.Write(p) - w.mu.Unlock() +// progress, the calling goroutine blocks until the syncWriter is available. +func (w *syncWriter) Write(p []byte) (n int, err error) { + w.Lock() + n, err = w.Writer.Write(p) + w.Unlock() + return n, err +} + +// fdWriter is an io.Writer that also has an Fd method. The most common +// example of an fdWriter is an *os.File. +type fdWriter interface { + io.Writer + Fd() uintptr +} + +// fdSyncWriter synchronizes concurrent writes to an fdWriter. +type fdSyncWriter struct { + sync.Mutex + fdWriter +} + +// Write writes p to the underlying io.Writer. If another write is already in +// progress, the calling goroutine blocks until the fdSyncWriter is available. +func (w *fdSyncWriter) Write(p []byte) (n int, err error) { + w.Lock() + n, err = w.fdWriter.Write(p) + w.Unlock() return n, err } diff --git a/log/sync_test.go b/log/sync_test.go index 3d915ec..49b171f 100644 --- a/log/sync_test.go +++ b/log/sync_test.go @@ -1,8 +1,9 @@ -package log_test +package log_test import ( "bytes" "io" + "os" "testing" "github.com/go-kit/kit/log" @@ -70,3 +71,13 @@ w = log.NewSyncWriter(w) testConcurrency(t, log.NewLogfmtLogger(w), 10000) } + +func TestSyncWriterFd(t *testing.T) { + _, ok := log.NewSyncWriter(os.Stdout).(interface { + Fd() uintptr + }) + + if !ok { + t.Error("NewSyncWriter does not pass through Fd method") + } +}