github.com/Mrs4s/go-cqhttp@v1.2.0/global/log_hook.go (about)

     1  package global
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/mattn/go-colorable"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // LocalHook logrus本地钩子
    17  type LocalHook struct {
    18  	lock      *sync.Mutex
    19  	levels    []logrus.Level   // hook级别
    20  	formatter logrus.Formatter // 格式
    21  	path      string           // 写入path
    22  	writer    io.Writer        // io
    23  }
    24  
    25  // Levels ref: logrus/hooks.go impl Hook interface
    26  func (hook *LocalHook) Levels() []logrus.Level {
    27  	if len(hook.levels) == 0 {
    28  		return logrus.AllLevels
    29  	}
    30  	return hook.levels
    31  }
    32  
    33  func (hook *LocalHook) ioWrite(entry *logrus.Entry) error {
    34  	log, err := hook.formatter.Format(entry)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	_, err = hook.writer.Write(log)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	return nil
    44  }
    45  
    46  func (hook *LocalHook) pathWrite(entry *logrus.Entry) error {
    47  	dir := filepath.Dir(hook.path)
    48  	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
    49  		return err
    50  	}
    51  
    52  	fd, err := os.OpenFile(hook.path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
    53  	if err != nil {
    54  		return err
    55  	}
    56  	defer fd.Close()
    57  
    58  	log, err := hook.formatter.Format(entry)
    59  	if err != nil {
    60  		return err
    61  	}
    62  
    63  	_, err = fd.Write(log)
    64  	return err
    65  }
    66  
    67  // Fire ref: logrus/hooks.go impl Hook interface
    68  func (hook *LocalHook) Fire(entry *logrus.Entry) error {
    69  	hook.lock.Lock()
    70  	defer hook.lock.Unlock()
    71  
    72  	if hook.writer != nil {
    73  		return hook.ioWrite(entry)
    74  	}
    75  
    76  	if hook.path != "" {
    77  		return hook.pathWrite(entry)
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  // SetFormatter 设置日志格式
    84  func (hook *LocalHook) SetFormatter(consoleFormatter, fileFormatter logrus.Formatter) {
    85  	hook.lock.Lock()
    86  	defer hook.lock.Unlock()
    87  
    88  	// 支持处理windows平台的console色彩
    89  	logrus.SetOutput(colorable.NewColorableStdout())
    90  	// 用于在console写出
    91  	logrus.SetFormatter(consoleFormatter)
    92  	// 用于写入文件
    93  	hook.formatter = fileFormatter
    94  }
    95  
    96  // SetWriter 设置Writer
    97  func (hook *LocalHook) SetWriter(writer io.Writer) {
    98  	hook.lock.Lock()
    99  	defer hook.lock.Unlock()
   100  	hook.writer = writer
   101  }
   102  
   103  // SetPath 设置日志写入路径
   104  func (hook *LocalHook) SetPath(path string) {
   105  	hook.lock.Lock()
   106  	defer hook.lock.Unlock()
   107  	hook.path = path
   108  }
   109  
   110  // NewLocalHook 初始化本地日志钩子实现
   111  func NewLocalHook(args any, consoleFormatter, fileFormatter logrus.Formatter, levels ...logrus.Level) *LocalHook {
   112  	hook := &LocalHook{
   113  		lock: new(sync.Mutex),
   114  	}
   115  	hook.SetFormatter(consoleFormatter, fileFormatter)
   116  	hook.levels = append(hook.levels, levels...)
   117  
   118  	switch arg := args.(type) {
   119  	case string:
   120  		hook.SetPath(arg)
   121  	case io.Writer:
   122  		hook.SetWriter(arg)
   123  	default:
   124  		panic(fmt.Sprintf("unsupported type: %v", reflect.TypeOf(args)))
   125  	}
   126  
   127  	return hook
   128  }
   129  
   130  // GetLogLevel 获取日志等级
   131  //
   132  // 可能的值有
   133  //
   134  // "trace","debug","info","warn","warn","error"
   135  func GetLogLevel(level string) []logrus.Level {
   136  	switch level {
   137  	case "trace":
   138  		return []logrus.Level{
   139  			logrus.TraceLevel, logrus.DebugLevel,
   140  			logrus.InfoLevel, logrus.WarnLevel, logrus.ErrorLevel,
   141  			logrus.FatalLevel, logrus.PanicLevel,
   142  		}
   143  	case "debug":
   144  		return []logrus.Level{
   145  			logrus.DebugLevel, logrus.InfoLevel,
   146  			logrus.WarnLevel, logrus.ErrorLevel,
   147  			logrus.FatalLevel, logrus.PanicLevel,
   148  		}
   149  	case "info":
   150  		return []logrus.Level{
   151  			logrus.InfoLevel, logrus.WarnLevel,
   152  			logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel,
   153  		}
   154  	case "warn":
   155  		return []logrus.Level{
   156  			logrus.WarnLevel, logrus.ErrorLevel,
   157  			logrus.FatalLevel, logrus.PanicLevel,
   158  		}
   159  	case "error":
   160  		return []logrus.Level{
   161  			logrus.ErrorLevel, logrus.FatalLevel,
   162  			logrus.PanicLevel,
   163  		}
   164  	default:
   165  		return []logrus.Level{
   166  			logrus.InfoLevel, logrus.WarnLevel,
   167  			logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel,
   168  		}
   169  	}
   170  }
   171  
   172  // LogFormat specialize for go-cqhttp
   173  type LogFormat struct {
   174  	EnableColor bool
   175  }
   176  
   177  // Format implements logrus.Formatter
   178  func (f LogFormat) Format(entry *logrus.Entry) ([]byte, error) {
   179  	buf := NewBuffer()
   180  	defer PutBuffer(buf)
   181  
   182  	if f.EnableColor {
   183  		buf.WriteString(GetLogLevelColorCode(entry.Level))
   184  	}
   185  
   186  	buf.WriteByte('[')
   187  	buf.WriteString(entry.Time.Format("2006-01-02 15:04:05"))
   188  	buf.WriteString("] [")
   189  	buf.WriteString(strings.ToUpper(entry.Level.String()))
   190  	buf.WriteString("]: ")
   191  	buf.WriteString(entry.Message)
   192  	buf.WriteString(" \n")
   193  
   194  	if f.EnableColor {
   195  		buf.WriteString(colorReset)
   196  	}
   197  
   198  	ret := make([]byte, len(buf.Bytes()))
   199  	copy(ret, buf.Bytes()) // copy buffer
   200  	return ret, nil
   201  }
   202  
   203  const (
   204  	colorCodePanic = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
   205  	colorCodeFatal = "\x1b[1;31m" // color.Style{color.Bold, color.Red}.String()
   206  	colorCodeError = "\x1b[31m"   // color.Style{color.Red}.String()
   207  	colorCodeWarn  = "\x1b[33m"   // color.Style{color.Yellow}.String()
   208  	colorCodeInfo  = "\x1b[37m"   // color.Style{color.White}.String()
   209  	colorCodeDebug = "\x1b[32m"   // color.Style{color.Green}.String()
   210  	colorCodeTrace = "\x1b[36m"   // color.Style{color.Cyan}.String()
   211  	colorReset     = "\x1b[0m"
   212  )
   213  
   214  // GetLogLevelColorCode 获取日志等级对应色彩code
   215  func GetLogLevelColorCode(level logrus.Level) string {
   216  	switch level {
   217  	case logrus.PanicLevel:
   218  		return colorCodePanic
   219  	case logrus.FatalLevel:
   220  		return colorCodeFatal
   221  	case logrus.ErrorLevel:
   222  		return colorCodeError
   223  	case logrus.WarnLevel:
   224  		return colorCodeWarn
   225  	case logrus.InfoLevel:
   226  		return colorCodeInfo
   227  	case logrus.DebugLevel:
   228  		return colorCodeDebug
   229  	case logrus.TraceLevel:
   230  		return colorCodeTrace
   231  
   232  	default:
   233  		return colorCodeInfo
   234  	}
   235  }