1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-05 03:23:49 +08:00
go-cqhttp/global/log_hook.go
2021-10-05 04:55:59 +08:00

264 lines
6.1 KiB
Go

package global
import (
"fmt"
"io"
"os"
"path/filepath"
"reflect"
"strings"
"sync"
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
)
// LocalHook logrus本地钩子
type LocalHook struct {
lock *sync.Mutex
levels []logrus.Level // hook级别
formatter logrus.Formatter // 格式
path string // 写入path
writer io.Writer // io
}
// Levels 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, 0o666)
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
}
// Fire ref: logrus/hooks.go impl Hook interface
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
}
// SetFormatter 设置日志格式
func (hook *LocalHook) SetFormatter(consoleFormatter, fileFormatter logrus.Formatter) {
hook.lock.Lock()
defer hook.lock.Unlock()
consoleFormatter = tryChangeFormatter(consoleFormatter)
fileFormatter = tryChangeFormatter(fileFormatter)
// 支持处理windows平台的console色彩
logrus.SetOutput(colorable.NewColorableStdout())
// 用于在console写出
logrus.SetFormatter(consoleFormatter)
// 用于写入文件
hook.formatter = fileFormatter
}
func tryChangeFormatter(formatter logrus.Formatter) logrus.Formatter {
if formatter == nil {
// 用默认的
formatter = &logrus.TextFormatter{DisableColors: true}
} else {
switch f := formatter.(type) {
case *logrus.TextFormatter:
textFormatter := f
textFormatter.DisableColors = true
default:
// todo
}
}
return formatter
}
// SetWriter 设置Writer
func (hook *LocalHook) SetWriter(writer io.Writer) {
hook.lock.Lock()
defer hook.lock.Unlock()
hook.writer = writer
}
// SetPath 设置日志写入路径
func (hook *LocalHook) SetPath(path string) {
hook.lock.Lock()
defer hook.lock.Unlock()
hook.path = path
}
// NewLocalHook 初始化本地日志钩子实现
func NewLocalHook(args interface{}, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook {
hook := &LocalHook{
lock: new(sync.Mutex),
}
hook.SetFormatter(consoleFormatter, fileFormatter)
hook.levels = append(hook.levels, levels...)
switch arg := args.(type) {
case string:
hook.SetPath(arg)
case io.Writer:
hook.SetWriter(arg)
default:
panic(fmt.Sprintf("unsupported type: %v", reflect.TypeOf(args)))
}
return hook
}
// GetLogLevel 获取日志等级
//
// 可能的值有
//
// "trace","debug","info","warn","warn","error"
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.Level{
logrus.InfoLevel, logrus.WarnLevel,
logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel,
}
}
}
// LogFormat specialize for go-cqhttp
type LogFormat struct {
EnableColor bool
}
// Format implements logrus.Formatter
func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
buf := NewBuffer()
defer PutBuffer(buf)
if f.EnableColor {
buf.WriteString(GetLogLevelColorCode(entry.Level))
}
buf.WriteByte('[')
buf.WriteString(entry.Time.Format("2006-01-02 15:04:05"))
buf.WriteString("] [")
buf.WriteString(strings.ToUpper(entry.Level.String()))
buf.WriteString("]: ")
buf.WriteString(entry.Message)
buf.WriteString(" \n")
if f.EnableColor {
buf.WriteString(ResetSet)
}
ret := append([]byte(nil), buf.Bytes()...) // copy buffer
return ret, nil
}
// 为了不引入新依赖,直接将对应库需要的部分复制过来了,具体可参考 github.com\gookit\color@v1.4.2\color.go
// ResetSet 重置色彩 ansi code
const ResetSet = "\x1b[0m"
const (
// SettingTpl 开始色彩 ansi code
SettingTpl = "\x1b[%sm"
)
var (
colorCodePanic = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String()
colorCodeFatal = fmt.Sprintf(SettingTpl, "1;31") // color.Style{color.Bold, color.Red}.String()
colorCodeError = fmt.Sprintf(SettingTpl, "31") // color.Style{color.Red}.String()
colorCodeWarn = fmt.Sprintf(SettingTpl, "33") // color.Style{color.Yellow}.String()
colorCodeInfo = fmt.Sprintf(SettingTpl, "32") // color.Style{color.Green}.String()
colorCodeDebug = fmt.Sprintf(SettingTpl, "37") // color.Style{color.White}.String()
colorCodeTrace = fmt.Sprintf(SettingTpl, "36") // color.Style{color.Cyan}.String()
)
// GetLogLevelColorCode 获取日志等级对应色彩code
func GetLogLevelColorCode(level logrus.Level) string {
switch level {
case logrus.PanicLevel:
return colorCodePanic
case logrus.FatalLevel:
return colorCodeFatal
case logrus.ErrorLevel:
return colorCodeError
case logrus.WarnLevel:
return colorCodeWarn
case logrus.InfoLevel:
return colorCodeInfo
case logrus.DebugLevel:
return colorCodeDebug
case logrus.TraceLevel:
return colorCodeTrace
default:
return colorCodeInfo
}
}