write_syncer.go raw

   1  // Copyright (c) 2016 Uber Technologies, Inc.
   2  //
   3  // Permission is hereby granted, free of charge, to any person obtaining a copy
   4  // of this software and associated documentation files (the "Software"), to deal
   5  // in the Software without restriction, including without limitation the rights
   6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   7  // copies of the Software, and to permit persons to whom the Software is
   8  // furnished to do so, subject to the following conditions:
   9  //
  10  // The above copyright notice and this permission notice shall be included in
  11  // all copies or substantial portions of the Software.
  12  //
  13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19  // THE SOFTWARE.
  20  
  21  package zapcore
  22  
  23  import (
  24  	"io"
  25  	"sync"
  26  
  27  	"go.uber.org/multierr"
  28  )
  29  
  30  // A WriteSyncer is an io.Writer that can also flush any buffered data. Note
  31  // that *os.File (and thus, os.Stderr and os.Stdout) implement WriteSyncer.
  32  type WriteSyncer interface {
  33  	io.Writer
  34  	Sync() error
  35  }
  36  
  37  // AddSync converts an io.Writer to a WriteSyncer. It attempts to be
  38  // intelligent: if the concrete type of the io.Writer implements WriteSyncer,
  39  // we'll use the existing Sync method. If it doesn't, we'll add a no-op Sync.
  40  func AddSync(w io.Writer) WriteSyncer {
  41  	switch w := w.(type) {
  42  	case WriteSyncer:
  43  		return w
  44  	default:
  45  		return writerWrapper{w}
  46  	}
  47  }
  48  
  49  type lockedWriteSyncer struct {
  50  	sync.Mutex
  51  	ws WriteSyncer
  52  }
  53  
  54  // Lock wraps a WriteSyncer in a mutex to make it safe for concurrent use. In
  55  // particular, *os.Files must be locked before use.
  56  func Lock(ws WriteSyncer) WriteSyncer {
  57  	if _, ok := ws.(*lockedWriteSyncer); ok {
  58  		// no need to layer on another lock
  59  		return ws
  60  	}
  61  	return &lockedWriteSyncer{ws: ws}
  62  }
  63  
  64  func (s *lockedWriteSyncer) Write(bs []byte) (int, error) {
  65  	s.Lock()
  66  	n, err := s.ws.Write(bs)
  67  	s.Unlock()
  68  	return n, err
  69  }
  70  
  71  func (s *lockedWriteSyncer) Sync() error {
  72  	s.Lock()
  73  	err := s.ws.Sync()
  74  	s.Unlock()
  75  	return err
  76  }
  77  
  78  type writerWrapper struct {
  79  	io.Writer
  80  }
  81  
  82  func (w writerWrapper) Sync() error {
  83  	return nil
  84  }
  85  
  86  type multiWriteSyncer []WriteSyncer
  87  
  88  // NewMultiWriteSyncer creates a WriteSyncer that duplicates its writes
  89  // and sync calls, much like io.MultiWriter.
  90  func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer {
  91  	if len(ws) == 1 {
  92  		return ws[0]
  93  	}
  94  	return multiWriteSyncer(ws)
  95  }
  96  
  97  // See https://golang.org/src/io/multi.go
  98  // When not all underlying syncers write the same number of bytes,
  99  // the smallest number is returned even though Write() is called on
 100  // all of them.
 101  func (ws multiWriteSyncer) Write(p []byte) (int, error) {
 102  	var writeErr error
 103  	nWritten := 0
 104  	for _, w := range ws {
 105  		n, err := w.Write(p)
 106  		writeErr = multierr.Append(writeErr, err)
 107  		if nWritten == 0 && n != 0 {
 108  			nWritten = n
 109  		} else if n < nWritten {
 110  			nWritten = n
 111  		}
 112  	}
 113  	return nWritten, writeErr
 114  }
 115  
 116  func (ws multiWriteSyncer) Sync() error {
 117  	var err error
 118  	for _, w := range ws {
 119  		err = multierr.Append(err, w.Sync())
 120  	}
 121  	return err
 122  }
 123