├── .catwatch.yaml ├── MAINTAINERS ├── .zappr.yaml ├── .travis.yml ├── .gitignore ├── LICENSE ├── ginglog_test.go ├── ginglog.go └── README.md /.catwatch.yaml: -------------------------------------------------------------------------------- 1 | title: Gin-Glog 2 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Sandor Szücs 2 | Raffaele Di Fazio 3 | Nick Jüttner 4 | -------------------------------------------------------------------------------- /.zappr.yaml: -------------------------------------------------------------------------------- 1 | approvals: 2 | groups: 3 | zalando: 4 | minimum: 2 5 | from: 6 | orgs: 7 | - zalando 8 | X-Zalando-Team: teapot 9 | # one of [code, doc, config, tools, secrets] 10 | X-Zalando-Type: code 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | 4 | language: go 5 | 6 | go: 7 | - 1.8.4 8 | - 1.9 9 | - tip 10 | 11 | before_install: 12 | - go get github.com/mattn/goveralls 13 | 14 | script: 15 | - go test -race -v ./... 16 | - $HOME/gopath/bin/goveralls -service=travis-ci 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Zalando SE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ginglog_test.go: -------------------------------------------------------------------------------- 1 | package ginglog 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | const checkMark = "\u2713" 11 | const ballotX = "\u2717" 12 | 13 | func Test_ErrorLogger(t *testing.T) { 14 | middleware := ErrorLogger() 15 | assert.NotNil(t, middleware, "Can't get ErrorLogger middleware") 16 | } 17 | 18 | func Test_Logger(t *testing.T) { 19 | middleware := Logger(1 * time.Second) 20 | assert.NotNil(t, middleware, "Can't get Logger middleware") 21 | } 22 | 23 | func Test_colorForMethod(t *testing.T) { 24 | colors := map[string]string{ 25 | "GET": blue, 26 | "POST": cyan, 27 | "PUT": yellow, 28 | "HEAD": magenta, 29 | "OPTIONS": white, 30 | "UNKNOWN": reset} 31 | 32 | for method, color := range colors { 33 | expect := colorForMethod(method) 34 | assert.NotNil(t, expect, "Can't get color from method %s %s", 35 | method, ballotX) 36 | t.Logf("Check if color %s%s%s is the right one for this method", 37 | string(expect), method, reset) 38 | if assert.Equal(t, expect, color, "Method %s has NOT the right color %s", 39 | method, ballotX) { 40 | t.Logf("Method %s has the right color %s", method, checkMark) 41 | } 42 | } 43 | } 44 | 45 | func Test_colorForStatus(t *testing.T) { 46 | colors := map[int]string{ 47 | 200: green, 48 | 301: white, 49 | 404: yellow, 50 | 500: red} 51 | 52 | for status, color := range colors { 53 | expect := colorForStatus(status) 54 | assert.NotNil(t, expect, "Can't get color from status %d %s", 55 | status, ballotX) 56 | t.Logf("Check if color %s%d%s is the right one for this status", 57 | string(expect), status, reset) 58 | if assert.Equal(t, expect, color, "Status %d has NOT the right color %s", 59 | status, ballotX) { 60 | t.Logf("Status %d has the right color %s", status, checkMark) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ginglog.go: -------------------------------------------------------------------------------- 1 | // Package ginglog provides a logging middleware to get 2 | // https://github.com/golang/glog as logging library for 3 | // https://github.com/gin-gonic/gin. It can be used as replacement for 4 | // the internal logging middleware 5 | // http://godoc.org/github.com/gin-gonic/gin#Logger. 6 | // 7 | // Example: 8 | // package main 9 | // import ( 10 | // "flag 11 | // "time" 12 | // "github.com/golang/glog" 13 | // "github.com/szuecs/gin-glog" 14 | // "github.com/gin-gonic/gin" 15 | // ) 16 | // func main() { 17 | // flag.Parse() 18 | // router := gin.New() 19 | // router.Use(ginglog.Logger(3 * time.Second)) 20 | // //.. 21 | // router.Use(gin.Recovery()) 22 | // glog.Info("bootstrapped application") 23 | // router.Run(":8080") 24 | // } 25 | // 26 | // Your service will get new command line parameters from 27 | // https://github.com/golang/glog. 28 | package ginglog 29 | 30 | import ( 31 | "time" 32 | 33 | "github.com/gin-gonic/gin" 34 | "github.com/golang/glog" 35 | ) 36 | 37 | func setupLogging(duration time.Duration) { 38 | go func() { 39 | for range time.Tick(duration) { 40 | glog.Flush() 41 | } 42 | }() 43 | } 44 | 45 | var ( 46 | green = string([]byte{27, 91, 57, 55, 59, 52, 50, 109}) 47 | white = string([]byte{27, 91, 57, 48, 59, 52, 55, 109}) 48 | yellow = string([]byte{27, 91, 57, 55, 59, 52, 51, 109}) 49 | red = string([]byte{27, 91, 57, 55, 59, 52, 49, 109}) 50 | blue = string([]byte{27, 91, 57, 55, 59, 52, 52, 109}) 51 | magenta = string([]byte{27, 91, 57, 55, 59, 52, 53, 109}) 52 | cyan = string([]byte{27, 91, 57, 55, 59, 52, 54, 109}) 53 | reset = string([]byte{27, 91, 48, 109}) 54 | ) 55 | 56 | // ErrorLogger returns an ErrorLoggerT with parameter gin.ErrorTypeAny 57 | func ErrorLogger() gin.HandlerFunc { 58 | return ErrorLoggerT(gin.ErrorTypeAny) 59 | } 60 | 61 | // ErrorLoggerT returns an ErrorLoggerT middleware with the given 62 | // type gin.ErrorType. 63 | func ErrorLoggerT(typ gin.ErrorType) gin.HandlerFunc { 64 | return func(c *gin.Context) { 65 | c.Next() 66 | 67 | if !c.Writer.Written() { 68 | json := c.Errors.ByType(typ).JSON() 69 | if json != nil { 70 | c.JSON(-1, json) 71 | } 72 | } 73 | } 74 | } 75 | 76 | // Logger prints a logline for each request and measures the time to 77 | // process for a call. It formats the log entries similar to 78 | // http://godoc.org/github.com/gin-gonic/gin#Logger does. 79 | // 80 | // Example: 81 | // router := gin.New() 82 | // router.Use(ginglog.Logger(3 * time.Second)) 83 | func Logger(duration time.Duration) gin.HandlerFunc { 84 | setupLogging(duration) 85 | return func(c *gin.Context) { 86 | t := time.Now() 87 | 88 | // process request 89 | c.Next() 90 | 91 | latency := time.Since(t) 92 | clientIP := c.ClientIP() 93 | method := c.Request.Method 94 | statusCode := c.Writer.Status() 95 | statusColor := colorForStatus(statusCode) 96 | methodColor := colorForMethod(method) 97 | path := c.Request.URL.Path 98 | 99 | switch { 100 | case statusCode >= 400 && statusCode <= 499: 101 | { 102 | glog.Warningf("[GIN] |%s %3d %s| %12v | %s |%s %s %-7s %s\n%s", 103 | statusColor, statusCode, reset, 104 | latency, 105 | clientIP, 106 | methodColor, reset, method, 107 | path, 108 | c.Errors.String(), 109 | ) 110 | } 111 | case statusCode >= 500: 112 | { 113 | glog.Errorf("[GIN] |%s %3d %s| %12v | %s |%s %s %-7s %s\n%s", 114 | statusColor, statusCode, reset, 115 | latency, 116 | clientIP, 117 | methodColor, reset, method, 118 | path, 119 | c.Errors.String(), 120 | ) 121 | } 122 | default: 123 | glog.V(2).Infof("[GIN] |%s %3d %s| %12v | %s |%s %s %-7s %s\n%s", 124 | statusColor, statusCode, reset, 125 | latency, 126 | clientIP, 127 | methodColor, reset, method, 128 | path, 129 | c.Errors.String(), 130 | ) 131 | } 132 | 133 | } 134 | } 135 | 136 | func colorForStatus(code int) string { 137 | switch { 138 | case code >= 200 && code <= 299: 139 | return green 140 | case code >= 300 && code <= 399: 141 | return white 142 | case code >= 400 && code <= 499: 143 | return yellow 144 | default: 145 | return red 146 | } 147 | } 148 | 149 | func colorForMethod(method string) string { 150 | switch { 151 | case method == "GET": 152 | return blue 153 | case method == "POST": 154 | return cyan 155 | case method == "PUT": 156 | return yellow 157 | case method == "DELETE": 158 | return red 159 | case method == "PATCH": 160 | return green 161 | case method == "HEAD": 162 | return magenta 163 | case method == "OPTIONS": 164 | return white 165 | default: 166 | return reset 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gin-Glog 2 | 3 | [Gin](https://github.com/gin-gonic/gin) middleware for Logging with 4 | [glog](https://github.com/golang/glog). It is meant as drop in 5 | replacement for the default logger used in Gin. 6 | 7 | [![Build Status](https://travis-ci.org/szuecs/gin-glog.svg?branch=master)](https://travis-ci.org/szuecs/gin-glog) 8 | [![Coverage Status](https://coveralls.io/repos/szuecs/gin-glog/badge.svg?branch=master&service=github)](https://coveralls.io/github/szuecs/gin-glog?branch=master) 9 | [![Go Report Card](https://goreportcard.com/badge/szuecs/gin-glog)](https://goreportcard.com/report/szuecs/gin-glog) 10 | [![GoDoc](https://godoc.org/github.com/szuecs/gin-glog?status.svg)](https://godoc.org/github.com/szuecs/gin-glog) 11 | 12 | ## Project Context and Features 13 | 14 | When it comes to choosing a Go framework, there's a lot of confusion 15 | about what to use. The scene is very fragmented, and detailed 16 | comparisons of different frameworks are still somewhat rare. Meantime, 17 | how to handle dependencies and structure projects are big topics in 18 | the Go community. We've liked using Gin for its speed, 19 | accessibility, and usefulness in developing microservice 20 | architectures. In creating Gin-Glog, we wanted to take fuller 21 | advantage of [Gin](https://github.com/gin-gonic/gin)'s capabilities 22 | and help other devs do likewise. 23 | 24 | Gin-Glog replaces the default logger of [Gin](https://github.com/gin-gonic/gin) to use 25 | [Glog](https://github.com/golang/glog). 26 | 27 | ## How Glog is different compared to other loggers 28 | 29 | Glog is an efficient pure Go implementation of leveled logs. If you 30 | use Glog, you do not use blocking calls for writing logs. A goroutine 31 | in the background will flush queued loglines into appropriate 32 | logfiles. It provides logrotation, maintains symlinks to current files 33 | and creates flags to your command line interface. 34 | 35 | ## Requirements 36 | 37 | Gin-Glog uses the following [Go](https://golang.org/) packages as 38 | dependencies: 39 | 40 | - github.com/gin-gonic/gin 41 | - github.com/golang/glog 42 | 43 | ## Installation 44 | 45 | Assuming you've installed Go and Gin, run this: 46 | 47 | go get github.com/szuecs/gin-glog 48 | 49 | ## Usage 50 | ### Example 51 | 52 | Start your webapp to log to STDERR and /tmp: 53 | 54 | % ./webapp -log_dir="/tmp" -alsologtostderr -v=2 55 | 56 | ```go 57 | package main 58 | 59 | import ( 60 | "flag" 61 | "time" 62 | 63 | "github.com/golang/glog" 64 | "github.com/szuecs/gin-glog" 65 | "github.com/gin-gonic/gin" 66 | ) 67 | 68 | func main() { 69 | flag.Parse() 70 | router := gin.New() 71 | router.Use(ginglog.Logger(3 * time.Second)) 72 | //.. 73 | router.Use(gin.Recovery()) 74 | 75 | glog.Warning("warning") 76 | glog.Error("err") 77 | glog.Info("info") 78 | glog.V(2).Infoln("This line will be printed if you use -v=N with N >= 2.") 79 | 80 | router.Run(":8080") 81 | } 82 | ``` 83 | 84 | STDERR output of the example above. Lines with prefix "[Gin-Debug]" 85 | are hardcoded output of Gin and will not appear in your logfile: 86 | 87 | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. 88 | - using env: export GIN_MODE=release 89 | - using code: gin.SetMode(gin.ReleaseMode) 90 | 91 | W0306 16:37:12.836001 367 main.go:18] warning 92 | E0306 16:37:12.836335 367 main.go:19] err 93 | I0306 16:37:12.836402 367 main.go:20] info 94 | I0306 16:26:33.901278 32538 main.go:19] This line will be printed if you use -v=N with N >= 2. 95 | [GIN-debug] Listening and serving HTTP on :8080 96 | 97 | 98 | ## Synopsis 99 | 100 | Glog will add the following flags to your binary: 101 | 102 | -alsologtostderr 103 | log to standard error as well as files 104 | -log_backtrace_at value 105 | when logging hits line file:N, emit a stack trace (default :0) 106 | -log_dir string 107 | If non-empty, write log files in this directory 108 | -logtostderr 109 | log to standard error instead of files 110 | -stderrthreshold value 111 | logs at or above this threshold go to stderr 112 | -v value 113 | log level for V logs 114 | -vmodule value 115 | comma-separated list of pattern=N settings for file-filtered logging 116 | 117 | 118 | ## Contributing/TODO 119 | 120 | We welcome contributions from the community; just submit a pull 121 | request. To help you get started, here are some items that we'd love 122 | help with: 123 | 124 | - Remove hardcoded logs in [Gin](https://github.com/gin-gonic/gin) 125 | - the code base 126 | 127 | Please use github issues as starting point for contributions, new 128 | ideas or bugreports. 129 | 130 | ## Contact 131 | 132 | * E-Mail: team-techmonkeys@zalando.de 133 | * IRC on freenode: #zalando-techmonkeys 134 | 135 | ## Contributors 136 | 137 | Thanks to: 138 | 139 | - <your name> 140 | 141 | ## License 142 | 143 | See [LICENSE](LICENSE) file. 144 | --------------------------------------------------------------------------------