...

Source file src/cdr.dev/slog/internal/syncwriter/syncwriter.go

Documentation: cdr.dev/slog/internal/syncwriter

     1  // Package syncwriter implements a concurrency safe io.Writer wrapper.
     2  package syncwriter
     3  
     4  import (
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"sync"
    10  	"syscall"
    11  )
    12  
    13  // Writer implements a concurrency safe io.Writer wrapper.
    14  type Writer struct {
    15  	mu sync.Mutex
    16  	w  io.Writer
    17  
    18  	errorf func(f string, v ...interface{})
    19  }
    20  
    21  // New returns a new Writer that writes to w.
    22  func New(w io.Writer) *Writer {
    23  	return &Writer{
    24  		w: w,
    25  
    26  		errorf: func(f string, v ...interface{}) {
    27  			println(fmt.Sprintf(f, v...))
    28  		},
    29  	}
    30  }
    31  
    32  func (w *Writer) Write(name string, p []byte) {
    33  	w.mu.Lock()
    34  	defer w.mu.Unlock()
    35  	_, err := w.w.Write(p)
    36  	if err != nil {
    37  		w.errorf("%v: failed to write entry: %+v", name, err)
    38  	}
    39  }
    40  
    41  type syncer interface {
    42  	Sync() error
    43  }
    44  
    45  var _ syncer = &os.File{}
    46  
    47  // Sync calls Sync on the underlying writer
    48  // if possible.
    49  func (w *Writer) Sync(sinkName string) {
    50  	w.mu.Lock()
    51  	defer w.mu.Unlock()
    52  
    53  	s, ok := w.w.(syncer)
    54  	if !ok {
    55  		return
    56  	}
    57  	err := s.Sync()
    58  	if _, ok := w.w.(*os.File); ok {
    59  		// Opened files do not necessarily support syncing.
    60  		// E.g. stdout and stderr both do not so we need
    61  		// to ignore these errors.
    62  		// See https://github.com/uber-go/zap/issues/370
    63  		// See https://github.com/cdr/slog/pull/43
    64  		if errorsIsAny(err, syscall.EINVAL, syscall.ENOTTY, syscall.EBADF) {
    65  			return
    66  		}
    67  	}
    68  
    69  	w.errorf("failed to sync %v: %+v", sinkName, err)
    70  }
    71  
    72  func errorsIsAny(err error, errs ...error) bool {
    73  	for _, e := range errs {
    74  		if errors.Is(err, e) {
    75  			return true
    76  		}
    77  	}
    78  	return false
    79  }
    80  

View as plain text