|
@@ -0,0 +1,561 @@
|
|
|
|
|
+package common
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "os"
|
|
|
|
|
+ "path/filepath"
|
|
|
|
|
+ "runtime"
|
|
|
|
|
+ "strings"
|
|
|
|
|
+ "time"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/sirupsen/logrus"
|
|
|
|
|
+ "gopkg.in/natefinch/lumberjack.v2"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+var (
|
|
|
|
|
+ Logger *logrus.Logger
|
|
|
|
|
+
|
|
|
|
|
+ // 不同级别的日志文件writer
|
|
|
|
|
+ infoLogger *lumberjack.Logger
|
|
|
|
|
+ warnLogger *lumberjack.Logger
|
|
|
|
|
+ errorLogger *lumberjack.Logger
|
|
|
|
|
+ mainLogger *lumberjack.Logger
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// 日志级别常量
|
|
|
|
|
+const (
|
|
|
|
|
+ LogLevelTrace = "trace"
|
|
|
|
|
+ LogLevelDebug = "debug"
|
|
|
|
|
+ LogLevelInfo = "info"
|
|
|
|
|
+ LogLevelWarn = "warn"
|
|
|
|
|
+ LogLevelError = "error"
|
|
|
|
|
+ LogLevelFatal = "fatal"
|
|
|
|
|
+ LogLevelPanic = "panic"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+// LogConfig 日志配置结构体
|
|
|
|
|
+type LogConfig struct {
|
|
|
|
|
+ LogDir string // 日志目录
|
|
|
|
|
+ LogLevel string // 日志级别
|
|
|
|
|
+ MaxSize int // 每个日志文件最大大小(MB)
|
|
|
|
|
+ MaxBackups int // 保留的旧日志文件数量
|
|
|
|
|
+ MaxAge int // 保留日志文件的最大天数
|
|
|
|
|
+ Compress bool // 是否压缩旧日志文件
|
|
|
|
|
+ LogModules []string // 需要记录日志的模块
|
|
|
|
|
+ EnableCaller bool // 是否显示调用者信息
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// InitLogger 初始化日志系统
|
|
|
|
|
+func InitLogger(config *LogConfig) error {
|
|
|
|
|
+ if config == nil {
|
|
|
|
|
+ // 使用默认配置
|
|
|
|
|
+ config = getDefaultLogConfig()
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保日志目录存在
|
|
|
|
|
+ if err := os.MkdirAll(config.LogDir, 0755); err != nil {
|
|
|
|
|
+ return fmt.Errorf("创建日志目录失败: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建logrus实例
|
|
|
|
|
+ Logger = logrus.New()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置日志级别
|
|
|
|
|
+ level, err := logrus.ParseLevel(config.LogLevel)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ level = logrus.InfoLevel
|
|
|
|
|
+ }
|
|
|
|
|
+ Logger.SetLevel(level)
|
|
|
|
|
+
|
|
|
|
|
+ // 设置日志格式
|
|
|
|
|
+ Logger.SetFormatter(&logrus.JSONFormatter{
|
|
|
|
|
+ TimestampFormat: "2006-01-02 15:04:05",
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 设置调用者信息
|
|
|
|
|
+ Logger.SetReportCaller(config.EnableCaller)
|
|
|
|
|
+
|
|
|
|
|
+ // 设置日志输出
|
|
|
|
|
+ if err := setLogOutput(config); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 记录系统启动日志
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "system_startup",
|
|
|
|
|
+ "pid": os.Getpid(),
|
|
|
|
|
+ }).Info("SCADA培训系统启动")
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// setLogOutput 设置日志输出
|
|
|
|
|
+func setLogOutput(config *LogConfig) error {
|
|
|
|
|
+ // 创建不同级别的日志文件
|
|
|
|
|
+ infoLogPath := filepath.Join(config.LogDir, "info.log")
|
|
|
|
|
+ warnLogPath := filepath.Join(config.LogDir, "warn.log")
|
|
|
|
|
+ errorLogPath := filepath.Join(config.LogDir, "error.log")
|
|
|
|
|
+
|
|
|
|
|
+ // 创建主日志文件
|
|
|
|
|
+ mainLogPath := filepath.Join(config.LogDir, "app.log")
|
|
|
|
|
+
|
|
|
|
|
+ // 为主日志创建writer
|
|
|
|
|
+ mainWriter := &lumberjack.Logger{
|
|
|
|
|
+ Filename: mainLogPath,
|
|
|
|
|
+ MaxSize: config.MaxSize,
|
|
|
|
|
+ MaxBackups: config.MaxBackups,
|
|
|
|
|
+ MaxAge: config.MaxAge,
|
|
|
|
|
+ Compress: config.Compress,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 同时输出到控制台和文件
|
|
|
|
|
+ // 使用MultiWriter同时写入控制台和文件
|
|
|
|
|
+ Logger.SetOutput(mainWriter)
|
|
|
|
|
+
|
|
|
|
|
+ // 保存writer引用,用于后续使用
|
|
|
|
|
+ mainLogger = mainWriter
|
|
|
|
|
+ infoLogger = &lumberjack.Logger{
|
|
|
|
|
+ Filename: infoLogPath,
|
|
|
|
|
+ MaxSize: config.MaxSize,
|
|
|
|
|
+ MaxBackups: config.MaxBackups,
|
|
|
|
|
+ MaxAge: config.MaxAge,
|
|
|
|
|
+ Compress: config.Compress,
|
|
|
|
|
+ }
|
|
|
|
|
+ warnLogger = &lumberjack.Logger{
|
|
|
|
|
+ Filename: warnLogPath,
|
|
|
|
|
+ MaxSize: config.MaxSize,
|
|
|
|
|
+ MaxBackups: config.MaxBackups,
|
|
|
|
|
+ MaxAge: config.MaxAge,
|
|
|
|
|
+ Compress: config.Compress,
|
|
|
|
|
+ }
|
|
|
|
|
+ errorLogger = &lumberjack.Logger{
|
|
|
|
|
+ Filename: errorLogPath,
|
|
|
|
|
+ MaxSize: config.MaxSize,
|
|
|
|
|
+ MaxBackups: config.MaxBackups,
|
|
|
|
|
+ MaxAge: config.MaxAge,
|
|
|
|
|
+ Compress: config.Compress,
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 启动日志清理协程
|
|
|
|
|
+ go startLogCleaner(config)
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// startLogCleaner 启动日志清理协程
|
|
|
|
|
+func startLogCleaner(config *LogConfig) {
|
|
|
|
|
+ // 每天凌晨2点执行日志清理
|
|
|
|
|
+ ticker := time.NewTicker(24 * time.Hour)
|
|
|
|
|
+ defer ticker.Stop()
|
|
|
|
|
+
|
|
|
|
|
+ // 计算到下一个凌晨2点的时间
|
|
|
|
|
+ now := time.Now()
|
|
|
|
|
+ next := time.Date(now.Year(), now.Month(), now.Day(), 2, 0, 0, 0, now.Location())
|
|
|
|
|
+ if next.Before(now) {
|
|
|
|
|
+ next = next.Add(24 * time.Hour)
|
|
|
|
|
+ }
|
|
|
|
|
+ initialDelay := next.Sub(now)
|
|
|
|
|
+
|
|
|
|
|
+ // 先等待到下一个凌晨2点
|
|
|
|
|
+ time.Sleep(initialDelay)
|
|
|
|
|
+
|
|
|
|
|
+ // 执行日志清理
|
|
|
|
|
+ cleanOldLogs(config)
|
|
|
|
|
+
|
|
|
|
|
+ for range ticker.C {
|
|
|
|
|
+ cleanOldLogs(config)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// cleanOldLogs 清理旧的日志文件
|
|
|
|
|
+func cleanOldLogs(config *LogConfig) {
|
|
|
|
|
+ if config.MaxAge <= 0 {
|
|
|
|
|
+ return // 不启用自动清理
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 清理info级别的日志
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "info-*.log.gz", config.MaxAge)
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "info-*.log", config.MaxAge)
|
|
|
|
|
+
|
|
|
|
|
+ // 清理warn级别的日志
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "warn-*.log.gz", config.MaxAge)
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "warn-*.log", config.MaxAge)
|
|
|
|
|
+
|
|
|
|
|
+ // 清理error级别的日志
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "error-*.log.gz", config.MaxAge)
|
|
|
|
|
+ cleanOldLogsByPattern(config.LogDir, "error-*.log", config.MaxAge)
|
|
|
|
|
+
|
|
|
|
|
+ // 记录日志清理操作
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "log_cleanup",
|
|
|
|
|
+ "max_age_days": config.MaxAge,
|
|
|
|
|
+ }).Info("执行日志清理操作")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// cleanOldLogsByPattern 根据模式清理旧的日志文件
|
|
|
|
|
+func cleanOldLogsByPattern(logDir, pattern string, maxAge int) {
|
|
|
|
|
+ matches, err := filepath.Glob(filepath.Join(logDir, pattern))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "log_cleanup_error",
|
|
|
|
|
+ "error": err.Error(),
|
|
|
|
|
+ "pattern": pattern,
|
|
|
|
|
+ }).Error("获取日志文件列表失败")
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ cutoffTime := time.Now().AddDate(0, 0, -maxAge)
|
|
|
|
|
+
|
|
|
|
|
+ for _, filePath := range matches {
|
|
|
|
|
+ fileInfo, err := os.Stat(filePath)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if fileInfo.ModTime().Before(cutoffTime) {
|
|
|
|
|
+ if err := os.Remove(filePath); err != nil {
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "log_file_delete_error",
|
|
|
|
|
+ "error": err.Error(),
|
|
|
|
|
+ "file": filePath,
|
|
|
|
|
+ }).Error("删除旧日志文件失败")
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "log_file_deleted",
|
|
|
|
|
+ "file": filePath,
|
|
|
|
|
+ "mod_time": fileInfo.ModTime().Format("2006-01-02 15:04:05"),
|
|
|
|
|
+ }).Debug("已删除旧日志文件")
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// SetLogLevel 动态设置日志级别
|
|
|
|
|
+func SetLogLevel(level string) error {
|
|
|
|
|
+ if Logger == nil {
|
|
|
|
|
+ return fmt.Errorf("日志系统未初始化")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ logLevel, err := logrus.ParseLevel(level)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("无效的日志级别: %s", level)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Logger.SetLevel(logLevel)
|
|
|
|
|
+ Logger.WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "log_level_changed",
|
|
|
|
|
+ "new_level": level,
|
|
|
|
|
+ }).Info("日志级别已更新")
|
|
|
|
|
+
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GetLogFilesInfo 获取日志文件信息
|
|
|
|
|
+func GetLogFilesInfo() ([]map[string]interface{}, error) {
|
|
|
|
|
+ if Logger == nil {
|
|
|
|
|
+ return nil, fmt.Errorf("日志系统未初始化")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取log目录
|
|
|
|
|
+ logDir := filepath.Dir(getDefaultLogConfig().LogDir)
|
|
|
|
|
+
|
|
|
|
|
+ // 查找所有日志文件
|
|
|
|
|
+ patterns := []string{"info*.log", "warn*.log", "error*.log"}
|
|
|
|
|
+ var result []map[string]interface{}
|
|
|
|
|
+
|
|
|
|
|
+ for _, pattern := range patterns {
|
|
|
|
|
+ matches, err := filepath.Glob(filepath.Join(logDir, pattern))
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ for _, filePath := range matches {
|
|
|
|
|
+ fileInfo, err := os.Stat(filePath)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fileInfoMap := map[string]interface{}{
|
|
|
|
|
+ "name": filepath.Base(filePath),
|
|
|
|
|
+ "path": filePath,
|
|
|
|
|
+ "size": fileInfo.Size(),
|
|
|
|
|
+ "mod_time": fileInfo.ModTime().Format("2006-01-02 15:04:05"),
|
|
|
|
|
+ "is_compressed": strings.HasSuffix(filePath, ".gz"),
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ result = append(result, fileInfoMap)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// getDefaultLogConfig 获取默认日志配置
|
|
|
|
|
+func getDefaultLogConfig() *LogConfig {
|
|
|
|
|
+ exePath, err := os.Executable()
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ exePath = "."
|
|
|
|
|
+ }
|
|
|
|
|
+ exeDir := filepath.Dir(exePath)
|
|
|
|
|
+
|
|
|
|
|
+ return &LogConfig{
|
|
|
|
|
+ LogDir: filepath.Join(exeDir, "log"),
|
|
|
|
|
+ LogLevel: "info",
|
|
|
|
|
+ MaxSize: 100, // 100MB
|
|
|
|
|
+ MaxBackups: 30, // 保留30个旧文件
|
|
|
|
|
+ MaxAge: 90, // 保留90天
|
|
|
|
|
+ Compress: true,
|
|
|
|
|
+ EnableCaller: true,
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// GetLogger 获取日志实例
|
|
|
|
|
+func GetLogger() *logrus.Logger {
|
|
|
|
|
+ if Logger == nil {
|
|
|
|
|
+ // 如果未初始化,使用默认配置初始化
|
|
|
|
|
+ InitLogger(nil)
|
|
|
|
|
+ }
|
|
|
|
|
+ return Logger
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogWithFields 带字段的日志记录
|
|
|
|
|
+func LogWithFields(fields logrus.Fields) *logrus.Entry {
|
|
|
|
|
+ return GetLogger().WithFields(fields)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogInfo 记录信息级别日志
|
|
|
|
|
+func LogInfo(msg string, fields ...logrus.Fields) {
|
|
|
|
|
+ logger := GetLogger()
|
|
|
|
|
+
|
|
|
|
|
+ // 写入主日志文件
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ entry := logger.WithFields(fields[0])
|
|
|
|
|
+ entry.Info(msg)
|
|
|
|
|
+ // 同时写入info级别的日志文件
|
|
|
|
|
+ writeToLevelFile("info", msg, fields[0])
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Info(msg)
|
|
|
|
|
+ // 同时写入info级别的日志文件
|
|
|
|
|
+ writeToLevelFile("info", msg, nil)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogWarn 记录警告级别日志
|
|
|
|
|
+func LogWarn(msg string, fields ...logrus.Fields) {
|
|
|
|
|
+ logger := GetLogger()
|
|
|
|
|
+
|
|
|
|
|
+ // 写入主日志文件
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ entry := logger.WithFields(fields[0])
|
|
|
|
|
+ entry.Warn(msg)
|
|
|
|
|
+ // 同时写入warn级别的日志文件
|
|
|
|
|
+ writeToLevelFile("warn", msg, fields[0])
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Warn(msg)
|
|
|
|
|
+ // 同时写入warn级别的日志文件
|
|
|
|
|
+ writeToLevelFile("warn", msg, nil)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogError 记录错误级别日志
|
|
|
|
|
+func LogError(msg string, err error, fields ...logrus.Fields) {
|
|
|
|
|
+ logger := GetLogger()
|
|
|
|
|
+ logFields := logrus.Fields{}
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ logFields["error"] = err.Error()
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 写入主日志文件
|
|
|
|
|
+ entry := logger.WithFields(logFields)
|
|
|
|
|
+ entry.Error(msg)
|
|
|
|
|
+
|
|
|
|
|
+ // 同时写入error级别的日志文件
|
|
|
|
|
+ writeToLevelFile("error", msg, logFields)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogDebug 记录调试级别日志
|
|
|
|
|
+func LogDebug(msg string, fields ...logrus.Fields) {
|
|
|
|
|
+ logger := GetLogger()
|
|
|
|
|
+
|
|
|
|
|
+ // 写入主日志文件(debug级别不单独记录到debug.log)
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ entry := logger.WithFields(fields[0])
|
|
|
|
|
+ entry.Debug(msg)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Debug(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogTrace 记录跟踪级别日志
|
|
|
|
|
+func LogTrace(msg string, fields ...logrus.Fields) {
|
|
|
|
|
+ logger := GetLogger()
|
|
|
|
|
+
|
|
|
|
|
+ // 写入主日志文件(trace级别不单独记录到trace.log)
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ entry := logger.WithFields(fields[0])
|
|
|
|
|
+ entry.Trace(msg)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ logger.Trace(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// writeToLevelFile 将日志写入对应的级别日志文件
|
|
|
|
|
+func writeToLevelFile(level, msg string, fields logrus.Fields) {
|
|
|
|
|
+ var writer *lumberjack.Logger
|
|
|
|
|
+ switch level {
|
|
|
|
|
+ case "info":
|
|
|
|
|
+ writer = infoLogger
|
|
|
|
|
+ case "warn":
|
|
|
|
|
+ writer = warnLogger
|
|
|
|
|
+ case "error":
|
|
|
|
|
+ writer = errorLogger
|
|
|
|
|
+ default:
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if writer == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 创建文件专用的logger
|
|
|
|
|
+ fileLogger := logrus.New()
|
|
|
|
|
+ fileLogger.SetFormatter(&logrus.JSONFormatter{
|
|
|
|
|
+ TimestampFormat: "2006-01-02 15:04:05",
|
|
|
|
|
+ })
|
|
|
|
|
+ fileLogger.SetOutput(writer)
|
|
|
|
|
+
|
|
|
|
|
+ // 根据级别设置日志级别
|
|
|
|
|
+ switch level {
|
|
|
|
|
+ case "info":
|
|
|
|
|
+ fileLogger.SetLevel(logrus.InfoLevel)
|
|
|
|
|
+ if fields != nil {
|
|
|
|
|
+ fileLogger.WithFields(fields).Info(msg)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fileLogger.Info(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+ case "warn":
|
|
|
|
|
+ fileLogger.SetLevel(logrus.WarnLevel)
|
|
|
|
|
+ if fields != nil {
|
|
|
|
|
+ fileLogger.WithFields(fields).Warn(msg)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fileLogger.Warn(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+ case "error":
|
|
|
|
|
+ fileLogger.SetLevel(logrus.ErrorLevel)
|
|
|
|
|
+ if fields != nil {
|
|
|
|
|
+ fileLogger.WithFields(fields).Error(msg)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ fileLogger.Error(msg)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogRequest 记录HTTP请求日志
|
|
|
|
|
+func LogRequest(method, path, ip, userAgent string, statusCode int, duration time.Duration) {
|
|
|
|
|
+ GetLogger().WithFields(logrus.Fields{
|
|
|
|
|
+ "event": "http_request",
|
|
|
|
|
+ "method": method,
|
|
|
|
|
+ "path": path,
|
|
|
|
|
+ "ip": ip,
|
|
|
|
|
+ "user_agent": userAgent,
|
|
|
|
|
+ "status": statusCode,
|
|
|
|
|
+ "duration": duration.Milliseconds(),
|
|
|
|
|
+ }).Info("HTTP请求")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogResponse 记录HTTP响应日志
|
|
|
|
|
+func LogResponse(data interface{}, fields ...logrus.Fields) {
|
|
|
|
|
+ logFields := logrus.Fields{
|
|
|
|
|
+ "event": "http_response",
|
|
|
|
|
+ "data": data,
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ GetLogger().WithFields(logFields).Info("HTTP响应")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogDBOperation 记录数据库操作日志
|
|
|
|
|
+func LogDBOperation(operation, table string, duration time.Duration, fields ...logrus.Fields) {
|
|
|
|
|
+ logFields := logrus.Fields{
|
|
|
|
|
+ "event": "db_operation",
|
|
|
|
|
+ "operation": operation,
|
|
|
|
|
+ "table": table,
|
|
|
|
|
+ "duration": duration.Milliseconds(),
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ GetLogger().WithFields(logFields).Info("数据库操作")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogUserAction 记录用户操作日志
|
|
|
|
|
+func LogUserAction(userID, userName, action, module string, fields ...logrus.Fields) {
|
|
|
|
|
+ logFields := logrus.Fields{
|
|
|
|
|
+ "event": "user_action",
|
|
|
|
|
+ "user_id": userID,
|
|
|
|
|
+ "user_name": userName,
|
|
|
|
|
+ "action": action,
|
|
|
|
|
+ "module": module,
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ GetLogger().WithFields(logFields).Info("用户操作")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogSystemError 记录系统错误日志
|
|
|
|
|
+func LogSystemError(component, operation string, err error, fields ...logrus.Fields) {
|
|
|
|
|
+ logFields := logrus.Fields{
|
|
|
|
|
+ "event": "system_error",
|
|
|
|
|
+ "component": component,
|
|
|
|
|
+ "operation": operation,
|
|
|
|
|
+ "error": err.Error(),
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 添加调用者信息
|
|
|
|
|
+ if pc, file, line, ok := runtime.Caller(1); ok {
|
|
|
|
|
+ funcName := runtime.FuncForPC(pc).Name()
|
|
|
|
|
+ logFields["caller"] = fmt.Sprintf("%s:%d:%s", filepath.Base(file), line, funcName)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ GetLogger().WithFields(logFields).Error("系统错误")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// LogBusinessEvent 记录业务事件日志
|
|
|
|
|
+func LogBusinessEvent(eventType, description string, fields ...logrus.Fields) {
|
|
|
|
|
+ logFields := logrus.Fields{
|
|
|
|
|
+ "event": "business_event",
|
|
|
|
|
+ "event_type": eventType,
|
|
|
|
|
+ "description": description,
|
|
|
|
|
+ }
|
|
|
|
|
+ if len(fields) > 0 {
|
|
|
|
|
+ for k, v := range fields[0] {
|
|
|
|
|
+ logFields[k] = v
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ GetLogger().WithFields(logFields).Info("业务事件")
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// CloseLogger 关闭日志系统
|
|
|
|
|
+func CloseLogger() {
|
|
|
|
|
+ if Logger != nil {
|
|
|
|
|
+ Logger.Info("SCADA培训系统关闭")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|