// Package output contains writers that lambdalog delivers records to. The
// default is stdout; alternative destinations (CloudWatch, file rotation)
// are in sibling files.
//
// See mercemay.top/src/lambdalog/output/.
package output
import (
"bufio"
"io"
"os"
"sync"
)
// Stdout returns an io.Writer that writes to os.Stdout with a process-wide
// mutex so records do not interleave when multiple goroutines log
// concurrently. The buffered writer is flushed on every record (by the
// caller), so crashes do not lose the last line.
func Stdout() io.Writer {
return stdoutInstance
}
var stdoutInstance = newSyncWriter(os.Stdout)
type syncWriter struct {
mu sync.Mutex
bw *bufio.Writer
}
func newSyncWriter(w io.Writer) *syncWriter {
return &syncWriter{bw: bufio.NewWriterSize(w, 8*1024)}
}
// Write satisfies io.Writer.
func (s *syncWriter) Write(p []byte) (int, error) {
s.mu.Lock()
defer s.mu.Unlock()
n, err := s.bw.Write(p)
if err != nil {
return n, err
}
if err := s.bw.Flush(); err != nil {
return n, err
}
return n, nil
}
// Flush flushes the underlying buffered writer. Exported for tests and for
// shutdown hooks.
func (s *syncWriter) Flush() error {
s.mu.Lock()
defer s.mu.Unlock()
return s.bw.Flush()
}
// Flush flushes the default Stdout writer.
func Flush() error { return stdoutInstance.Flush() }
// Discard is an io.Writer that drops all input. It is useful as a default
// when lambdalog is used by libraries that do not want to emit their own
// records during tests.
var Discard io.Writer = discardWriter{}
type discardWriter struct{}
// Write satisfies io.Writer by returning len(p), nil.
func (discardWriter) Write(p []byte) (int, error) { return len(p), nil }
// Multi returns a writer that fans w out to each destination. Unlike
// io.MultiWriter, errors from one destination do not short-circuit the
// write to others.
func Multi(ws ...io.Writer) io.Writer { return multiWriter(ws) }
type multiWriter []io.Writer
// Write writes p to each underlying writer and returns the first error.
func (m multiWriter) Write(p []byte) (int, error) {
var firstErr error
for _, w := range m {
if _, err := w.Write(p); err != nil && firstErr == nil {
firstErr = err
}
}
return len(p), firstErr
}