导入

  1. import "github.com/sirupsen/logrus"

准备上手用了,上手之前咱们先规划一下,将这个功能设置成一个中间件,比如:logger.go
日志可以记录到 File 中,定义一个 LoggerToFile 方法。
日志可以记录到 MongoDB 中,定义一个 LoggerToMongo 方法。
日志可以记录到 ES 中,定义一个 LoggerToES 方法。
日志可以记录到 MQ 中,定义一个 LoggerToMQ 方法。

这次咱们先实现记录到文件, 实现 LoggerToFile 方法,其他的可以根据自己的需求进行实现。
这个 logger 中间件,创建好了,可以任意在其他项目中进行迁移使用。
废话不多说,直接看代码。

  1. package middleware
  2. import (
  3. "fmt"
  4. "ginDemo/config"
  5. "github.com/gin-gonic/gin"
  6. "github.com/sirupsen/logrus"
  7. "os"
  8. "path"
  9. "time"
  10. )
  11. // 日志记录到文件
  12. func LoggerToFile() gin.HandlerFunc {
  13. logFilePath := config.Log_FILE_PATH
  14. logFileName := config.LOG_FILE_NAME
  15. //日志文件
  16. fileName := path.Join(logFilePath, logFileName)
  17. //写入文件
  18. src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
  19. if err != nil {
  20. fmt.Println("err", err)
  21. }
  22. //实例化
  23. logger := logrus.New()
  24. //设置输出
  25. logger.Out = src
  26. //设置日志级别
  27. logger.SetLevel(logrus.DebugLevel)
  28. //设置日志格式
  29. logger.SetFormatter(&logrus.TextFormatter{})
  30. return func(c *gin.Context) {
  31. // 开始时间
  32. startTime := time.Now()
  33. // 处理请求
  34. c.Next()
  35. // 结束时间
  36. endTime := time.Now()
  37. // 执行时间
  38. latencyTime := endTime.Sub(startTime)
  39. // 请求方式
  40. reqMethod := c.Request.Method
  41. // 请求路由
  42. reqUri := c.Request.RequestURI
  43. // 状态码
  44. statusCode := c.Writer.Status()
  45. // 请求IP
  46. clientIP := c.ClientIP()
  47. // 日志格式
  48. logger.Infof("| %3d | %13v | %15s | %s | %s |",
  49. statusCode,
  50. latencyTime,
  51. clientIP,
  52. reqMethod,
  53. reqUri,
  54. )
  55. }
  56. }
  57. // 日志记录到 MongoDB
  58. func LoggerToMongo() gin.HandlerFunc {
  59. return func(c *gin.Context) {
  60. }
  61. }
  62. // 日志记录到 ES
  63. func LoggerToES() gin.HandlerFunc {
  64. return func(c *gin.Context) {
  65. }
  66. }
  67. // 日志记录到 MQ
  68. func LoggerToMQ() gin.HandlerFunc {
  69. return func(c *gin.Context) {
  70. }
  71. }

日志中间件写好了,怎么调用呢?
只需在 main.go 中新增:

  1. engine := gin.Default() //在这行后新增
  2. engine.Use(middleware.LoggerToFile())

运行一下,看看日志:

  1. time="2019-07-17T22:10:45+08:00" level=info msg="| 200 | 27.698µs | ::1 | GET | /v1/product/add?name=a&price=10 |"
  2. time="2019-07-17T22:10:46+08:00" level=info msg="| 200 | 27.239µs | ::1 | GET | /v1/product/add?name=a&price=10 |"

这个 time="2019-07-17T22:10:45+08:00" ,这个时间格式不是咱们想要的,怎么办?
时间需要格式化一下,修改 logger.SetFormatter

  1. //设置日志格式
  2. logger.SetFormatter(&logrus.TextFormatter{
  3. TimestampFormat:"2006-01-02 15:04:05",
  4. })

执行以下,再看日志:

  1. time="2019-07-17 22:15:57" level=info msg="| 200 | 185.027µs | ::1 | GET | /v1/product/add?name=a&price=10 |"
  2. time="2019-07-17 22:15:58" level=info msg="| 200 | 56.989µs | ::1 | GET | /v1/product/add?name=a&price=10 |"

时间变得正常了。
我不喜欢文本格式,喜欢 JSON 格式,怎么办?

  1. //设置日志格式
  2. logger.SetFormatter(&logrus.JSONFormatter{
  3. TimestampFormat:"2006-01-02 15:04:05",
  4. })

执行以下,再看日志:

  1. {"level":"info","msg":"| 200 | 24.78µs | ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:55"}
  2. {"level":"info","msg":"| 200 | 26.946µs | ::1 | GET | /v1/product/add?name=a\u0026price=10 |","time":"2019-07-17 22:23:56"}

