gin-vue-admin/server/core/zap.go

88 lines
3.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package core
import (
"context"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/core/internal"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/service"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
astutil "github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
"github.com/flipped-aurora/gin-vue-admin/server/utils/stacktrace"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"strings"
)
// Zap 获取 zap.Logger
// Author [SliverHorn](https://github.com/SliverHorn)
func Zap() (logger *zap.Logger) {
if ok, _ := utils.PathExists(global.GVA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
fmt.Printf("create %v directory\n", global.GVA_CONFIG.Zap.Director)
_ = os.Mkdir(global.GVA_CONFIG.Zap.Director, os.ModePerm)
}
levels := global.GVA_CONFIG.Zap.Levels()
length := len(levels)
cores := make([]zapcore.Core, 0, length)
for i := 0; i < length; i++ {
core := internal.NewZapCore(levels[i])
cores = append(cores, core)
}
// 通过 Hooks 捕捉 Error 及以上级别日志,写入系统错误表
dbHook := zap.Hooks(func(entry zapcore.Entry) error {
// 仅处理 Error 及以上级别
if entry.Level < zapcore.ErrorLevel {
return nil
}
// 避免与 GORM zap 写入互相递归:跳过由 gorm logger writer 触发的日志
if strings.Contains(entry.Caller.File, "gorm_logger_writer.go") {
return nil
}
// 避免重复记录 panic 恢复日志panic 由 GinRecovery 单独捕捉入库
if strings.Contains(entry.Message, "[Recovery from panic]") {
return nil
}
form := "后端"
level := entry.Level.String()
// 尽可能携带来源与堆栈信息(使用 runtime 采集并过滤 zap 内部栈)
info := entry.Message
if entry.Caller.File != "" {
info = fmt.Sprintf("%s | caller=%s:%d", info, entry.Caller.File, entry.Caller.Line)
}
stack := entry.Stack
if stack != "" {
info = fmt.Sprintf("%s | stack=%s", info, stack)
// 解析最终业务调用方,并提取其方法源码
if frame, ok := stacktrace.FindFinalCaller(stack); ok {
fnName, fnSrc, sLine, eLine, exErr := astutil.ExtractFuncSourceByPosition(frame.File, frame.Line)
if exErr == nil {
info = fmt.Sprintf("%s | final_caller=%s:%d (%s lines %d-%d)\n----- 产生日志的方法代码如下 -----\n%s", info, frame.File, frame.Line, fnName, sLine, eLine, fnSrc)
} else {
info = fmt.Sprintf("%s | final_caller=%s:%d (%s) | extract_err=%v", info, frame.File, frame.Line, fnName, exErr)
}
}
}
// 使用后台上下文,避免依赖 gin.Context
ctx := context.Background()
_ = service.ServiceGroupApp.SystemServiceGroup.SysErrorService.CreateSysError(ctx, &system.SysError{
Form: &form,
Info: &info,
Level: level,
})
return nil
})
logger = zap.New(zapcore.NewTee(cores...), dbHook)
// 启用 Error 及以上级别的堆栈捕捉,确保 entry.Stack 可用
opts := []zap.Option{zap.AddStacktrace(zapcore.ErrorLevel)}
if global.GVA_CONFIG.Zap.ShowLine {
opts = append(opts, zap.AddCaller())
}
logger = logger.WithOptions(opts...)
return logger
}