| // 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] |
| } |
| return multiWriteSyncer(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 |
| } |