1package zerolog
2
3import (
4	"io"
5	"sync"
6)
7
8// LevelWriter defines as interface a writer may implement in order
9// to receive level information with payload.
10type LevelWriter interface {
11	io.Writer
12	WriteLevel(level Level, p []byte) (n int, err error)
13}
14
15type levelWriterAdapter struct {
16	io.Writer
17}
18
19func (lw levelWriterAdapter) WriteLevel(l Level, p []byte) (n int, err error) {
20	return lw.Write(p)
21}
22
23type syncWriter struct {
24	mu sync.Mutex
25	lw LevelWriter
26}
27
28// SyncWriter wraps w so that each call to Write is synchronized with a mutex.
29// This syncer can be used to wrap the call to writer's Write method if it is
30// not thread safe. Note that you do not need this wrapper for os.File Write
31// operations on POSIX and Windows systems as they are already thread-safe.
32func SyncWriter(w io.Writer) io.Writer {
33	if lw, ok := w.(LevelWriter); ok {
34		return &syncWriter{lw: lw}
35	}
36	return &syncWriter{lw: levelWriterAdapter{w}}
37}
38
39// Write implements the io.Writer interface.
40func (s *syncWriter) Write(p []byte) (n int, err error) {
41	s.mu.Lock()
42	defer s.mu.Unlock()
43	return s.lw.Write(p)
44}
45
46// WriteLevel implements the LevelWriter interface.
47func (s *syncWriter) WriteLevel(l Level, p []byte) (n int, err error) {
48	s.mu.Lock()
49	defer s.mu.Unlock()
50	return s.lw.WriteLevel(l, p)
51}
52
53type multiLevelWriter struct {
54	writers []LevelWriter
55}
56
57func (t multiLevelWriter) Write(p []byte) (n int, err error) {
58	for _, w := range t.writers {
59		n, err = w.Write(p)
60		if err != nil {
61			return
62		}
63		if n != len(p) {
64			err = io.ErrShortWrite
65			return
66		}
67	}
68	return len(p), nil
69}
70
71func (t multiLevelWriter) WriteLevel(l Level, p []byte) (n int, err error) {
72	for _, w := range t.writers {
73		n, err = w.WriteLevel(l, p)
74		if err != nil {
75			return
76		}
77		if n != len(p) {
78			err = io.ErrShortWrite
79			return
80		}
81	}
82	return len(p), nil
83}
84
85// MultiLevelWriter creates a writer that duplicates its writes to all the
86// provided writers, similar to the Unix tee(1) command. If some writers
87// implement LevelWriter, their WriteLevel method will be used instead of Write.
88func MultiLevelWriter(writers ...io.Writer) LevelWriter {
89	lwriters := make([]LevelWriter, 0, len(writers))
90	for _, w := range writers {
91		if lw, ok := w.(LevelWriter); ok {
92			lwriters = append(lwriters, lw)
93		} else {
94			lwriters = append(lwriters, levelWriterAdapter{w})
95		}
96	}
97	return multiLevelWriter{lwriters}
98}
99