@@ -0,0 +1,72 @@
|
||||
// Package logx 提供「控制台 + 按天切分文件」的运行日志。
|
||||
package logx
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type dailyWriter struct {
|
||||
dir string
|
||||
|
||||
mu sync.Mutex
|
||||
f *os.File
|
||||
day string
|
||||
}
|
||||
|
||||
func (w *dailyWriter) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
day := time.Now().Format("2006-01-02")
|
||||
if w.f == nil || w.day != day {
|
||||
if w.f != nil {
|
||||
_ = w.f.Close()
|
||||
w.f = nil
|
||||
}
|
||||
if err := os.MkdirAll(w.dir, 0o755); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f, err := os.OpenFile(filepath.Join(w.dir, "daili-"+day+".log"),
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
w.f = f
|
||||
w.day = day
|
||||
}
|
||||
|
||||
// 写文件失败不能影响控制台输出
|
||||
n, err := w.f.Write(p)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *dailyWriter) Close() error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if w.f == nil {
|
||||
return nil
|
||||
}
|
||||
err := w.f.Close()
|
||||
w.f = nil
|
||||
return err
|
||||
}
|
||||
|
||||
// Setup 把标准日志同时写到控制台与 dir 下按天切分的文件。
|
||||
// 返回的 Closer 在进程退出前关闭即可。
|
||||
func Setup(dir string) (io.Closer, error) {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
w := &dailyWriter{dir: dir}
|
||||
// 预热:启动即创建当天文件,避免“刚启动就崩、日志却没落盘”
|
||||
if _, err := w.Write(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.SetOutput(io.MultiWriter(os.Stderr, w))
|
||||
return w, nil
|
||||
}
|
||||
Reference in New Issue
Block a user