This commit is contained in:
yun
2026-09-14 00:14:30 +08:00
parent 316c6420a7
commit 7e1b474823
20 changed files with 1885 additions and 166 deletions
+73 -13
View File
@@ -3,40 +3,100 @@ package loggerx
import (
"context"
"fmt"
"io"
"sync"
"sync/atomic"
)
var loggerc *Logger
// 包级默认实例
//
// 两个刻意的设计:
// 1. 惰性初始化:import 一个日志库不该产生副作用(旧实现在 init 里 NewLogger
// 等于仅仅 import 就劫持全局 stdlib log、还起了几个后台 goroutine
// 2. 指针原子发布:NewLoggerc 不做「就地改字段」,而是按现有配置建一个新实例
// 再整体换上。就地改会让并发打日志的读方看到半改状态,
// expandData 这种 map 更会直接触发 fatal error: concurrent map read and map write
var (
loggercOnce sync.Once
loggercPtr atomic.Pointer[Logger]
)
func init() {
loggerc = NewLogger(context.Background())
// loggercDefault 返回包级实例,必要时创建
func loggercDefault() *Logger {
loggercOnce.Do(func() {
loggercPtr.Store(NewLogger(context.Background(), SetAsGlobalLog()))
})
if l := loggercPtr.Load(); l != nil {
return l
}
// CloseLoggerc 之后又有人用包级函数:重新建一个默认实例
l := NewLogger(context.Background(), SetAsGlobalLog())
loggercPtr.Store(l)
return l
}
// NewLoggerc 配置包级实例
// 在现有配置基础上追加选项,构造新实例后原子替换,因此可与并发日志调用共存
// (替换瞬间可能有极少几条日志落进旧实例)
func NewLoggerc(ctx context.Context, opts ...Option) {
base := loggercDefault()
// 复制一份选项再改:绝不动正在被并发读取的那一份
opt := base.option
opt.drivers = append([]io.Writer(nil), base.option.drivers...)
opt.expandData = make(map[string]string, len(base.option.expandData))
for k, v := range base.option.expandData {
opt.expandData[k] = v
}
for _, apply := range opts {
apply(&loggerc.option)
apply(&opt)
}
if ctx == nil {
ctx = context.Background()
}
old := loggercPtr.Load()
next := NewLogger(ctx, func(o *loggerOption) { *o = opt })
// NewLogger 只在实例创建时决定要不要起刷盘协程,走的是入参选项,
// 这里已经通过闭包把完整 opt 传进去了,无需额外处理
loggercPtr.Store(next)
// 旧实例必须收掉,否则后台 goroutine 与文件句柄会一直累积
if old != nil && old != next {
_ = old.Close()
}
}
// CloseLoggerc 关闭包级实例(并还原全局 stdlib log)
// 建议在 main 退出前调用,否则包级实例缓冲里的日志(最多 32KB)不会落盘
// 之后再次调用包级函数会自动新建一个默认实例,不会把包级 API 永久废掉
func CloseLoggerc() error {
l := loggercPtr.Load()
if l == nil {
return nil
}
err := l.Close()
loggercPtr.Store(nil)
loggercOnce = sync.Once{}
return err
}
func Channel(ch string) (r *Logger) {
rr := *loggerc
rr.channel = ch
return &rr
return loggercDefault().Channel(ch)
}
func Info(ctx context.Context, v ...any) {
loggerc.logger(ctx, "info", v...)
loggercDefault().logger(ctx, "info", v...)
}
func Infof(ctx context.Context, format string, v ...any) {
s := fmt.Sprintf(format, v...)
loggerc.logger(ctx, "info", s)
loggercDefault().logger(ctx, "info", fmt.Sprintf(format, v...))
}
func Error(ctx context.Context, v ...any) {
loggerc.logger(ctx, "error", v...)
loggercDefault().logger(ctx, "error", v...)
}
func Errorf(ctx context.Context, format string, v ...any) {
s := fmt.Sprintf(format, v...)
loggerc.logger(ctx, "error", s)
loggercDefault().logger(ctx, "error", fmt.Sprintf(format, v...))
}