Skip to content

Commit bc1f503

Browse files
committedJul 16, 2015
add flushing buffer in period for rotating file handler
1 parent a70a037 commit bc1f503

4 files changed

+112
-7
lines changed
 

‎README.md

+3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ handlers:
117117
filepath: "./test.log"
118118
mode: O_APPEND
119119
bufferSize: 0
120+
# 30 * 1000 ms -> 30 seconds
121+
bufferFlushTime: 30000
122+
inputChanSize: 1
120123
# 100 * 1024 * 1024 -> 100M
121124
maxBytes: 104857600
122125
backupCount: 9

‎config.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"reflect"
1414
"strconv"
1515
"strings"
16+
"time"
1617
)
1718

1819
var (
@@ -530,6 +531,15 @@ func DictConfig(conf *Conf) error {
530531
if err != nil {
531532
return err
532533
}
534+
bufferFlushTimeMS, err := m.GetInt("bufferFlushTime")
535+
if err != nil {
536+
return err
537+
}
538+
bufferFlushTime := time.Millisecond * time.Duration(bufferFlushTimeMS)
539+
inputChanSize, err := m.GetInt("inputChanSize")
540+
if err != nil {
541+
return err
542+
}
533543
maxBytes, err := m.GetUint64("maxBytes")
534544
if err != nil {
535545
return err
@@ -539,7 +549,13 @@ func DictConfig(conf *Conf) error {
539549
return err
540550
}
541551
handler, err = NewRotatingFileHandler(
542-
filepath, mode, bufferSize, maxBytes, backupCount)
552+
filepath,
553+
mode,
554+
bufferSize,
555+
bufferFlushTime,
556+
inputChanSize,
557+
maxBytes,
558+
backupCount)
543559
if err != nil {
544560
return err
545561
}

‎handler_rotating_file.go

+82-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package logging
33
import (
44
"fmt"
55
"os"
6+
"sync"
7+
"time"
68
)
79

810
// Check whether the specified directory/file exists or not.
@@ -71,12 +73,20 @@ func (self *BaseRotatingHandler) RolloverEmit(
7173
return nil
7274
}
7375

76+
type HandleFunc func(record *LogRecord) int
77+
7478
// Handler for logging to a set of files, which switches from one file to
7579
// the next when the current file reaches a certain size.
7680
type RotatingFileHandler struct {
7781
*BaseRotatingHandler
78-
maxBytes uint64
79-
backupCount uint32
82+
maxBytes uint64
83+
backupCount uint32
84+
bufferFlushTime time.Duration
85+
inputChanSize int
86+
handleFunc HandleFunc
87+
inputChan chan *LogRecord
88+
exitChan chan int
89+
group *sync.WaitGroup
8090
}
8191

8292
// Open the specified file and use it as the stream for logging.
@@ -96,10 +106,22 @@ type RotatingFileHandler struct {
96106
// "app.log.3" etc. respectively.
97107
//
98108
// If maxBytes is zero, rollover never occurs.
109+
//
110+
// bufferSize specifies the size of the internal buffer. If it is positive,
111+
// the internal buffer will be enabled, the logs will be first written into
112+
// the internal buffer, when the internal buffer is full all buffer content
113+
// will be flushed to file.
114+
// bufferFlushTime specifies the time for flushing the internal buffer
115+
// in period, no matter the buffer is full or not.
116+
// inputChanSize specifies the chan size of the handler. If it is positive,
117+
// this handler will be initialized as a standardlone go routine to handle
118+
// log message.
99119
func NewRotatingFileHandler(
100120
filepath string,
101121
mode int,
102122
bufferSize int,
123+
bufferFlushTime time.Duration,
124+
inputChanSize int,
103125
maxBytes uint64,
104126
backupCount uint32) (*RotatingFileHandler, error) {
105127

@@ -119,6 +141,21 @@ func NewRotatingFileHandler(
119141
BaseRotatingHandler: base,
120142
maxBytes: maxBytes,
121143
backupCount: backupCount,
144+
bufferFlushTime: bufferFlushTime,
145+
inputChanSize: inputChanSize,
146+
}
147+
if inputChanSize > 0 {
148+
object.handleFunc = object.handleChan
149+
object.inputChan = make(chan *LogRecord, inputChanSize)
150+
object.exitChan = make(chan int, 1)
151+
object.group = &sync.WaitGroup{}
152+
object.group.Add(1)
153+
go func() {
154+
defer object.group.Done()
155+
object.loop()
156+
}()
157+
} else {
158+
object.handleFunc = object.handleCall
122159
}
123160
return object, nil
124161
}
@@ -127,11 +164,19 @@ func MustNewRotatingFileHandler(
127164
filepath string,
128165
mode int,
129166
bufferSize int,
167+
bufferFlushTime time.Duration,
168+
inputChanSize int,
130169
maxBytes uint64,
131170
backupCount uint32) *RotatingFileHandler {
132171

133172
handler, err := NewRotatingFileHandler(
134-
filepath, mode, bufferSize, maxBytes, backupCount)
173+
filepath,
174+
mode,
175+
bufferSize,
176+
bufferFlushTime,
177+
inputChanSize,
178+
maxBytes,
179+
backupCount)
135180
if err != nil {
136181
panic("NewRotatingFileHandler(), error: " + err.Error())
137182
}
@@ -175,9 +220,9 @@ func (self *RotatingFileHandler) RotateFile(sourceFile, destFile string) error {
175220

176221
// Do a rollover, as described above.
177222
func (self *RotatingFileHandler) DoRollover() (err error) {
178-
self.Close()
223+
self.FileHandler.Close()
179224
defer func() {
180-
if e := self.Open(); e != nil {
225+
if e := self.FileHandler.Open(); e != nil {
181226
if e == nil {
182227
err = e
183228
}
@@ -205,6 +250,37 @@ func (self *RotatingFileHandler) Emit(record *LogRecord) error {
205250
return self.RolloverEmit(self, record)
206251
}
207252

208-
func (self *RotatingFileHandler) Handle(record *LogRecord) int {
253+
func (self *RotatingFileHandler) handleCall(record *LogRecord) int {
209254
return self.Handle2(self, record)
210255
}
256+
257+
func (self *RotatingFileHandler) handleChan(record *LogRecord) int {
258+
self.inputChan <- record
259+
return 0
260+
}
261+
262+
func (self *RotatingFileHandler) loop() {
263+
ticker := time.NewTicker(self.bufferFlushTime)
264+
for {
265+
select {
266+
case r := <-self.inputChan:
267+
self.Handle2(self, r)
268+
case <-self.exitChan:
269+
return
270+
case <-ticker.C:
271+
self.Flush()
272+
}
273+
}
274+
}
275+
276+
func (self *RotatingFileHandler) Handle(record *LogRecord) int {
277+
return self.handleFunc(record)
278+
}
279+
280+
func (self *RotatingFileHandler) Close() {
281+
if self.inputChanSize > 0 {
282+
self.exitChan <- 1
283+
self.group.Wait()
284+
}
285+
self.BaseRotatingHandler.Close()
286+
}

‎handler_rotating_file_test.go

+10
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import (
55
"os"
66
"strings"
77
"testing"
8+
"time"
89
)
910

1011
var (
12+
testBufferFlushTime = time.Millisecond * 5000
13+
testInputChanSize = 1
1114
testRotateMaxBytes = uint64(5 * 1000) // 5k bytes
1215
testRotateBackupCount = uint32(1)
1316
)
@@ -30,6 +33,8 @@ func TestRotatingFileHandler_TruncateWithBackup(t *testing.T) {
3033
testFileName,
3134
testFileMode,
3235
testBufferSize,
36+
testBufferFlushTime,
37+
testInputChanSize,
3338
testRotateMaxBytes,
3439
testRotateBackupCount)
3540
require.Nil(t, err)
@@ -61,6 +66,8 @@ func TestRotatingFileHandler_AppendWithoutBackup(t *testing.T) {
6166
testFileName,
6267
os.O_APPEND,
6368
testBufferSize,
69+
testBufferFlushTime,
70+
testInputChanSize,
6471
testRotateMaxBytes,
6572
backupCount)
6673
require.Nil(t, err)
@@ -70,6 +77,7 @@ func TestRotatingFileHandler_AppendWithoutBackup(t *testing.T) {
7077
size := uint64(len(message) + 1)
7178
totalSize := testRotateMaxBytes * (uint64(testRotateBackupCount) + 2)
7279
times := totalSize / size
80+
require.Equal(t, totalSize, size*times)
7381
for i := uint64(0); i < times; i++ {
7482
logger.Errorf(message)
7583
}
@@ -94,6 +102,8 @@ func BenchmarkRotatingFileHandler(b *testing.B) {
94102
testFileName,
95103
os.O_APPEND,
96104
testBufferSize,
105+
testBufferFlushTime,
106+
testInputChanSize,
97107
rotateMaxBytes,
98108
backupCount)
99109
if err != nil {

0 commit comments

Comments
 (0)
Please sign in to comment.