本文主要研究一下golang的zap的WriteSyncer
zap@v1.16.0/zapcore/write_syncer.go
type WriteSyncer interface { io.Writer Sync() error }
WriteSyncer内嵌了io.Writer接口,定义了Sync方法
/usr/local/go/src/io/io.go
type Writer interface { Write(p []byte) (n int, err error) }
Writer接口定义了Write方法
zap@v1.16.0/zapcore/write_syncer.go
type lockedWriteSyncer struct { sync.Mutex ws WriteSyncer } 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 }
lockedWriteSyncer定义了sync.Mutex及WriteSyncer属性,它实现了WriteSyncer接口,它对Write和Sync方法都加了锁,内部委托的WriteSyncer;Lock方法用于创建lockedWriteSyncer
zap@v1.16.0/zapcore/write_syncer.go
type multiWriteSyncer []WriteSyncer func NewMultiWriteSyncer(ws ...WriteSyncer) WriteSyncer { if len(ws) == 1 { return ws[0] } // Copy to protect against https://github.com/golang/go/issues/7809 return multiWriteSyncer(append([]WriteSyncer(nil), ws...)) } 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 }
multiWriteSyncer为[]WriteSyncer
类型,它实现了WriteSyncer接口,其Write方法会遍历multiWriteSyncer挨个执行其w.Write,然后会用multierr.Append(writeErr, err)来包装err;其Sync方法会遍历multiWriteSyncer,挨个执行w.Sync(),然后会用multierr.Append(writeErr, err)来包装err;NewMultiWriteSyncer方法用于创建multiWriteSyncer
zap@v1.16.0/writer.go
func CombineWriteSyncers(writers ...zapcore.WriteSyncer) zapcore.WriteSyncer { if len(writers) == 0 { return zapcore.AddSync(ioutil.Discard) } return zapcore.Lock(zapcore.NewMultiWriteSyncer(writers...)) } func Open(paths ...string) (zapcore.WriteSyncer, func(), error) { writers, close, err := open(paths) if err != nil { return nil, nil, err } writer := CombineWriteSyncers(writers...) return writer, close, nil } func open(paths []string) ([]zapcore.WriteSyncer, func(), error) { writers := make([]zapcore.WriteSyncer, 0, len(paths)) closers := make([]io.Closer, 0, len(paths)) close := func() { for _, c := range closers { c.Close() } } var openErr error for _, path := range paths { sink, err := newSink(path) if err != nil { openErr = multierr.Append(openErr, fmt.Errorf("couldn't open sink %q: %v", path, err)) continue } writers = append(writers, sink) closers = append(closers, sink) } if openErr != nil { close() return writers, nil, openErr } return writers, close, nil }
CombineWriteSyncers方法会先使用zapcore.NewMultiWriteSyncer(writers...)创建multiWriteSyncer,在通过Lock创建lockedWriteSyncer;Open方法根据paths创建zapcore.WriteSyncer,最后通过CombineWriteSyncers来创建带锁的multiWriteSyncer
zap@v1.16.0/sink.go
type Sink interface { zapcore.WriteSyncer io.Closer }
Sink接口内嵌了zapcore.WriteSyncer及io.Closer接口
/usr/local/go/src/io/io.go
type Closer interface { Close() error }
Closer接口定义了Close方法
type nopCloserSink struct{ zapcore.WriteSyncer } func (nopCloserSink) Close() error { return nil }
nopCloserSink内嵌了zapcore.WriteSyncer,其Close方法为空操作
zap@v1.16.0/sink.go
var ( _sinkMutex sync.RWMutex _sinkFactories map[string]func(*url.URL) (Sink, error) // keyed by scheme ) func init() { resetSinkRegistry() } func resetSinkRegistry() { _sinkMutex.Lock() defer _sinkMutex.Unlock() _sinkFactories = map[string]func(*url.URL) (Sink, error){ schemeFile: newFileSink, } } func newSink(rawURL string) (Sink, error) { u, err := url.Parse(rawURL) if err != nil { return nil, fmt.Errorf("can't parse %q as a URL: %v", rawURL, err) } if u.Scheme == "" { u.Scheme = schemeFile } _sinkMutex.RLock() factory, ok := _sinkFactories[u.Scheme] _sinkMutex.RUnlock() if !ok { return nil, &errSinkNotFound{u.Scheme} } return factory(u) }
newSink方法解析url,然后通过scheme找到对应的factory,调用factory创建Sink;_sinkFactories默认注册了newFileSink
zap@v1.16.0/sink.go
func newFileSink(u *url.URL) (Sink, error) { if u.User != nil { return nil, fmt.Errorf("user and password not allowed with file URLs: got %v", u) } if u.Fragment != "" { return nil, fmt.Errorf("fragments not allowed with file URLs: got %v", u) } if u.RawQuery != "" { return nil, fmt.Errorf("query parameters not allowed with file URLs: got %v", u) } // Error messages are better if we check hostname and port separately. if u.Port() != "" { return nil, fmt.Errorf("ports not allowed with file URLs: got %v", u) } if hn := u.Hostname(); hn != "" && hn != "localhost" { return nil, fmt.Errorf("file URLs must leave host empty or use localhost: got %v", u) } switch u.Path { case "stdout": return nopCloserSink{os.Stdout}, nil case "stderr": return nopCloserSink{os.Stderr}, nil } return os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) }
newFileSink针对stdout创建nopCloserSink{os.Stdout}
,针对stderr创建nopCloserSink{os.Stderr}
,非以上两者则返回os.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666);*os.File
拥有Write、Sync、Close方法,实现了Sink接口
func sinkDemo() { sink, cleanup, err := zap.Open("stdout", "/tmp/out1", "/tmp/out2") if err == nil { defer cleanup() } sink.Write([]byte("hello")) sink.Write([]byte("world")) }
输出
helloworld
同时/tmp/out1,/tmp/out2也都有输出
*os.File
拥有Write、Sync、Close方法,实现了Sink接口;nopCloserSink内嵌了zapcore.WriteSyncer,其Close方法为空操作;FileSink则是基于文件的sink