导入

  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"}

说明一下:time、msg、level 这些参数是 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. }