Files
workspace/code/app/app-go/pkg/logger/logger.go
T
2026-05-07 21:34:09 +08:00

234 lines
4.3 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)
// ======================
// encoder 配置
// ======================
encoderConfig := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
MessageKey: "msg",
CallerKey: "caller",
EncodeCaller: zapcore.ShortCallerEncoder,
}
// 👉 文件 JSON encoder
jsonEncoder := zapcore.NewJSONEncoder(func() zapcore.EncoderConfig {
cfg := encoderConfig
cfg.EncodeLevel = zapcore.CapitalLevelEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return cfg
}())
// 👉 控制台 encoder(可读)
consoleEncoder := zapcore.NewConsoleEncoder(func() zapcore.EncoderConfig {
cfg := encoderConfig
cfg.EncodeLevel = zapcore.CapitalColorLevelEncoder
cfg.EncodeTime = zapcore.TimeEncoderOfLayout("2006-01-02 15:04:05")
return cfg
}())
// ======================
// writer
// ======================
consoleWriter := zapcore.AddSync(os.Stdout)
infoWriter := getWriter(cfg, "info")
errorWriter := getWriter(cfg, "error")
// ======================
// core
// ======================
// 控制台(所有级别)
consoleCore := zapcore.NewCore(
consoleEncoder,
consoleWriter,
level,
)
// info 文件
infoCore := zapcore.NewCore(
jsonEncoder,
infoWriter,
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl < zapcore.ErrorLevel && lvl >= level
}),
)
// error 文件
errorCore := zapcore.NewCore(
jsonEncoder,
errorWriter,
zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
return lvl >= zapcore.ErrorLevel
}),
)
core := zapcore.NewTee(
consoleCore,
infoCore,
errorCore,
)
Log = zap.New(
core,
zap.AddCaller(), // 打印调用位置
zap.AddCallerSkip(1), // 跳过封装层
)
}
// ======================
// 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
}
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()
}
}