180 lines
4.1 KiB
Go
180 lines
4.1 KiB
Go
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)
|
||
}
|