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 zapcore
import (
)
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 state
mu sync.Mutex
initialized bool // whether initialize() has run
stopped bool // whether Stop() has run
writer *bufio.Writer
ticker *time.Ticker
stop chan struct{} // closed when flushLoop should stop
done chan struct{} // closed when flushLoop has stopped
}
func ( *BufferedWriteSyncer) () {
:= .Size
if == 0 {
= _defaultBufferSize
}
:= .FlushInterval
if == 0 {
= _defaultFlushInterval
}
if .Clock == nil {
.Clock = DefaultClock
}
.ticker = .Clock.NewTicker()
.writer = bufio.NewWriterSize(.WS, )
.stop = make(chan struct{})
.done = make(chan struct{})
.initialized = true
go .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 error
if .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
}
= .stopped
if {
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. |