• golang——工程组件logrus日志记录框架(结构化记录,支持文件切割,hook)


    logrus

    介绍一个golang 日志框架logrus

    • 支持文本与JSON数据格式
    • 支持结构化记录
    • 支持hook

    文档介绍

    logrus文档

    std

    官方案例介绍了如何配置std打印

    package main
    
    import (
      "os"
      log "github.com/sirupsen/logrus"
    )
    
    func init() {
      // Log as JSON instead of the default ASCII formatter.
      log.SetFormatter(&log.JSONFormatter{})
    
      // Output to stdout instead of the default stderr
      // Can be any io.Writer, see below for File example
      log.SetOutput(os.Stdout)
    
      // Only log the warning severity or above.
      log.SetLevel(log.WarnLevel)
    }
    
    func main() {
      log.WithFields(log.Fields{
        "animal": "walrus",
        "size":   10,
      }).Info("A group of walrus emerges from the ocean")
    
      log.WithFields(log.Fields{
        "omg":    true,
        "number": 122,
      }).Warn("The group's number increased tremendously!")
    
      log.WithFields(log.Fields{
        "omg":    true,
        "number": 100,
      }).Fatal("The ice breaks!")
    
      // A common pattern is to re-use fields between logging statements by re-using
      // the logrus.Entry returned from WithFields()
      contextLogger := log.WithFields(log.Fields{
        "common": "this is a common field",
        "other": "I also should be logged always",
      })
    
      contextLogger.Info("I'll be logged with common and other field")
      contextLogger.Info("Me too")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    file

    仔细观察,如果要输出到文件,只需要在logrus.SetOutput设置上对应的*os.File即可

    文件切割

    文件日志记录一般都是要切割的,不然文件太大不方便查看

    采用的库

    https://github.com/lestrrat-go/file-rotatelogs
    
    • 1
    案例

    log.go

    package logs
    
    import (
        "fmt"
        "github.com/sirupsen/logrus"
        "log"
    )
    
    type Log struct {
        *logrus.Entry
        LogWriter
    }
    
    
    func (l *Log) Flush() {
        l.LogWriter.Flush()
    }
    
    type LogConf struct {
        Level       logrus.Level
        AdapterName string
        Hook        logrus.Hook
    }
    
    
    
    func InitLog(conf LogConf) *Log {
        adapterName := "std"
        if conf.AdapterName != "" {
            adapterName = conf.AdapterName
        }
        writer, ok := writerAdapter[adapterName]
        if !ok {
            adapterName = "std"
            writer, _ = writerAdapter[adapterName]
        }
        fmt.Println("adapterName:" + adapterName)
        log :=&Log{
            logrus.NewEntry(logrus.New()),
            writer(),  // 初始化writer
        }
        // Log as JSON instead of the default ASCII formatter.
        log.Logger.SetFormatter(&logrus.JSONFormatter{})
    
        // Output to stdout instead of the default stderr
        // Can be any io.Writer, see below for File example
        log.Logger.SetOutput(log.LogWriter)
    
        // Only log the warning severity or above.
        if conf.Level != 0 {
            log.Logger.SetLevel(conf.Level)
        } else {
            log.Logger.SetLevel(logrus.InfoLevel)
        }
        if conf.Hook != nil {
            log.Logger.AddHook(conf.Hook)
        }
        // 设置日志打印位置
        log.Logger.SetReportCaller(true)
    
        return log
    }
    
    type TestHook struct {
    
    }
    
    func (hook *TestHook) Levels() []logrus.Level {
        return []logrus.Level{logrus.InfoLevel}
    }
    func (hook *TestHook)  Fire(entry *logrus.Entry) error {
        log.Print("hook: %+v", entry)
        return nil
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    file_rotate.go

    package logs
    
    import (
        rotatelogs "github.com/lestrrat-go/file-rotatelogs"
        "log"
        "time"
    )
    
    type fileRotateWriter struct {
        *rotatelogs.RotateLogs
    }
    
    func (frw *fileRotateWriter) Flush() {
        frw.Close()
    }
    
    func newFileRotateWriter() LogWriter {
        writer, err := getRotateLogs()
        if err != nil {
            log.Fatal(err)
        }
    
        return &fileRotateWriter{
            writer,
        }
    }
    
    func getRotateLogs() (*rotatelogs.RotateLogs, error) {
        path := LOGPATH
        logf, err := rotatelogs.New(
            path + ".%Y%m%d%H%M", // 指定文件格式
            //rotatelogs.WithLinkName(path),           // 将最新文件软链到path,windows环境不支持
            rotatelogs.WithMaxAge(time.Second*1800),     // 日志最长保存时长
            rotatelogs.WithRotationTime(time.Second*60), // 日志切分时间间隔
        )
        return logf, err
    }
    
    func init() {
        RegisterInitWriterFunc("file-rotate", newFileRotateWriter)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    main.go

    package main
    
    import (
        "logrus-practice/logs"
        "github.com/sirupsen/logrus"
    )
    
    
    
    func main() {
        conf := logs.LogConf{
            Level: logrus.InfoLevel,
            AdapterName: "file-rotate",
            Hook: &logs.TestHook{},
        }
        log := logs.InitLog(conf)
        log.WithFields(logrus.Fields{
            "animal": "walrus",
            "size":   10,
        }).Info("A group of walrus emerges from the ocean")
    
        log.WithFields(logrus.Fields{
            "omg":    true,
            "number": 122,
        }).Warn("The group's number increased tremendously!")
    
        log.WithFields(logrus.Fields{
            "omg":    true,
            "number": 100,
        }).Fatal("The ice breaks!")
    
        // A common pattern is to re-use fields between logging statements by re-using
        // the logrus.Entry returned from WithFields()
        contextLogger := log.WithFields(logrus.Fields{
            "common": "this is a common field",
            "other": "I also should be logged always",
        })
    
        contextLogger.Info("I'll be logged with common and other field")
        contextLogger.Info("Me too")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    hook机制

    框架支持,在不同level的日志打印中进行hook。我们可以根据传入的*logrus.Entry里用户自定义的field进行不同的业务处理

    上面代码已经含有hook的使用

    type TestHook struct {
    
    }
    
    func (hook *TestHook) Levels() []logrus.Level {
        return []logrus.Level{logrus.InfoLevel}
    }
    func (hook *TestHook)  Fire(entry *logrus.Entry) error {
        // 可以根据data字段里的field 去做操作
        log.Print("hook: %+v", entry)
        return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    生成环境中,很多公司都会采用ELK那一套,把日志转发录入到ES中,在kibana按条件搜索

  • 相关阅读:
    outsystems合集系列(二)
    【多卡训练报错】:The server socket has failed to listen on any local network address.
    java学习(常用类)
    【Aseprite】像素画背景制作
    【力扣周赛】第 362 场周赛(⭐差分&匹配&状态压缩DP&矩阵快速幂优化DP&KMP)
    rust学习-any中的downcast和downcast_ref
    快速计算发票金额
    关于推特社交机器人数据集(来自上一篇论文)
    22年牛客最新大厂Java岗面试题大合集(含答案)
    读取XML的几种方式
  • 原文地址:https://blog.csdn.net/qq_43058348/article/details/133959223