diff --git a/example/main.go b/example/main.go index 81859a0..1d7e6d7 100644 --- a/example/main.go +++ b/example/main.go @@ -15,6 +15,7 @@ func main() { // loggerx.SetTimeZone(time.UTC), loggerx.SetTimeZone(time.FixedZone("CST", 8*3600)), ) + log.WriteAsync().Info(ctx, "哈哈哈2异步") log.Info(ctx, "哈哈哈2") log.Info(ctx, "哈哈哈2") log.Info(ctx, "哈哈哈2") @@ -22,5 +23,10 @@ func main() { log.Info(ctx, "哈哈哈2") log.Info(ctx, "哈哈哈2") log.Info(ctx, "哈哈哈2") - log.Info(ctx, "哈哈哈2") + + for i := 0; i < 100; i++ { + log.WriteAsync().Infof(ctx, "异步 %d", i) + } + + time.Sleep(time.Second) } diff --git a/loggerx.go b/loggerx.go index cdebd9c..8e3c925 100644 --- a/loggerx.go +++ b/loggerx.go @@ -21,11 +21,12 @@ import ( // 需要实现io.Writer接口 type Logger struct { - ctx context.Context - filePath *sync.Map // filePath - mu *sync.Mutex - option loggerOption - channel string + ctx context.Context + filePath *sync.Map // filePath + mu *sync.Mutex + option loggerOption + channel string + writeType writeType // 是否异步落盘,这里作用范围是本条,优先判断这里 } type filePath struct { @@ -45,10 +46,11 @@ func NewLogger(ctx context.Context, opts ...Option) *Logger { } l := &Logger{ - ctx: ctx, - filePath: &sync.Map{}, - mu: &sync.Mutex{}, - option: opt, + ctx: ctx, + filePath: &sync.Map{}, + mu: &sync.Mutex{}, + option: opt, + writeType: writeTypeDefault, } log.SetOutput(l) @@ -87,6 +89,18 @@ func (l *Logger) Channel(ch string) (r *Logger) { return &rr } +func (l *Logger) WriteAsync() (r *Logger) { + rr := *l + rr.writeType = writeTypeAsync + return &rr +} + +func (l *Logger) WriteSync() (r *Logger) { + rr := *l + rr.writeType = writeTypeSync + return &rr +} + // 获取TraceField的字段 func (l *Logger) GetTraceField() string { return l.option.traceField diff --git a/options.go b/options.go index fb1cb9a..ce15e2d 100644 --- a/options.go +++ b/options.go @@ -13,6 +13,7 @@ type loggerOption struct { isGinLog bool isGid bool isPrintFile bool + writeType writeType // 是否异步罗盘 traceField string // trace字段 errorToInfo bool // 错误日志是否写入info日志 days int // 日志保存天数 @@ -22,11 +23,21 @@ type loggerOption struct { timeZone *time.Location // 时区 } +type writeType uint8 + +const ( + // 0.默认(同步) 1.指定同步 2.指定异步 + writeTypeDefault writeType = iota + writeTypeSync + writeTypeAsync +) + func defaultOptions() loggerOption { return loggerOption{ isGinLog: true, isGid: true, isPrintFile: true, + writeType: writeTypeDefault, // 默认同步 format: "json", dir: "./log", traceField: "trace_id", @@ -45,6 +56,13 @@ func SetTraceField(traceField string) Option { } } +// 是否异步写入 +func SetWriteAsync() Option { + return func(o *loggerOption) { + o.writeType = writeTypeAsync + } +} + // 打印到控制台 func SetToConsole() Option { return func(o *loggerOption) { diff --git a/readme.md b/readme.md index 85524a3..54a6984 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,3 @@ - # 简介 这个是基于原生log实现的日志存储。 @@ -11,7 +10,6 @@ log.Println("ddddd") # 用法 - # 开发计划 1. 自动清除过期的日志文件 @@ -23,3 +21,5 @@ log.Println("ddddd") 2. 按照文件大小分割 3. 按照日志行数分割 6. 支持debug 模式 + +* [X] 添加异步落库,支持全局和单次 diff --git a/storage.go b/storage.go index 7e8e2da..df7083f 100644 --- a/storage.go +++ b/storage.go @@ -2,9 +2,20 @@ package loggerx import ( "io" + "sync" ) +// 写入,需要判断同步还是异步 func (l *Logger) write(event string, b []byte) (n int, err error) { + if l.toAsync(event, b) { + // fmt.Println("异步写入") + return len(b), nil + } + return l.store(event, b) +} + +// 实际的存储 +func (l *Logger) store(event string, b []byte) (n int, err error) { if l.option.isPrintFile { f, err := l.getFile(event, false) @@ -30,3 +41,32 @@ func (l *Logger) write(event string, b []byte) (n int, err error) { } return n, err } + +var chanStore = make(chan cacheData, 1000) +var chanOnce = sync.Once{} + +type cacheData struct { + logger *Logger + Event string + Data []byte +} + +func (l *Logger) toAsync(event string, b []byte) bool { + chanOnce.Do(func() { + go func() { + for val := range chanStore { + val.logger.store(val.Event, val.Data) + } + }() + }) + + if l.writeType == writeTypeSync || // 指定同步模式 + (l.writeType == writeTypeDefault && l.option.writeType != writeTypeAsync) { // 默认同步模式 + return false + } + + // 为了避免丢失,还是要阻塞等待 + chanStore <- cacheData{l, event, b} + + return true +}