golang의 zap의 WriteSyncer에 대해서 얘기를 나눠보도록 하겠습니다.

8019 단어 golang

순서


본고는 주로 golang의 zap의 WriteSyncer를 연구하고자 합니다.

WriteSyncer


[email protected]/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


[email protected]/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는 동기화를 정의합니다.Mutex와 WriteSyncer 속성은 WriteSyncer 인터페이스를 실현했고 Write와 Sync 방법에 모두 자물쇠를 달았으며 내부에 의뢰한 WriteSyncer;Lock 메서드는 lockedWriteSyncer를 만드는 데 사용됩니다.

multiWriteSyncer


[email protected]/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


[email protected]/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를 만듭니다.오픈 방법은 paths에 따라zapcore를 만듭니다.WriteSyncer, 마지막으로 CombineWriteSyncers를 통해 자물쇠가 달린 multiWriteSyncer를 만듭니다.

Sink


[email protected]/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


[email protected]/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


[email protected]/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)
}

stdout용 newFileSink 만들기nopCloserSink{os.Stdout}, stderr에 대한 생성nopCloserSink{os.Stderr}, 이상 둘이 아니면 os로 돌아간다.OpenFile(u.Path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); *os.File Sink 인터페이스를 위한 Write, Sync, Close 메서드

인스턴스

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
  • 좋은 웹페이지 즐겨찾기