go 日志 异步+集中写+滚动 实现
异步和集中写采用 time.NewTicker + chan 的方式。
滚动部分采用第三方库(gopkg.in/natefinch/lumberjack.v2)。
对 slog 实现了异步+集中写+滚动。
核心代码在下方
package asyncwrite
import (
"gopkg.in/natefinch/lumberjack.v2"
"time"
)
type AsyncW struct {
io *lumberjack.Logger
dataChan chan *[]byte
done chan bool
}
func AsyncLumberjack(io *lumberjack.Logger) *AsyncW {
r := AsyncW{
io: io,
dataChan: make(chan *[]byte, 1024),
done: make(chan bool),
}
go func() {
ticker := time.NewTicker(time.Millisecond * 333) // 每0.333秒执行一次批量写入
defer ticker.Stop()
var data []byte
for {
select {
case val := <-r.dataChan:
data = append(data, *val...)
if len(data) >= 100 {
r.io.Write(data)
data = []byte{}
}
case <-ticker.C:
if len(data) > 0 {
r.io.Write(data)
data = []byte{}
}
case <-r.done:
if len(data) > 0 {
r.io.Write(data)
data = []byte{}
}
return
}
}
}()
return &r
}
func (itself *AsyncW) Write(p []byte) (n int, err error) {
data := make([]byte, len(p))
copy(data, p)
itself.dataChan <- &data
return 0, nil
}
func (itself *AsyncW) Stop() {
itself.done <- true
itself.io.Close()
close(itself.dataChan)
close(itself.done)
}
func init() {
once.Do(func() {
var logOut io.Writer
logOut = os.Stdout
switch logType {
default:
slog.Info("Unknown Log Output Type")
case LogTypeStdout:
case LogTypeFile:
if rolling {
logOut = getAsyncFileIoRolling()
} else {
logOut = getFileIo()
}
}
logLevel := slog.LevelInfo
if debug {
logLevel = slog.LevelDebug
}
slog.SetDefault(slog.New(slog.NewJSONHandler(logOut, &slog.HandlerOptions{
AddSource: true,
ReplaceAttr: replace,
Level: logLevel,
})))
})
}
func getAsyncFileIoRolling() *asyncwrite.AsyncW {
aw = asyncwrite.AsyncLumberjack(&lumberjack.Logger{
Filename: logPath,
MaxSize: maxSize, // megabytes
MaxBackups: maxBackUps, // maxBackUps
MaxAge: maxAge, //days
LocalTime: true,
Compress: false, // disabled by default
})
return aw
}
func Shutdown() {
aw.Stop()
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
ReplaceAttr: replace,
})))
slog.Info("logging 👋")
}
本作品采用《CC 协议》,转载必须注明作者和本文链接