From 360f7188e16fef699d5f09e0a429bc2d24c0f568 Mon Sep 17 00:00:00 2001 From: xuthus5 Date: Tue, 19 Jan 2021 22:07:28 +0800 Subject: [PATCH] local log hook --- global/log_hook.go | 152 +++++++++++++++++++++++++++++++++++ main.go | 195 ++++++++++++++++++++------------------------- 2 files changed, 237 insertions(+), 110 deletions(-) create mode 100644 global/log_hook.go diff --git a/global/log_hook.go b/global/log_hook.go new file mode 100644 index 0000000..add40d5 --- /dev/null +++ b/global/log_hook.go @@ -0,0 +1,152 @@ +package global + +import ( + "fmt" + "github.com/sirupsen/logrus" + "io" + "os" + "path/filepath" + "reflect" + "sync" +) + +type LocalHook struct { + lock *sync.Mutex + levels []logrus.Level // hook级别 + formatter logrus.Formatter // 格式 + path string // 写入path + writer io.Writer // io +} + +// ref: logrus/hooks.go. impl Hook interface +func (hook *LocalHook) Levels() []logrus.Level { + if len(hook.levels) == 0 { + return logrus.AllLevels + } + return hook.levels +} + +func (hook *LocalHook) ioWrite(entry *logrus.Entry) error { + log, err := hook.formatter.Format(entry) + if err != nil { + return err + } + + _, err = hook.writer.Write(log) + if err != nil { + return err + } + return nil +} + +func (hook *LocalHook) pathWrite(entry *logrus.Entry) error { + dir := filepath.Dir(hook.path) + if err := os.MkdirAll(dir, os.ModePerm); err != nil { + return err + } + + fd, err := os.OpenFile(hook.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666) + if err != nil { + return err + } + defer fd.Close() + + log, err := hook.formatter.Format(entry) + + if err != nil { + return err + } + + _, err = fd.Write(log) + return err +} + +func (hook *LocalHook) Fire(entry *logrus.Entry) error { + hook.lock.Lock() + defer hook.lock.Unlock() + + if hook.writer != nil { + return hook.ioWrite(entry) + } + + if hook.path != "" { + return hook.pathWrite(entry) + } + + return nil +} + +func (hook *LocalHook) SetFormatter(formatter logrus.Formatter) { + hook.lock.Lock() + defer hook.lock.Unlock() + + if formatter == nil { + // 用默认的 + formatter = &logrus.TextFormatter{DisableColors: true} + } else { + switch formatter.(type) { + case *logrus.TextFormatter: + textFormatter := formatter.(*logrus.TextFormatter) + textFormatter.DisableColors = true + default: + // todo + } + } + + hook.formatter = formatter +} + +func (hook *LocalHook) SetWriter(writer io.Writer) { + hook.lock.Lock() + defer hook.lock.Unlock() + hook.writer = writer +} + +func (hook *LocalHook) SetPath(path string) { + hook.lock.Lock() + defer hook.lock.Unlock() + hook.path = path +} + +func NewLocalHook(args interface{}, formatter logrus.Formatter, levels ...logrus.Level) *LocalHook { + hook := &LocalHook{ + lock: new(sync.Mutex), + } + hook.SetFormatter(formatter) + hook.levels = append(hook.levels, levels...) + + switch args.(type) { + case string: + hook.SetPath(args.(string)) + case io.Writer: + hook.SetWriter(args.(io.Writer)) + default: + panic(fmt.Sprintf("unsupported type: %v", reflect.TypeOf(args))) + } + + return hook +} + +func GetLogLevel(level string) []logrus.Level { + switch level { + case "trace": + return []logrus.Level{logrus.TraceLevel, logrus.DebugLevel, + logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel, + logrus.FatalLevel, logrus.PanicLevel} + case "debug": + return []logrus.Level{logrus.DebugLevel, logrus.InfoLevel, + logrus.WarnLevel, logrus.ErrorLevel, + logrus.FatalLevel, logrus.PanicLevel} + case "info": + return []logrus.Level{logrus.InfoLevel, logrus.WarnLevel, + logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel} + case "warn": + return []logrus.Level{logrus.WarnLevel, logrus.ErrorLevel, + logrus.FatalLevel, logrus.PanicLevel} + case "error": + return []logrus.Level{logrus.ErrorLevel, logrus.FatalLevel, + logrus.PanicLevel} + default: + return logrus.AllLevels + } +} diff --git a/main.go b/main.go index a525964..d61ef75 100644 --- a/main.go +++ b/main.go @@ -29,42 +29,14 @@ import ( "github.com/Mrs4s/go-cqhttp/global" jsoniter "github.com/json-iterator/go" rotatelogs "github.com/lestrrat-go/file-rotatelogs" - "github.com/rifflock/lfshook" log "github.com/sirupsen/logrus" easy "github.com/t-tomalak/logrus-easy-formatter" ) var json = jsoniter.ConfigCompatibleWithStandardLibrary +var conf *global.JsonConfig func init() { - log.SetFormatter(&easy.Formatter{ - TimestampFormat: "2006-01-02 15:04:05", - LogFormat: "[%time%] [%lvl%]: %msg% \n", - }) - w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24)) - if err == nil { - log.SetOutput(io.MultiWriter(os.Stderr, w)) - } - if !global.PathExists(global.IMAGE_PATH) { - if err := os.MkdirAll(global.IMAGE_PATH, 0755); err != nil { - log.Fatalf("创建图片缓存文件夹失败: %v", err) - } - } - if !global.PathExists(global.VOICE_PATH) { - if err := os.MkdirAll(global.VOICE_PATH, 0755); err != nil { - log.Fatalf("创建语音缓存文件夹失败: %v", err) - } - } - if !global.PathExists(global.VIDEO_PATH) { - if err := os.MkdirAll(global.VIDEO_PATH, 0755); err != nil { - log.Fatalf("创建视频缓存文件夹失败: %v", err) - } - } - if !global.PathExists(global.CACHE_PATH) { - if err := os.MkdirAll(global.CACHE_PATH, 0755); err != nil { - log.Fatalf("创建发送图片缓存文件夹失败: %v", err) - } - } if global.PathExists("cqhttp.json") { log.Info("发现 cqhttp.json 将在五秒后尝试导入配置,按 Ctrl+C 取消.") log.Warn("警告: 该操作会删除 cqhttp.json 并覆盖 config.hjson 文件.") @@ -94,6 +66,44 @@ func init() { } _ = os.Remove("cqhttp.json") } + + conf = getConfig() + if conf == nil { + return + } + + logFormatter := &easy.Formatter{ + TimestampFormat: "2006-01-02 15:04:05", + LogFormat: "[%time%] [%lvl%]: %msg% \n", + } + w, err := rotatelogs.New(path.Join("logs", "%Y-%m-%d.log"), rotatelogs.WithRotationTime(time.Hour*24)) + if err != nil { + log.Errorf("rotatelogs init err: %v", err) + panic(err) + } + + log.AddHook(global.NewLocalHook(w, logFormatter, global.GetLogLevel(conf.LogLevel)...)) + + if !global.PathExists(global.IMAGE_PATH) { + if err := os.MkdirAll(global.IMAGE_PATH, 0755); err != nil { + log.Fatalf("创建图片缓存文件夹失败: %v", err) + } + } + if !global.PathExists(global.VOICE_PATH) { + if err := os.MkdirAll(global.VOICE_PATH, 0755); err != nil { + log.Fatalf("创建语音缓存文件夹失败: %v", err) + } + } + if !global.PathExists(global.VIDEO_PATH) { + if err := os.MkdirAll(global.VIDEO_PATH, 0755); err != nil { + log.Fatalf("创建视频缓存文件夹失败: %v", err) + } + } + if !global.PathExists(global.CACHE_PATH) { + if err := os.MkdirAll(global.CACHE_PATH, 0755); err != nil { + log.Fatalf("创建发送图片缓存文件夹失败: %v", err) + } + } } func main() { @@ -120,93 +130,12 @@ func main() { } } - var conf *global.JsonConfig - if global.PathExists("config.json") { - conf = global.Load("config.json") - _ = conf.Save("config.hjson") - _ = os.Remove("config.json") - } else if os.Getenv("UIN") != "" { - log.Infof("将从环境变量加载配置.") - uin, _ := strconv.ParseInt(os.Getenv("UIN"), 10, 64) - pwd := os.Getenv("PASS") - post := os.Getenv("HTTP_POST") - conf = &global.JsonConfig{ - Uin: uin, - Password: pwd, - HttpConfig: &global.GoCQHttpConfig{ - Enabled: true, - Host: "0.0.0.0", - Port: 5700, - PostUrls: map[string]string{}, - }, - WSConfig: &global.GoCQWebsocketConfig{ - Enabled: true, - Host: "0.0.0.0", - Port: 6700, - }, - PostMessageFormat: "string", - Debug: os.Getenv("DEBUG") == "true", - } - if post != "" { - conf.HttpConfig.PostUrls[post] = os.Getenv("HTTP_SECRET") - } - } else { - conf = global.Load("config.hjson") - } - if conf == nil { - err := global.WriteAllText("config.hjson", global.DefaultConfigWithComments) - if err != nil { - log.Fatalf("创建默认配置文件时出现错误: %v", err) - return - } - log.Infof("默认配置文件已生成, 请编辑 config.hjson 后重启程序.") - time.Sleep(time.Second * 5) - return - } if conf.Uin == 0 || (conf.Password == "" && conf.PasswordEncrypted == "") { log.Warnf("请修改 config.hjson 以添加账号密码.") time.Sleep(time.Second * 5) return } - // log classified by level - // Collect all records up to the specified level (default level: warn) - logLevel := conf.LogLevel - if logLevel != "" { - date := time.Now().Format("2006-01-02") - var logPathMap lfshook.PathMap - switch conf.LogLevel { - case "warn": - logPathMap = lfshook.PathMap{ - log.WarnLevel: path.Join("logs", date+"-warn.log"), - log.ErrorLevel: path.Join("logs", date+"-warn.log"), - log.FatalLevel: path.Join("logs", date+"-warn.log"), - log.PanicLevel: path.Join("logs", date+"-warn.log"), - } - case "error": - logPathMap = lfshook.PathMap{ - log.ErrorLevel: path.Join("logs", date+"-error.log"), - log.FatalLevel: path.Join("logs", date+"-error.log"), - log.PanicLevel: path.Join("logs", date+"-error.log"), - } - default: - logPathMap = lfshook.PathMap{ - log.WarnLevel: path.Join("logs", date+"-warn.log"), - log.ErrorLevel: path.Join("logs", date+"-warn.log"), - log.FatalLevel: path.Join("logs", date+"-warn.log"), - log.PanicLevel: path.Join("logs", date+"-warn.log"), - } - } - - log.AddHook(lfshook.NewHook( - logPathMap, - &easy.Formatter{ - TimestampFormat: "2006-01-02 15:04:05", - LogFormat: "[%time%] [%lvl%]: %msg% \n", - }, - )) - } - log.Info("当前版本:", coolq.Version) if conf.Debug { log.SetLevel(log.DebugLevel) @@ -487,3 +416,49 @@ func restart(Args []string) { } cmd.Start() } + +func getConfig() *global.JsonConfig { + if global.PathExists("config.json") { + conf = global.Load("config.json") + _ = conf.Save("config.hjson") + _ = os.Remove("config.json") + } else if os.Getenv("UIN") != "" { + log.Infof("将从环境变量加载配置.") + uin, _ := strconv.ParseInt(os.Getenv("UIN"), 10, 64) + pwd := os.Getenv("PASS") + post := os.Getenv("HTTP_POST") + conf = &global.JsonConfig{ + Uin: uin, + Password: pwd, + HttpConfig: &global.GoCQHttpConfig{ + Enabled: true, + Host: "0.0.0.0", + Port: 5700, + PostUrls: map[string]string{}, + }, + WSConfig: &global.GoCQWebsocketConfig{ + Enabled: true, + Host: "0.0.0.0", + Port: 6700, + }, + PostMessageFormat: "string", + Debug: os.Getenv("DEBUG") == "true", + } + if post != "" { + conf.HttpConfig.PostUrls[post] = os.Getenv("HTTP_SECRET") + } + } else { + conf = global.Load("config.hjson") + } + if conf == nil { + err := global.WriteAllText("config.hjson", global.DefaultConfigWithComments) + if err != nil { + log.Fatalf("创建默认配置文件时出现错误: %v", err) + return nil + } + log.Infof("默认配置文件已生成, 请编辑 config.hjson 后重启程序.") + time.Sleep(time.Second * 5) + return nil + } + return conf +}