内置日志库

  • log包提供基本的日志功能,但是没有提供日志级别(比如debug、warning、error),

简单使用

输出文件

  • 准确的说,只要实现接口io.Writer的类型都可以作为文件的输出。
  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "time"
  7. )
  8. func logPrinta(baseStr string) {
  9. for i := 0; i < 10; i++ {
  10. msg := fmt.Sprintf("%s_%d", baseStr, i)
  11. log.Println(msg)
  12. }
  13. }
  14. func main() {
  15. // 创建文件对象
  16. file, err := os.OpenFile("my.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. // 设置log输出到文件
  21. log.SetOutput(file)
  22. go logPrinta("golang !!")
  23. go logPrinta("中文加爱我的无 !!")
  24. go logPrinta("wdwdw爱的味道无多w")
  25. time.Sleep(1 * time.Hour)
  26. }

自定义的logger

  1. package main
  2. import (
  3. "log"
  4. "os"
  5. )
  6. var (
  7. WarningLogger *log.Logger
  8. InfoLogger *log.Logger
  9. ErrorLogger *log.Logger
  10. )
  11. func init() {
  12. file, err := os.OpenFile("c.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
  13. if err != nil {
  14. log.Fatal(err)
  15. }
  16. InfoLogger = log.New(file, "[INFO]", log.Ldate|log.Ltime|log.Lshortfile)
  17. WarningLogger = log.New(file, "[WARNING]", log.LstdFlags|log.Lshortfile)
  18. ErrorLogger = log.New(file, "[ERROR]", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
  19. }
  20. func main() {
  21. // 创建文件对象
  22. InfoLogger.Println("[常见写法]启动服务....")
  23. InfoLogger.Println("[日期简写]正常上报....")
  24. WarningLogger.Println("[文件长路径]不严重的错误,报个warining....")
  25. ErrorLogger.Println("[微妙时间戳]严重的错误,报个error....")
  26. }

log.flag 和iota讲解

  • itoa枚举依次是 1,2,4,8,16,32
  1. const (
  2. Ldate = 1 << iota // the date in the local time zone: 2009/01/23
  3. Ltime // the time in the local time zone: 01:23:23
  4. Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
  5. Llongfile // full file name and line number: /a/b/c/d.go:23
  6. Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
  7. LUTC // if Ldate or Ltime is set, use UTC rather than the local time zone
  8. Lmsgprefix // move the "prefix" from the beginning of the line to before the message
  9. LstdFlags = Ldate | Ltime // initial values for the standard logger
  10. )
  • 因为可以组由组合标志位,后端进行 &判断
    日志处理 - 图1
    • if l.flag&(Ldate|Ltime|Lmicroseconds) != 0
    • 代表你原来的flag中有Ldate|Ltime|Lmicroseconds
  • logger不能决定字段 输出顺序。 a|b =b|a

github.com/sirupsen/logrus

简单使用

  • WithFields 添加字段 Logrus鼓励通过日志字段进行谨慎的结构化日志记录,而不是冗长的、不可解析的错误消息
  1. package main
  2. import (
  3. log "github.com/sirupsen/logrus"
  4. "os"
  5. )
  6. type s struct {
  7. Name string
  8. Age int
  9. }
  10. func main() {
  11. a := s{
  12. Name: "小乙",
  13. Age: 10,
  14. }
  15. // 设置日志等级
  16. log.SetLevel(log.DebugLevel)
  17. // 设置日志打到那里去
  18. log.SetOutput(os.Stdout)
  19. // 设置filename
  20. log.SetReportCaller(true)
  21. // 设置format text
  22. //log.SetFormatter(&log.TextFormatter{
  23. //
  24. // TimestampFormat: "2006-01-02 15:04:05",
  25. //})
  26. log.SetFormatter(&log.JSONFormatter{
  27. TimestampFormat: "2006-01-02 15:04:05",
  28. })
  29. log.Debug("调试信息")
  30. log.Info("提示信息")
  31. log.Warn("警告信息")
  32. log.Error("错误信息")
  33. log.Infof("[格式化打印结构体:%+v]", a)
  34. log.WithFields(log.Fields{
  35. "user_id": 123,
  36. "ip": "1.1.1.1",
  37. "request_id": "asdwdadmaskmdlasmldkmwqlkdkmakldm",
  38. }).Info("用户登录成功")
  39. }

全局的logger init函数

  • 设置level
  • 设置output
  • 设置format
  • 新建g/log.go
  1. package g
  2. import (
  3. "github.com/sirupsen/logrus"
  4. "os"
  5. )
  6. var Logger = logrus.New()
  7. func InitLogger(level string) {
  8. switch level {
  9. case "info":
  10. Logger.SetLevel(logrus.InfoLevel)
  11. case "debug":
  12. Logger.SetLevel(logrus.DebugLevel)
  13. case "warn":
  14. Logger.SetLevel(logrus.WarnLevel)
  15. }
  16. Logger.SetOutput(os.Stdout)
  17. // 设置filename
  18. Logger.SetReportCaller(true)
  19. // 设置format text
  20. //log.SetFormatter(&log.TextFormatter{
  21. //
  22. // TimestampFormat: "2006-01-02 15:04:05",
  23. //})
  24. Logger.SetFormatter(&logrus.JSONFormatter{
  25. TimestampFormat: "2006-01-02 15:04:05",
  26. })
  27. }
  • 使用
  1. g.InitLogger("info")
  2. g.Logger.Println("测试")

hook使用

hook字段说明

  • Levels 代表在哪几个级别下应用这个hook
  • Fire 代表 执行哪个函数
  1. type Hook interface {
  2. Levels() []Level
  3. Fire(*Entry) error
  4. }

日志发送钉钉样例

钉钉机器人发送群消息

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. logger "github.com/sirupsen/logrus"
  7. "net/http"
  8. )
  9. type dingHook struct {
  10. apiUrl string //钉钉 群机器人token url
  11. levels []logger.Level
  12. atMobiles []string // at谁
  13. appName string // 模块前缀
  14. jsonBodies chan []byte //异步发送内容队列
  15. closeChan chan bool //主进程关闭消息通道
  16. }
  17. //- Levels 代表在哪几个级别下应用这个hook
  18. func (dh *dingHook) Levels() []logger.Level {
  19. return dh.levels
  20. }
  21. //- Fire 代表 执行具体什么逻辑
  22. func (dh *dingHook) Fire(e *logger.Entry) error {
  23. msg, _ := e.String()
  24. dh.DirectSend(msg)
  25. return nil
  26. }
  27. // 同步发送钉钉的函数
  28. func (dh *dingHook) DirectSend(msg string) {
  29. dm := dingMsg{
  30. MsgType: "text",
  31. }
  32. dm.Text.Content = fmt.Sprintf("[日志告警log]\n[app=%s]\n"+
  33. "[日志详情:%s]", dh.appName, msg)
  34. dm.At.AtMobiles = dh.atMobiles
  35. bs, err := json.Marshal(dm)
  36. if err != nil {
  37. logger.Errorf("[消息json.marshal失败][error:%v][msg:%v]", err, msg)
  38. return
  39. }
  40. res, err := http.Post(dh.apiUrl, "application/json", bytes.NewBuffer(bs))
  41. if err != nil {
  42. logger.Errorf("[消息发送失败][error:%v][msg:%v]", err, msg)
  43. return
  44. }
  45. if res != nil && res.StatusCode != 200 {
  46. logger.Errorf("[钉钉返回错误][StatusCode:%v][msg:%v]", res.StatusCode, msg)
  47. return
  48. }
  49. }
  50. // 定义发钉钉信息的字段
  51. /*
  52. {
  53. "at": {
  54. "atMobiles":[
  55. "180xxxxxx"
  56. ],
  57. "atUserIds":[
  58. "user123"
  59. ],
  60. "isAtAll": false
  61. },
  62. "text": {
  63. "content":"我就是我, @XXX 是不一样的烟火"
  64. },
  65. "msgtype":"text"
  66. }
  67. */
  68. type dingMsg struct {
  69. MsgType string `json:"msgtype"`
  70. Text struct {
  71. Content string `json:"content"`
  72. } `json:"text"`
  73. At struct {
  74. AtMobiles []string `json:"atMobiles"`
  75. } `json:"at"`
  76. }
  77. func main() {
  78. dh := &dingHook{
  79. apiUrl: "https://oapi.dingtalk.com/robot/send?access_token=xxxxx",
  80. levels: []logger.Level{logger.WarnLevel, logger.InfoLevel},
  81. atMobiles: []string{"xxxx"},
  82. appName: "live",
  83. jsonBodies: make(chan []byte),
  84. closeChan: make(chan bool),
  85. }
  86. //dh.DirectSend("直接发送一波看看")
  87. level := logger.InfoLevel
  88. logger.SetLevel(level)
  89. // 设置filename
  90. logger.SetReportCaller(true)
  91. logger.SetFormatter(&logger.JSONFormatter{
  92. TimestampFormat: "2006-01-02 15:04:05",
  93. })
  94. // 添加hook
  95. logger.AddHook(dh)
  96. logger.Info("这是我自己hook的logrus")
  97. }

总结如何使用 logrus的hook功能

  1. 要去实现先Fire 和Levels方法 —> 实现那个hook接口
  2. 你的处理逻辑在Fire中
    • 比如发送到redis 、es 、钉钉、logstash
  3. addHook,直接打印日志发送就可以了

原理追踪

  • logrus.Info 对应D:\go_path\pkg\mod\github.com\sirupsen\logrus@v1.8.1\entry.go
  1. func (entry *Entry) Info(args ...interface{}) {
  2. entry.Log(InfoLevel, args...)
  3. }
  • D:\go_path\pkg\mod\github.com\sirupsen\logrus@v1.8.1\entry.go +241
  1. newEntry.fireHooks()
  • D:\go_path\pkg\mod\github.com\sirupsen\logrus@v1.8.1\hooks.go
  1. // Fire all the hooks for the passed level. Used by `entry.log` to fire
  2. // appropriate hooks for a log entry.
  3. func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
  4. for _, hook := range hooks[level] {
  5. if err := hook.Fire(entry); err != nil {
  6. return err
  7. }
  8. }
  9. return nil
  10. }

https://github.com/rifflock/lfshook

  • 简单使用

结合logrotate github.com/lestrrat-go/file-rotatelogs

  • 保留4个文件 rotatelogs.WithRotationCount(4)
  • 切割时间 rotatelogs.WithRotationTime(1*time.Second)
  • 删除时间 rotatelogs.WithMaxAge(2*time.Minute)