Files
workspace/code/allapp/allapp-go-v3/pkg/logger/logger.go
T
2026-04-03 17:29:24 +08:00

202 lines
3.6 KiB
Go

package logger
import (
"context"
"os"
"path/filepath"
"strings"
"time"
"github.com/gofiber/fiber/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
var Log *zap.Logger
const TraceIDKey = "request_id"
// ======================
// 配置结构
// ======================
type Config struct {
Dir string // 日志目录
Level string // debug info warn error
MaxSize int // MB
MaxBackups int
MaxAge int // 天
Compress bool // 是否压缩
}
// ======================
// 初始化
// ======================
func Init(cfg Config) {
if cfg.Dir == "" {
cfg.Dir = "logs"
}
if cfg.Level == "" {
cfg.Level = "info"
}
level := parseLevel(cfg.Level)
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
MessageKey: "msg",
CallerKey: "caller",
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: zapcore.ISO8601TimeEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
encoder := zapcore.NewJSONEncoder(encoderConfig)
// 控制台
consoleWriter := zapcore.AddSync(os.Stdout)
// 文件 writer(按天)
infoWriter := getWriter(cfg, "info")
errorWriter := getWriter(cfg, "error")
// info core
infoCore := zapcore.NewCore(
encoder,
zapcore.NewMultiWriteSyncer(consoleWriter, infoWriter),
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl < zapcore.ErrorLevel && lvl >= level
}),
)
// error core
errorCore := zapcore.NewCore(
encoder,
zapcore.NewMultiWriteSyncer(consoleWriter, errorWriter),
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel
}),
)
core := zapcore.NewTee(infoCore, errorCore)
Log = zap.New(
core,
)
}
// ======================
// Daily Writer(核心)
// ======================
type DailyWriter struct {
cfg Config
level string
log *lumberjack.Logger
date string
}
func newDailyWriter(cfg Config, level string) *DailyWriter {
return &DailyWriter{
cfg: cfg,
level: level,
}
}
func (w *DailyWriter) Write(p []byte) (n int, err error) {
today := time.Now().Format("2006-01-02")
// 跨天自动切换
if w.log == nil || w.date != today {
dir := filepath.Join(w.cfg.Dir, w.level)
_ = os.MkdirAll(dir, os.ModePerm)
filename := filepath.Join(dir, today+".log")
w.log = &lumberjack.Logger{
Filename: filename,
MaxSize: w.cfg.MaxSize,
MaxBackups: w.cfg.MaxBackups,
MaxAge: w.cfg.MaxAge,
Compress: w.cfg.Compress,
}
w.date = today
}
return w.log.Write(p)
}
func (w *DailyWriter) Sync() error {
if w.log != nil {
return w.log.Close()
}
return nil
}
// ======================
// writer 封装
// ======================
func getWriter(cfg Config, level string) zapcore.WriteSyncer {
return zapcore.AddSync(newDailyWriter(cfg, level))
}
// ======================
// level 解析
// ======================
func parseLevel(lvl string) zapcore.Level {
switch strings.ToLower(lvl) {
case "debug":
return zap.DebugLevel
case "warn":
return zap.WarnLevel
case "error":
return zap.ErrorLevel
default:
return zap.InfoLevel
}
}
// ======================
// trace 支持
// ======================
func SetTraceID(c fiber.Ctx, traceID string) {
if c == nil {
return
}
// context
ctx := context.WithValue(c.Context(), TraceIDKey, traceID)
c.SetContext(ctx)
}
func FromCtx(ctx context.Context) *zap.Logger {
if ctx == nil {
return Log
}
traceID, _ := ctx.Value(TraceIDKey).(string)
if traceID == "" {
return Log
}
return Log.With(zap.String("trace_id", traceID))
}
// ======================
// 优雅退出
// ======================
func Sync() {
if Log != nil {
_ = Log.Sync()
}
}