l_ai_knowledge/internal/logger/logger.go

253 lines
6.5 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 logger
import (
"context"
"fmt"
"path"
"runtime"
"sort"
"strings"
"github.com/sirupsen/logrus"
"knowlege-lsxd/internal/types"
)
// LogLevel 日志级别类型
type LogLevel string
// 日志级别常量
const (
LevelDebug LogLevel = "debug"
LevelInfo LogLevel = "info"
LevelWarn LogLevel = "warn"
LevelError LogLevel = "error"
LevelFatal LogLevel = "fatal"
)
// ANSI颜色代码
const (
colorRed = "\033[31m"
colorGreen = "\033[32m"
colorYellow = "\033[33m"
colorBlue = "\033[34m"
colorPurple = "\033[35m"
colorCyan = "\033[36m"
colorReset = "\033[0m"
)
type CustomFormatter struct {
ForceColor bool // 是否强制使用颜色,即使在非终端环境下
}
func (f *CustomFormatter) Format(entry *logrus.Entry) ([]byte, error) {
timestamp := entry.Time.Format("2006-01-02 15:04:05.000")
level := strings.ToUpper(entry.Level.String())
// 根据日志级别设置颜色
var levelColor, resetColor string
if f.ForceColor {
switch entry.Level {
case logrus.DebugLevel:
levelColor = colorCyan
case logrus.InfoLevel:
levelColor = colorGreen
case logrus.WarnLevel:
levelColor = colorYellow
case logrus.ErrorLevel:
levelColor = colorRed
case logrus.FatalLevel:
levelColor = colorPurple
default:
levelColor = colorReset
}
resetColor = colorReset
}
// 取出 caller 字段
caller := ""
if val, ok := entry.Data["caller"]; ok {
caller = fmt.Sprintf("%v", val)
}
// 拼接字段部分request_id 优先,其他排序后输出
fields := ""
// request_id 优先输出
if v, ok := entry.Data["request_id"]; ok {
fields += fmt.Sprintf("request_id=%v ", v)
}
// 其余字段排序后输出
keys := make([]string, 0, len(entry.Data))
for k := range entry.Data {
if k != "caller" && k != "request_id" {
keys = append(keys, k)
}
}
sort.Strings(keys)
for _, k := range keys {
fields += fmt.Sprintf("%s=%v ", k, entry.Data[k])
}
fields = strings.TrimSpace(fields)
// 拼接最终输出内容,添加颜色
return []byte(fmt.Sprintf("%s%-5s%s[%s] [%s] %-20s | %s\n",
levelColor, level, resetColor, timestamp, fields, caller, entry.Message)), nil
}
// 初始化全局日志设置
func init() {
// 设置日志格式而不修改全局时区
logrus.SetFormatter(&CustomFormatter{ForceColor: true})
logrus.SetReportCaller(false)
}
// GetLogger 获取日志实例
func GetLogger(c context.Context) *logrus.Entry {
if logger := c.Value(types.LoggerContextKey); logger != nil {
return logger.(*logrus.Entry)
}
newLogger := logrus.New()
newLogger.SetFormatter(&CustomFormatter{ForceColor: true})
// 设置默认日志级别
newLogger.SetLevel(logrus.DebugLevel)
// 启用调用者信息
return logrus.NewEntry(newLogger)
}
// SetLogLevel 设置日志级别
func SetLogLevel(level LogLevel) {
var logLevel logrus.Level
switch level {
case LevelDebug:
logLevel = logrus.DebugLevel
case LevelInfo:
logLevel = logrus.InfoLevel
case LevelWarn:
logLevel = logrus.WarnLevel
case LevelError:
logLevel = logrus.ErrorLevel
case LevelFatal:
logLevel = logrus.FatalLevel
default:
logLevel = logrus.InfoLevel
}
logrus.SetLevel(logLevel)
}
// 添加调用者字段
func addCaller(entry *logrus.Entry, skip int) *logrus.Entry {
pc, file, line, ok := runtime.Caller(skip)
if !ok {
return entry
}
shortFile := path.Base(file)
funcName := "unknown"
if fn := runtime.FuncForPC(pc); fn != nil {
// 只保留函数名,不带包路径(如 doSomething
fullName := path.Base(fn.Name())
parts := strings.Split(fullName, ".")
funcName = parts[len(parts)-1]
}
return entry.WithField("caller", fmt.Sprintf("%s:%d[%s]", shortFile, line, funcName))
}
// WithRequestID 在日志中添加请求ID
func WithRequestID(c context.Context, requestID string) context.Context {
return WithField(c, "request_id", requestID)
}
// WithField 向日志中添加一个字段
func WithField(c context.Context, key string, value interface{}) context.Context {
logger := GetLogger(c).WithField(key, value)
return context.WithValue(c, types.LoggerContextKey, logger)
}
// WithFields 向日志中添加多个字段
func WithFields(c context.Context, fields logrus.Fields) context.Context {
logger := GetLogger(c).WithFields(fields)
return context.WithValue(c, types.LoggerContextKey, logger)
}
// Debug 输出调试级别的日志
func Debug(c context.Context, args ...interface{}) {
addCaller(GetLogger(c), 2).Debug(args...)
}
// Debugf 使用格式化字符串输出调试级别的日志
func Debugf(c context.Context, format string, args ...interface{}) {
addCaller(GetLogger(c), 2).Debugf(format, args...)
}
// Info 输出信息级别的日志
func Info(c context.Context, args ...interface{}) {
addCaller(GetLogger(c), 2).Info(args...)
}
// Infof 使用格式化字符串输出信息级别的日志
func Infof(c context.Context, format string, args ...interface{}) {
addCaller(GetLogger(c), 2).Infof(format, args...)
}
// Warn 输出警告级别的日志
func Warn(c context.Context, args ...interface{}) {
addCaller(GetLogger(c), 2).Warn(args...)
}
// Warnf 使用格式化字符串输出警告级别的日志
func Warnf(c context.Context, format string, args ...interface{}) {
addCaller(GetLogger(c), 2).Warnf(format, args...)
}
// Error 输出错误级别的日志
func Error(c context.Context, args ...interface{}) {
addCaller(GetLogger(c), 2).Error(args...)
}
// Errorf 使用格式化字符串输出错误级别的日志
func Errorf(c context.Context, format string, args ...interface{}) {
addCaller(GetLogger(c), 2).Errorf(format, args...)
}
// ErrorWithFields 输出带有额外字段的错误级别日志
func ErrorWithFields(c context.Context, err error, fields logrus.Fields) {
if fields == nil {
fields = logrus.Fields{}
}
if err != nil {
fields["error"] = err.Error()
}
addCaller(GetLogger(c), 2).WithFields(fields).Error("发生错误")
}
// Fatal 输出致命级别的日志并退出程序
func Fatal(c context.Context, args ...interface{}) {
addCaller(GetLogger(c), 2).Fatal(args...)
}
// Fatalf 使用格式化字符串输出致命级别的日志并退出程序
func Fatalf(c context.Context, format string, args ...interface{}) {
addCaller(GetLogger(c), 2).Fatalf(format, args...)
}
// CloneContext 复制上下文中的关键信息到新上下文
func CloneContext(ctx context.Context) context.Context {
newCtx := context.Background()
for _, k := range []types.ContextKey{
types.LoggerContextKey,
types.TenantIDContextKey,
types.RequestIDContextKey,
types.TenantInfoContextKey,
} {
if v := ctx.Value(k); v != nil {
newCtx = context.WithValue(newCtx, k, v)
}
}
return newCtx
}