前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊golang的zap的WriteSyncer

聊聊golang的zap的WriteSyncer

作者头像
code4it
发布2020-12-11 10:38:23
6600
发布2020-12-11 10:38:23
举报
文章被收录于专栏:码匠的流水账

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

WriteSyncer

zap@v1.16.0/zapcore/write_syncer.go

代码语言:javascript
复制
type WriteSyncer interface {
    io.Writer
    Sync() error
}

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

Writer

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

代码语言:javascript
复制
type Writer interface {
    Write(p []byte) (n int, err error)
}

Writer接口定义了Write方法

lockedWriteSyncer

zap@v1.16.0/zapcore/write_syncer.go

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
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

代码语言:javascript
复制
type Sink interface {
    zapcore.WriteSyncer
    io.Closer
}

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

Closer

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

代码语言:javascript
复制
type Closer interface {
    Close() error
}

Closer接口定义了Close方法

nopCloserSink

代码语言:javascript
复制
type nopCloserSink struct{ zapcore.WriteSyncer }

func (nopCloserSink) Close() error { return nil }

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

newSink

zap@v1.16.0/sink.go

代码语言:javascript
复制
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

代码语言:javascript
复制
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接口

实例

代码语言:javascript
复制
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"))
}

输出

代码语言:javascript
复制
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

  • zap
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • WriteSyncer
    • Writer
      • lockedWriteSyncer
        • multiWriteSyncer
          • CombineWriteSyncers
          • Sink
            • Closer
              • nopCloserSink
                • newSink
                  • newFileSink
                  • 实例
                  • 小结
                  • doc
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档