Source File
buffered_write_syncer.go
Belonging Package
go.uber.org/zap/zapcore
// Copyright (c) 2021 Uber Technologies, Inc.//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.package zapcoreimport ()const (// _defaultBufferSize specifies the default size used by Buffer._defaultBufferSize = 256 * 1024 // 256 kB// _defaultFlushInterval specifies the default flush interval for// Buffer._defaultFlushInterval = 30 * time.Second)// A BufferedWriteSyncer is a WriteSyncer that buffers writes in-memory before// flushing them to a wrapped WriteSyncer after reaching some limit, or at some// fixed interval--whichever comes first.//// BufferedWriteSyncer is safe for concurrent use. You don't need to use// zapcore.Lock for WriteSyncers with BufferedWriteSyncer.//// To set up a BufferedWriteSyncer, construct a WriteSyncer for your log// destination (*os.File is a valid WriteSyncer), wrap it with// BufferedWriteSyncer, and defer a Stop() call for when you no longer need the// object.//// func main() {// ws := ... // your log destination// bws := &zapcore.BufferedWriteSyncer{WS: ws}// defer bws.Stop()//// // ...// core := zapcore.NewCore(enc, bws, lvl)// logger := zap.New(core)//// // ...// }//// By default, a BufferedWriteSyncer will buffer up to 256 kilobytes of logs,// waiting at most 30 seconds between flushes.// You can customize these parameters by setting the Size or FlushInterval// fields.// For example, the following buffers up to 512 kB of logs before flushing them// to Stderr, with a maximum of one minute between each flush.//// ws := &BufferedWriteSyncer{// WS: os.Stderr,// Size: 512 * 1024, // 512 kB// FlushInterval: time.Minute,// }// defer ws.Stop()type BufferedWriteSyncer struct {// WS is the WriteSyncer around which BufferedWriteSyncer will buffer// writes.//// This field is required.WS WriteSyncer// Size specifies the maximum amount of data the writer will buffered// before flushing.//// Defaults to 256 kB if unspecified.Size int// FlushInterval specifies how often the writer should flush data if// there have been no writes.//// Defaults to 30 seconds if unspecified.FlushInterval time.Duration// Clock, if specified, provides control of the source of time for the// writer.//// Defaults to the system clock.Clock Clock// unexported fields for statemu sync.Mutexinitialized bool // whether initialize() has runstopped bool // whether Stop() has runwriter *bufio.Writerticker *time.Tickerstop chan struct{} // closed when flushLoop should stopdone chan struct{} // closed when flushLoop has stopped}func ( *BufferedWriteSyncer) () {:= .Sizeif == 0 {= _defaultBufferSize}:= .FlushIntervalif == 0 {= _defaultFlushInterval}if .Clock == nil {.Clock = DefaultClock}.ticker = .Clock.NewTicker().writer = bufio.NewWriterSize(.WS, ).stop = make(chan struct{}).done = make(chan struct{}).initialized = truego .flushLoop()}// Write writes log data into buffer syncer directly, multiple Write calls will be batched,// and log data will be flushed to disk when the buffer is full or periodically.func ( *BufferedWriteSyncer) ( []byte) (int, error) {.mu.Lock()defer .mu.Unlock()if !.initialized {.initialize()}// To avoid partial writes from being flushed, we manually flush the existing buffer if:// * The current write doesn't fit into the buffer fully, and// * The buffer is not empty (since bufio will not split large writes when the buffer is empty)if len() > .writer.Available() && .writer.Buffered() > 0 {if := .writer.Flush(); != nil {return 0,}}return .writer.Write()}// Sync flushes buffered log data into disk directly.func ( *BufferedWriteSyncer) () error {.mu.Lock()defer .mu.Unlock()var errorif .initialized {= .writer.Flush()}return multierr.Append(, .WS.Sync())}// flushLoop flushes the buffer at the configured interval until Stop is// called.func ( *BufferedWriteSyncer) () {defer close(.done)for {select {case <-.ticker.C:// we just simply ignore error here// because the underlying bufio writer stores any errors// and we return any error from Sync() as part of the close_ = .Sync()case <-.stop:return}}}// Stop closes the buffer, cleans up background goroutines, and flushes// remaining unwritten data.func ( *BufferedWriteSyncer) () ( error) {var bool// Critical section.func() {.mu.Lock()defer .mu.Unlock()if !.initialized {return}= .stoppedif {return}.stopped = true.ticker.Stop()close(.stop) // tell flushLoop to stop<-.done // and wait until it has}()// Don't call Sync on consecutive Stops.if ! {= .Sync()}return}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |