|
|
|
|
@@ -48,6 +48,7 @@ var (
|
|
|
|
|
atomicLevel zap.AtomicLevel
|
|
|
|
|
initOptions InitOptions
|
|
|
|
|
currentSink atomic.Value // sinkState
|
|
|
|
|
currentClose func()
|
|
|
|
|
stdLogUndo func()
|
|
|
|
|
bootstrapOnce sync.Once
|
|
|
|
|
)
|
|
|
|
|
@@ -72,16 +73,18 @@ func Init(options InitOptions) error {
|
|
|
|
|
|
|
|
|
|
func initLocked(options InitOptions) error {
|
|
|
|
|
normalized := options.normalized()
|
|
|
|
|
zl, al, err := buildLogger(normalized)
|
|
|
|
|
zl, al, closeFn, err := buildLogger(normalized)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
prev := global.Load()
|
|
|
|
|
prevClose := currentClose
|
|
|
|
|
global.Store(zl)
|
|
|
|
|
sugar.Store(zl.Sugar())
|
|
|
|
|
atomicLevel = al
|
|
|
|
|
initOptions = normalized
|
|
|
|
|
currentClose = closeFn
|
|
|
|
|
|
|
|
|
|
bridgeSlogLocked()
|
|
|
|
|
bridgeStdLogLocked()
|
|
|
|
|
@@ -89,6 +92,9 @@ func initLocked(options InitOptions) error {
|
|
|
|
|
if prev != nil {
|
|
|
|
|
_ = prev.Sync()
|
|
|
|
|
}
|
|
|
|
|
if prevClose != nil {
|
|
|
|
|
prevClose()
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -205,6 +211,27 @@ func Sync() {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Shutdown() {
|
|
|
|
|
mu.Lock()
|
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
|
|
if stdLogUndo != nil {
|
|
|
|
|
stdLogUndo()
|
|
|
|
|
stdLogUndo = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if l := global.Load(); l != nil {
|
|
|
|
|
_ = l.Sync()
|
|
|
|
|
}
|
|
|
|
|
if currentClose != nil {
|
|
|
|
|
currentClose()
|
|
|
|
|
currentClose = nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.Store(nil)
|
|
|
|
|
sugar.Store(nil)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func bridgeStdLogLocked() {
|
|
|
|
|
if stdLogUndo != nil {
|
|
|
|
|
stdLogUndo()
|
|
|
|
|
@@ -238,7 +265,7 @@ func bridgeSlogLocked() {
|
|
|
|
|
slog.SetDefault(slog.New(newSlogZapHandler(base.Named("slog"))))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, error) {
|
|
|
|
|
func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, func(), error) {
|
|
|
|
|
level, _ := parseLevel(options.Level)
|
|
|
|
|
atomic := zap.NewAtomicLevelAt(level)
|
|
|
|
|
|
|
|
|
|
@@ -265,6 +292,7 @@ func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, error) {
|
|
|
|
|
|
|
|
|
|
sinkCore := newSinkCore()
|
|
|
|
|
cores := make([]zapcore.Core, 0, 3)
|
|
|
|
|
var closers []io.Closer
|
|
|
|
|
|
|
|
|
|
if options.Output.ToStdout {
|
|
|
|
|
infoPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
|
|
|
@@ -273,12 +301,12 @@ func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, error) {
|
|
|
|
|
errPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
|
|
|
|
|
return lvl >= atomic.Level() && lvl >= zapcore.WarnLevel
|
|
|
|
|
})
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, zapcore.Lock(os.Stdout), infoPriority))
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, zapcore.Lock(os.Stderr), errPriority))
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, stdStreamWriteSyncer(os.Stdout), infoPriority))
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, stdStreamWriteSyncer(os.Stderr), errPriority))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if options.Output.ToFile {
|
|
|
|
|
fileCore, filePath, fileErr := buildFileCore(enc, atomic, options)
|
|
|
|
|
fileCore, filePath, fileCloser, fileErr := buildFileCore(enc, atomic, options)
|
|
|
|
|
if fileErr != nil {
|
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "time=%s level=WARN msg=\"日志文件输出初始化失败,降级为仅标准输出\" path=%s err=%v\n",
|
|
|
|
|
time.Now().Format(time.RFC3339Nano),
|
|
|
|
|
@@ -287,11 +315,12 @@ func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, error) {
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
cores = append(cores, fileCore)
|
|
|
|
|
closers = append(closers, fileCloser)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(cores) == 0 {
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, zapcore.Lock(os.Stdout), atomic))
|
|
|
|
|
cores = append(cores, zapcore.NewCore(enc, stdStreamWriteSyncer(os.Stdout), atomic))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
core := zapcore.NewTee(cores...)
|
|
|
|
|
@@ -313,10 +342,14 @@ func buildLogger(options InitOptions) (*zap.Logger, zap.AtomicLevel, error) {
|
|
|
|
|
zap.String("service", options.ServiceName),
|
|
|
|
|
zap.String("env", options.Environment),
|
|
|
|
|
)
|
|
|
|
|
return logger, atomic, nil
|
|
|
|
|
return logger, atomic, func() {
|
|
|
|
|
for _, closer := range closers {
|
|
|
|
|
_ = closer.Close()
|
|
|
|
|
}
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func buildFileCore(enc zapcore.Encoder, atomic zap.AtomicLevel, options InitOptions) (zapcore.Core, string, error) {
|
|
|
|
|
func buildFileCore(enc zapcore.Encoder, atomic zap.AtomicLevel, options InitOptions) (zapcore.Core, string, io.Closer, error) {
|
|
|
|
|
filePath := options.Output.FilePath
|
|
|
|
|
if strings.TrimSpace(filePath) == "" {
|
|
|
|
|
filePath = resolveLogFilePath("")
|
|
|
|
|
@@ -324,7 +357,7 @@ func buildFileCore(enc zapcore.Encoder, atomic zap.AtomicLevel, options InitOpti
|
|
|
|
|
|
|
|
|
|
dir := filepath.Dir(filePath)
|
|
|
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
|
|
|
return nil, filePath, err
|
|
|
|
|
return nil, filePath, nil, err
|
|
|
|
|
}
|
|
|
|
|
lj := &lumberjack.Logger{
|
|
|
|
|
Filename: filePath,
|
|
|
|
|
@@ -334,7 +367,25 @@ func buildFileCore(enc zapcore.Encoder, atomic zap.AtomicLevel, options InitOpti
|
|
|
|
|
Compress: options.Rotation.Compress,
|
|
|
|
|
LocalTime: options.Rotation.LocalTime,
|
|
|
|
|
}
|
|
|
|
|
return zapcore.NewCore(enc, zapcore.AddSync(lj), atomic), filePath, nil
|
|
|
|
|
return zapcore.NewCore(enc, zapcore.AddSync(lj), atomic), filePath, lj, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type stdStreamSyncer struct {
|
|
|
|
|
file *os.File
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func stdStreamWriteSyncer(file *os.File) zapcore.WriteSyncer {
|
|
|
|
|
return zapcore.Lock(&stdStreamSyncer{file: file})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *stdStreamSyncer) Write(p []byte) (int, error) {
|
|
|
|
|
return s.file.Write(p)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *stdStreamSyncer) Sync() error {
|
|
|
|
|
// Standard streams do not need fsync semantics, and on Windows a pipe-backed
|
|
|
|
|
// stdout/stderr can block indefinitely in FlushFileBuffers.
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type sinkCore struct {
|
|
|
|
|
|