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 }