乐趣区

关于golang:聊聊golang的zap的WriteSyncer

本文次要钻研一下 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

  • zap
退出移动版