package logs import ( "fmt" "github.com/natefinch/lumberjack" "github.com/sirupsen/logrus" "log" "os" "path" "path/filepath" "strings" "time" ) const ( defaultTimestamp = "2006-01-02 15:04:05.999" defaultLogDelimeter = "|" defaultLogLevel = logrus.ErrorLevel defaultLogFileSize = 50 // 日志保存天数 defaultMaxAge = 3 // 每天最多保存的日志数量 defaultMaxBackups = 3 ) type LogConfig struct { Path string AppInfo string MaxSize int // 日志文件最大尺寸,单位MB MaxBackups int // 最多保留的旧日志文件数 MaxAge int // 日志文件保留的最长时间,单位天 // 控制台输出 Console bool } // 定义Hook结构体 type FileHook struct { level logrus.Level formatter logrus.Formatter logger *log.Logger } func InitLog(level string, config LogConfig) error { FormatterInit() if config.Console { logrus.SetOutput(os.Stdout) } else { logrus.SetOutput(&NoOut{}) } if level == "" { level = "debug" } logrusLevel, err := logrus.ParseLevel(level) if err == nil { logrus.SetLevel(logrusLevel) } else { logrus.SetLevel(defaultLogLevel) logrus.WithError(err).Error("log level parse error, use default log level error") } logrus.SetFormatter(NewFormatter()) logrus.SetReportCaller(true) if config.Path == "" { config.Path = log_config.Path } if config.Path == "" { return fmt.Errorf("config.Path is nil") } log_config = config RegisterLogHook(config) return nil } // 实现Hook接口 func (hook *FileHook) Levels() []logrus.Level { return logrus.AllLevels[:hook.level+1] } func (hook *FileHook) Fire(entry *logrus.Entry) error { // 将日志消息格式化为字符串 bytes, err := hook.formatter.Format(entry) hook.logger.Print(string(bytes)) if err != nil { return fmt.Errorf("failed to format log entry: %v", err) } if err != nil { return fmt.Errorf("failed to write log entry: %v", err) } return nil } type NoOut struct { } func (no *NoOut) Write(p []byte) (n int, err error) { return 0, nil } func RegisterLogHook(logConf LogConfig) { hooks := make(logrus.LevelHooks) logrus.StandardLogger().ReplaceHooks(hooks) // 日志文件最大尺寸,不超过100MB if logConf.MaxSize > defaultLogFileSize || logConf.MaxSize < 1 { logConf.MaxSize = defaultLogFileSize } // 最多保留的旧日志文件数 if logConf.MaxBackups > defaultMaxBackups || logConf.MaxBackups == 0 { logConf.MaxBackups = defaultMaxBackups } // 日志文件保留的最长时间,单位天 if logConf.MaxAge > defaultMaxAge || logConf.MaxAge == 0 { logConf.MaxAge = defaultMaxAge } for _, val := range logrus.AllLevels { if val <= logrus.GetLevel() { fileName := filepath.Join(logConf.Path, logConf.AppInfo+"."+val.String()+".log") archive := &lumberjack.Logger{ Filename: fileName, MaxSize: logConf.MaxSize, // 日志文件最大尺寸,单位MB MaxBackups: logConf.MaxBackups, // 最多保留的旧日志文件数 MaxAge: logConf.MaxAge, // 日志文件保留的最长时间,单位天 LocalTime: true, // 使用本地时间 Compress: true, } sysLogger := &log.Logger{} sysLogger.SetOutput(archive) hooker := &FileHook{ level: val, formatter: NewFormatter(), logger: sysLogger, } hooks.Add(hooker) } } } func FormatterInit() { logrus.SetFormatter(NewFormatter()) logrus.SetReportCaller(true) } func NewFormatter() *MyFormatter { return &MyFormatter{ Pid: os.Getpid(), TimestampFormat: defaultTimestamp, } } // Custom log format definition type MyFormatter struct { Pid int TimestampFormat string } func (s *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) { //var callerFun string var fileInfo string var entryDataInfo string for key, val := range entry.Data { entryDataInfo += fmt.Sprintf(" %s=\"%v\"", key, val) } if entry.HasCaller() { //callerFun = entry.Caller.Function fileInfo = fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line) } timestamp := time.Now().Local().Format(s.TimestampFormat) msg := fmt.Sprintf("%s|%s|%d|%s] %s%s\n", timestamp, strings.ToUpper(entry.Level.String()), s.Pid, fileInfo, entry.Message, entryDataInfo) return []byte(msg), nil } var log_config LogConfig func GetLogConf() LogConfig { return log_config }