├── example └── server.go ├── gin_zerolog_test.go ├── gin_zerolog.go └── README.md /example/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/dn365/gin-zerolog" 7 | "github.com/gin-gonic/gin" 8 | "github.com/rs/zerolog" 9 | "github.com/rs/zerolog/log" 10 | ) 11 | 12 | func main() { 13 | // isConsole true 14 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 15 | r := gin.New() 16 | r.Use(ginzerolog.Logger("gin")) 17 | 18 | r.GET("/", func(c *gin.Context) { 19 | c.String(200, "hello, gin-zerolog example") 20 | }) 21 | 22 | r.GET("/ping", func(c *gin.Context) { 23 | c.String(200, "pong") 24 | }) 25 | 26 | r.Run(":3000") 27 | } 28 | -------------------------------------------------------------------------------- /gin_zerolog_test.go: -------------------------------------------------------------------------------- 1 | package ginzerolog 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func Test_ErrorLogger(t *testing.T) { 11 | middleware := ErrorLogger() 12 | assert.NotNil(t, middleware, "Can't get ErrorLogger middleware") 13 | } 14 | 15 | func Test_Logger(t *testing.T) { 16 | middleware := Logger("gin") 17 | assert.NotNil(t, middleware, "Can't get get Logger middleware") 18 | } 19 | 20 | func Test_logSwitch(t *testing.T) { 21 | testCdata := &ginHands{ 22 | SerName: "gin", 23 | Path: "/post", 24 | Latency: 1 * time.Second, 25 | Method: "GET", 26 | StatusCode: 200, 27 | ClientIP: "127.0.0.1", 28 | MsgStr: "", 29 | } 30 | logSwitch(testCdata) 31 | assert.NotNil(t, "", "Can't get logSwith middleware") 32 | } 33 | -------------------------------------------------------------------------------- /gin_zerolog.go: -------------------------------------------------------------------------------- 1 | package ginzerolog 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-gonic/gin" 7 | "github.com/rs/zerolog/log" 8 | ) 9 | 10 | type ginHands struct { 11 | SerName string 12 | Path string 13 | Latency time.Duration 14 | Method string 15 | StatusCode int 16 | ClientIP string 17 | MsgStr string 18 | } 19 | 20 | func ErrorLogger() gin.HandlerFunc { 21 | return ErrorLoggerT(gin.ErrorTypeAny) 22 | } 23 | 24 | func ErrorLoggerT(typ gin.ErrorType) gin.HandlerFunc { 25 | return func(c *gin.Context) { 26 | c.Next() 27 | 28 | if !c.Writer.Written() { 29 | json := c.Errors.ByType(typ).JSON() 30 | if json != nil { 31 | c.JSON(-1, json) 32 | } 33 | } 34 | } 35 | } 36 | 37 | func Logger(serName string) gin.HandlerFunc { 38 | return func(c *gin.Context) { 39 | t := time.Now() 40 | // before request 41 | path := c.Request.URL.Path 42 | raw := c.Request.URL.RawQuery 43 | c.Next() 44 | // after request 45 | // latency := time.Since(t) 46 | // clientIP := c.ClientIP() 47 | // method := c.Request.Method 48 | // statusCode := c.Writer.Status() 49 | if raw != "" { 50 | path = path + "?" + raw 51 | } 52 | msg := c.Errors.String() 53 | if msg == "" { 54 | msg = "Request" 55 | } 56 | cData := &ginHands{ 57 | SerName: serName, 58 | Path: path, 59 | Latency: time.Since(t), 60 | Method: c.Request.Method, 61 | StatusCode: c.Writer.Status(), 62 | ClientIP: c.ClientIP(), 63 | MsgStr: msg, 64 | } 65 | 66 | logSwitch(cData) 67 | } 68 | } 69 | 70 | func logSwitch(data *ginHands) { 71 | switch { 72 | case data.StatusCode >= 400 && data.StatusCode < 500: 73 | { 74 | log.Warn().Str("ser_name", data.SerName).Str("method", data.Method).Str("path", data.Path).Dur("resp_time", data.Latency).Int("status", data.StatusCode).Str("client_ip", data.ClientIP).Msg(data.MsgStr) 75 | } 76 | case data.StatusCode >= 500: 77 | { 78 | log.Error().Str("ser_name", data.SerName).Str("method", data.Method).Str("path", data.Path).Dur("resp_time", data.Latency).Int("status", data.StatusCode).Str("client_ip", data.ClientIP).Msg(data.MsgStr) 79 | } 80 | default: 81 | log.Info().Str("ser_name", data.SerName).Str("method", data.Method).Str("path", data.Path).Dur("resp_time", data.Latency).Int("status", data.StatusCode).Str("client_ip", data.ClientIP).Msg(data.MsgStr) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gin-Zerolog 2 | 3 | [Gin](https://github.com/gin-gonic/gin) middleware for Logging with 4 | [zerolog](https://github.com/rs/zerolog). It is meant as drop in 5 | replacement for the default logger used in Gin. 6 | 7 | ## Project Context 8 | 9 | Gin-Zerolog replaces the default logger of [Gin](https://github.com/gin-gonic/gin) to use 10 | [zerolog](https://github.com/rs/zerolog). 11 | 12 | ## Requirements 13 | 14 | Gin-Zerolog uses the following [Go](https://golang.org/) packages as 15 | dependencies: 16 | 17 | - github.com/gin-gonic/gin 18 | - github.com/rs/zerolog 19 | 20 | ## Installation 21 | 22 | Assuming you've installed Go and Gin, run this: 23 | 24 | go get github.com/dn365/gin-zerolog 25 | 26 | ## Usage 27 | ### Example 28 | 29 | Start your webapp to log to STDERR: 30 | 31 | ```go 32 | package main 33 | 34 | import ( 35 | "github.com/dn365/gin-zerolog" 36 | "github.com/gin-gonic/gin" 37 | ) 38 | 39 | func main() { 40 | r := gin.New() 41 | r.Use(ginzerolog.Logger("gin")) 42 | 43 | r.GET("/", func(c *gin.Context) { 44 | c.String(200, "hello, gin-zerolog example") 45 | }) 46 | 47 | r.GET("/ping", func(c *gin.Context) { 48 | c.String(200, "pong") 49 | }) 50 | 51 | r.Run(":3000") 52 | } 53 | ``` 54 | STDERR output of the example above. Lines with prefix "[Gin-Debug]" 55 | are hardcoded output of Gin and will not appear in your logfile: 56 | 57 | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. 58 | - using env: export GIN_MODE=release 59 | - using code: gin.SetMode(gin.ReleaseMode) 60 | 61 | [GIN-debug] GET / --> main.main.func1 (2 handlers) 62 | [GIN-debug] GET /ping --> main.main.func2 (2 handlers) 63 | [GIN-debug] Listening and serving HTTP on :3000 64 | {"time":"2017-12-27T14:22:15+08:00","level":"info","ser_name":"gin","method":"GET","path":"/","resp_time":0.043148,"status":200,"client_ip":"::1","message":"Request"} 65 | {"time":"2017-12-27T14:22:24+08:00","level":"info","ser_name":"gin","method":"GET","path":"/ping","resp_time":0.012409,"status":200,"client_ip":"::1","message":"Request"} 66 | {"time":"2017-12-27T14:22:36+08:00","level":"warn","ser_name":"gin","method":"GET","path":"/hello","resp_time":0.00149,"status":404,"client_ip":"::1","message":"Request"} 67 | 68 | 69 | 70 | ```go 71 | // isConsole true 72 | package main 73 | 74 | import ( 75 | "os" 76 | 77 | "github.com/dn365/gin-zerolog" 78 | "github.com/gin-gonic/gin" 79 | "github.com/rs/zerolog" 80 | "github.com/rs/zerolog/log" 81 | ) 82 | 83 | func main() { 84 | // isConsole true 85 | log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) 86 | r := gin.New() 87 | r.Use(ginzerolog.Logger("gin")) 88 | 89 | r.GET("/", func(c *gin.Context) { 90 | c.String(200, "hello, gin-zerolog example") 91 | }) 92 | 93 | r.GET("/ping", func(c *gin.Context) { 94 | c.String(200, "pong") 95 | }) 96 | 97 | r.Run(":3000") 98 | } 99 | 100 | ``` 101 | 102 | STDERR output of the example above. Lines with prefix "[Gin-Debug]" 103 | are hardcoded output of Gin and will not appear in your logfile: 104 | 105 | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. 106 | - using env: export GIN_MODE=release 107 | - using code: gin.SetMode(gin.ReleaseMode) 108 | 109 | [GIN-debug] GET / --> main.main.func1 (2 handlers) 110 | [GIN-debug] GET /ping --> main.main.func2 (2 handlers) 111 | [GIN-debug] Listening and serving HTTP on :3000 112 | 2017-12-27T14:18:40+08:00 |INFO| Request client_ip=::1 method=GET path=/ resp_time=0.063933 ser_name=gin status=200 113 | 2017-12-27T14:18:45+08:00 |INFO| Request client_ip=::1 method=GET path=/ping resp_time=0.023901 ser_name=gin status=200 114 | 2017-12-27T14:18:49+08:00 |WARN| Request client_ip=::1 method=GET path=/hello resp_time=0.001331 ser_name=gin status=404 115 | --------------------------------------------------------------------------------