2024-04-21 12:23:26 +08:00
|
|
|
|
package loggerx
|
|
|
|
|
|
|
2024-05-28 09:30:57 +08:00
|
|
|
|
import (
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"fmt"
|
2026-09-14 00:14:30 +08:00
|
|
|
|
"io"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"sync/atomic"
|
2024-05-28 09:30:57 +08:00
|
|
|
|
)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
|
2026-09-14 00:14:30 +08:00
|
|
|
|
// 包级默认实例
|
|
|
|
|
|
//
|
|
|
|
|
|
// 两个刻意的设计:
|
|
|
|
|
|
// 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]
|
|
|
|
|
|
)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
|
2026-09-14 00:14:30 +08:00
|
|
|
|
// 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
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-14 00:14:30 +08:00
|
|
|
|
// NewLoggerc 配置包级实例
|
|
|
|
|
|
// 在现有配置基础上追加选项,构造新实例后原子替换,因此可与并发日志调用共存
|
|
|
|
|
|
// (替换瞬间可能有极少几条日志落进旧实例)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
func NewLoggerc(ctx context.Context, opts ...Option) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
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
|
|
|
|
|
|
}
|
2024-04-21 12:23:26 +08:00
|
|
|
|
for _, apply := range opts {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
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()
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-14 00:14:30 +08:00
|
|
|
|
// 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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-21 12:23:26 +08:00
|
|
|
|
func Channel(ch string) (r *Logger) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
return loggercDefault().Channel(ch)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Info(ctx context.Context, v ...any) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
loggercDefault().logger(ctx, "info", v...)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Infof(ctx context.Context, format string, v ...any) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
loggercDefault().logger(ctx, "info", fmt.Sprintf(format, v...))
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Error(ctx context.Context, v ...any) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
loggercDefault().logger(ctx, "error", v...)
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Errorf(ctx context.Context, format string, v ...any) {
|
2026-09-14 00:14:30 +08:00
|
|
|
|
loggercDefault().logger(ctx, "error", fmt.Sprintf(format, v...))
|
2024-04-21 12:23:26 +08:00
|
|
|
|
}
|