// Copyright (c) 2016 Uber Technologies, Inc. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. package zapcore import ( "io" "sync" "go.uber.org/multierr" ) // A WriteSyncer is an io.Writer that can also flush any buffered data. Note // that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer. type WriteSyncer interface { io.Writer Sync() error } // AddSync converts an io.Writer to a WriteSyncer. It attempts to be // intelligent: if the concrete type of the io.Writer implements WriteSyncer, // we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync. func AddSync(w io.Writer) WriteSyncer { switch w := w.(type) { case WriteSyncer: return w default: return writerWrapper{w} } } type lockedWriteSyncer struct { sync.Mutex ws WriteSyncer } // Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In // particular, *os.Files must be locked before use. func Lock(ws WriteSyncer) WriteSyncer { if _, ok := ws.(*lockedWriteSyncer); ok { // no need to layer on another lock return ws } return &lockedWriteSyncer{ws: ws} } func (s *lockedWriteSyncer) Write(bs []byte) (int, error) { s.Lock() n, err := s.ws.Write(bs) s.Unlock() return n, err } func (s *lockedWriteSyncer) Sync() error { s.Lock() err := s.ws.Sync() s.Unlock() return err } type writerWrapper struct { io.Writer } func (w writerWrapper) Sync() error { return nil } 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...)) } // See https://golang.org/src/io/multi.go // When not all underlying syncers write the same number of bytes, // the smallest number is returned even though Write() is called on // all of them. func (ws multiWriteSyncer) Write(p []byte) (int, error) { var writeErr error nWritten := 0 for _, w := range ws { n, err := w.Write(p) writeErr = multierr.Append(writeErr, err) if nWritten == 0 && n != 0 { nWritten = n } else if n < nWritten { nWritten = n } } return nWritten, writeErr } func (ws multiWriteSyncer) Sync() error { var err error for _, w := range ws { err = multierr.Append(err, w.Sync()) } return err }