daShangDao_psiServer/utils/log.go
2026-06-15 13:47:39 +08:00

180 lines
4.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 utils
import (
"encoding/json"
"github.com/gin-gonic/gin"
rotateLogs "github.com/lestrrat-go/file-rotatelogs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"os"
"psi/config"
systemRes "psi/models/response"
"sync"
"time"
)
var loggerFactory map[string]*logrus.Logger
func InitLogger() error {
var err error
loggerFactory, err = NewLogger()
if err != nil {
return err
}
return nil
}
// GetLogger 获取日志配置(从 config.yaml 读取)
func GetLogger(conf interface{}) error {
logConfig := config.AppConfig.Log
switch v := conf.(type) {
case *struct {
Channel map[string]string
MaxAge int
RotateTime int
RootPath string
}:
v.Channel = logConfig.Channel
v.MaxAge = logConfig.MaxAge
v.RotateTime = logConfig.RotateTime
v.RootPath = logConfig.RootPath
default:
return errors.New("不支持的配置类型")
}
return nil
}
// NewLogger 初始化日志
func NewLogger() (map[string]*logrus.Logger, error) {
conf := struct {
Channel map[string]string
MaxAge int
RotateTime int
RootPath string
}{}
if err := GetLogger(&conf); err != nil {
return nil, errors.Wrap(err, "logger config get fail")
}
logger := make(map[string]*logrus.Logger, len(conf.Channel))
var (
wg sync.WaitGroup
mLock sync.Mutex
)
for k, v := range conf.Channel {
wg.Add(1)
go func(wg *sync.WaitGroup, channel string, path string, mLock sync.Locker) {
defer func() {
wg.Done()
}()
rootPath := conf.RootPath
if rootPath == "" {
rootPath = os.Getenv("run_path") + "/runtime/logs"
}
fullPath := rootPath + path
log := createLogger(fullPath, conf.MaxAge, conf.RotateTime)
mLock.Lock()
logger[channel] = log
mLock.Unlock()
}(&wg, k, v, &mLock)
}
wg.Wait()
return logger, nil
}
// 创建日志
func createLogger(path string, maxAge int, rotateTime int) *logrus.Logger {
// 确保目录存在
dir := path[:len(path)-len("/err.log")]
if len(path) > 0 {
// 提取目录路径
lastSlash := -1
for i := len(path) - 1; i >= 0; i-- {
if path[i] == '/' || path[i] == '\\' {
lastSlash = i
break
}
}
if lastSlash > 0 {
dir = path[:lastSlash]
if err := os.MkdirAll(dir, 0755); err != nil {
logrus.Errorf("创建日志目录失败: %v, path: %s", err, dir)
}
}
}
writer, err := rotateLogs.New(
path+"%Y%m%d%H%M",
//rotateLogs.WithLinkName(path),
rotateLogs.WithMaxAge(time.Duration(maxAge)*time.Hour),
rotateLogs.WithRotationTime(time.Duration(rotateTime)*time.Hour),
)
if err != nil {
logrus.Errorf("创建日志文件失败: %v, path: %s", err, path)
// 如果创建失败,使用标准输出作为备选
return createConsoleLogger()
}
log := new(logrus.Logger)
log.SetFormatter(&logrus.JSONFormatter{})
log.SetOutput(writer)
log.SetLevel(logrus.InfoLevel)
log.SetNoLock()
return log
}
// 创建控制台日志器(备选方案)
func createConsoleLogger() *logrus.Logger {
log := new(logrus.Logger)
log.SetFormatter(&logrus.JSONFormatter{})
log.SetOutput(os.Stdout)
log.SetLevel(logrus.InfoLevel)
return log
}
// ErrorLog LEVEL-ERROR
func ErrorLog(channel string, params logrus.Fields) {
if _, ok := loggerFactory[channel]; !ok {
return
}
loggerFactory[channel].WithFields(params).Error()
}
// InfoLog LEVEL-INFO
func InfoLog(channel string, params logrus.Fields) {
if _, ok := loggerFactory[channel]; !ok {
return
}
loggerFactory[channel].WithFields(params).Info(params)
}
// FailWithRequestLog 失败时记录请求参数并返回错误响应
// channel: 日志渠道(如 LoggerChannelWork, LoggerChannelRequest
// source: 错误来源描述
// err: 错误对象
// c: gin上下文
// req: 请求参数(可选,传入会自动序列化到日志)
func FailWithRequestLog(channel string, source string, err error, c *gin.Context, req interface{}) {
fields := logrus.Fields{
"source": source,
"err_msg": err.Error(),
}
if req != nil {
if reqJSON, jsonErr := json.Marshal(req); jsonErr == nil {
fields["request_params"] = string(reqJSON)
} else {
fields["request_params_error"] = jsonErr.Error()
}
}
ErrorLog(channel, fields)
systemRes.FailWithMessage(err.Error(), c)
}