Files
loggerx/loggerc.go
T

103 lines
3.1 KiB
Go
Raw Normal View History

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
}