美文网首页
聊聊golang的zap的WriteSyncer

聊聊golang的zap的WriteSyncer

作者: go4it | 来源:发表于2020-12-09 22:00 被阅读0次

    本文主要研究一下golang的zap的WriteSyncer

    WriteSyncer

    zap@v1.16.0/zapcore/write_syncer.go

    type WriteSyncer interface {
        io.Writer
        Sync() error
    }
    

    WriteSyncer内嵌了io.Writer接口,定义了Sync方法

    Writer

    /usr/local/go/src/io/io.go

    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    

    Writer接口定义了Write方法

    lockedWriteSyncer

    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

    multiWriteSyncer

    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

    CombineWriteSyncers

    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

    Sink

    zap@v1.16.0/sink.go

    type Sink interface {
        zapcore.WriteSyncer
        io.Closer
    }
    

    Sink接口内嵌了zapcore.WriteSyncer及io.Closer接口

    Closer

    /usr/local/go/src/io/io.go

    type Closer interface {
        Close() error
    }
    

    Closer接口定义了Close方法

    nopCloserSink

    type nopCloserSink struct{ zapcore.WriteSyncer }
    
    func (nopCloserSink) Close() error { return nil }
    

    nopCloserSink内嵌了zapcore.WriteSyncer,其Close方法为空操作

    newSink

    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

    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也都有输出

    小结

    • WriteSyncer内嵌了io.Writer接口,定义了Sync方法;它有lockedWriteSyncer、multiWriteSyncer两个实现,同时CombineWriteSyncers创建是带lock的multiWriteSyncer
    • Sink接口内嵌了zapcore.WriteSyncer及io.Closer接口;*os.File拥有Write、Sync、Close方法,实现了Sink接口;nopCloserSink内嵌了zapcore.WriteSyncer,其Close方法为空操作;FileSink则是基于文件的sink
    • zap.Open先通过newSink创建fileSink的zapcore.WriteSyncer,再通过CombineWriteSyncers将这些fileSink包装为带锁的multiWriteSyncer

    doc

    相关文章

      网友评论

          本文标题:聊聊golang的zap的WriteSyncer

          本文链接:https://www.haomeiwen.com/subject/neukgktx.html