msg 信息太多,不方便看,怎么办?

  1. // 日志格式
  2. logger.WithFields(logrus.Fields{
  3. "status_code" : statusCode,
  4. "latency_time" : latencyTime,
  5. "client_ip" : clientIP,
  6. "req_method" : reqMethod,
  7. "req_uri" : reqUri,
  8. }).Info()

执行以下,再看日志:

  1. {"client_ip":"::1","latency_time":26681,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:54"}
  2. {"client_ip":"::1","latency_time":24315,"level":"info","msg":"","req_method":"GET","req_uri":"/v1/product/add?name=a\u0026price=10","status_code":200,"time":"2019-07-17 22:37:55"}

说明一下:timemsglevel 这些参数是 logrus 自动加上的。
logrus 支持输出文件名和行号吗?
不支持,作者的回复是太耗性能。
不过网上也有人通过 Hook 的方式实现了,选择在生产环境使用的时候,记得做性能测试。
logrus 支持日志分割吗?
不支持,但有办法实现它。
1、可以利用 Linux logrotate,统一由运维进行处理。
2、可以利用 file-rotatelogs 实现。
需要导入包:
github.com/lestrrat-go/file-rotatelogs
github.com/rifflock/lfshook
奉上完整代码:

  1. package middleware
  2. import (
  3. "fmt"
  4. "ginDemo/config"
  5. "github.com/gin-gonic/gin"
  6. rotatelogs "github.com/lestrrat-go/file-rotatelogs"
  7. "github.com/rifflock/lfshook"
  8. "github.com/sirupsen/logrus"
  9. "os"
  10. "path"
  11. "time"
  12. )
  13. // 日志记录到文件
  14. func LoggerToFile() gin.HandlerFunc {
  15. logFilePath := config.Log_FILE_PATH
  16. logFileName := config.LOG_FILE_NAME
  17. // 日志文件
  18. fileName := path.Join(logFilePath, logFileName)
  19. // 写入文件
  20. src, err := os.OpenFile(fileName, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
  21. if err != nil {
  22. fmt.Println("err", err)
  23. }
  24. // 实例化
  25. logger := logrus.New()
  26. // 设置输出
  27. logger.Out = src
  28. // 设置日志级别
  29. logger.SetLevel(logrus.DebugLevel)
  30. // 设置 rotatelogs
  31. logWriter, err := rotatelogs.New(
  32. // 分割后的文件名称
  33. fileName + ".%Y%m%d.log",
  34. // 生成软链,指向最新日志文件
  35. rotatelogs.WithLinkName(fileName),
  36. // 设置最大保存时间(7天)
  37. rotatelogs.WithMaxAge(7*24*time.Hour),
  38. // 设置日志切割时间间隔(1天)
  39. rotatelogs.WithRotationTime(24*time.Hour),
  40. )
  41. writeMap := lfshook.WriterMap{
  42. logrus.InfoLevel: logWriter,
  43. logrus.FatalLevel: logWriter,
  44. logrus.DebugLevel: logWriter,
  45. logrus.WarnLevel: logWriter,
  46. logrus.ErrorLevel: logWriter,
  47. logrus.PanicLevel: logWriter,
  48. }
  49. lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{
  50. TimestampFormat:"2006-01-02 15:04:05",
  51. })
  52. // 新增 Hook
  53. logger.AddHook(lfHook)
  54. return func(c *gin.Context) {
  55. // 开始时间
  56. startTime := time.Now()
  57. // 处理请求
  58. c.Next()
  59. // 结束时间
  60. endTime := time.Now()
  61. // 执行时间
  62. latencyTime := endTime.Sub(startTime)
  63. // 请求方式
  64. reqMethod := c.Request.Method
  65. // 请求路由
  66. reqUri := c.Request.RequestURI
  67. // 状态码
  68. statusCode := c.Writer.Status()
  69. // 请求IP
  70. clientIP := c.ClientIP()
  71. // 日志格式
  72. logger.WithFields(logrus.Fields{
  73. "status_code" : statusCode,
  74. "latency_time" : latencyTime,
  75. "client_ip" : clientIP,
  76. "req_method" : reqMethod,
  77. "req_uri" : reqUri,
  78. }).Info()
  79. }
  80. }
  81. // 日志记录到 MongoDB
  82. func LoggerToMongo() gin.HandlerFunc {
  83. return func(c *gin.Context) {
  84. }
  85. }
  86. // 日志记录到 ES
  87. func LoggerToES() gin.HandlerFunc {
  88. return func(c *gin.Context) {
  89. }
  90. }
  91. // 日志记录到 MQ
  92. func LoggerToMQ() gin.HandlerFunc {
  93. return func(c *gin.Context) {
  94. }
  95. }