-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b6b8ff0
commit a2f4efd
Showing
5 changed files
with
367 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
linters-settings: | ||
govet: | ||
check-shadowing: true | ||
settings: | ||
printf: | ||
funcs: | ||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Infof | ||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Warnf | ||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Errorf | ||
- (github.com/golangci/golangci-lint/pkg/logutils.Log).Fatalf | ||
golint: | ||
min-confidence: 0 | ||
gocyclo: | ||
min-complexity: 10 | ||
maligned: | ||
suggest-new: true | ||
dupl: | ||
threshold: 100 | ||
goconst: | ||
min-len: 2 | ||
min-occurrences: 2 | ||
depguard: | ||
list-type: blacklist | ||
packages: | ||
# logging is allowed only by logutils.Log, logrus | ||
# is allowed to use only in logutils package | ||
- github.com/sirupsen/logrus | ||
misspell: | ||
locale: US | ||
lll: | ||
line-length: 140 | ||
goimports: | ||
local-prefixes: github.com/golangci/golangci-lint | ||
gocritic: | ||
enabled-tags: | ||
- performance | ||
- style | ||
- experimental | ||
disabled-checks: | ||
- wrapperFunc | ||
|
||
linters: | ||
enable-all: true | ||
disable: | ||
- maligned | ||
- prealloc | ||
- gochecknoglobals | ||
|
||
run: | ||
skip-dirs: | ||
- test/testdata_etc | ||
- pkg/golinters/goanalysis/(checker|passes) | ||
|
||
issues: | ||
exclude-rules: | ||
- text: "weak cryptographic primitive" | ||
linters: | ||
- gosec | ||
|
||
# golangci.com configuration | ||
# https://github.com/golangci/golangci/wiki/Configuration | ||
service: | ||
golangci-lint-version: 1.15.x # use the fixed version to not introduce new linters unexpectedly | ||
prepare: | ||
- echo "here I can run custom commands, but no preparation needed for this repo" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,8 @@ | ||
module github.com/gin-ext-lib | ||
module github.com/wajox/gin-ext-lib | ||
|
||
go 1.13 | ||
|
||
require ( | ||
github.com/gin-gonic/gin v1.4.0 | ||
github.com/rs/zerolog v1.16.0 | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g= | ||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= | ||
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ= | ||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= | ||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= | ||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= | ||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= | ||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= | ||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= | ||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= | ||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= | ||
github.com/rs/zerolog v1.16.0 h1:AaELmZdcJHT8m6oZ5py4213cdFK8XGXkB3dFdAQ+P7Q= | ||
github.com/rs/zerolog v1.16.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw= | ||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= | ||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= | ||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= | ||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= | ||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||
golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= | ||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= | ||
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= | ||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= | ||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= | ||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package middleware | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"time" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/rs/zerolog" | ||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
type LoggerConfig struct { | ||
Output io.Writer | ||
SkipPaths []string | ||
} | ||
|
||
func Logger() gin.HandlerFunc { | ||
return LoggerWithConfig(LoggerConfig{}) | ||
} | ||
|
||
func LoggerWithWriter(out io.Writer, notlogged ...string) gin.HandlerFunc { | ||
return LoggerWithConfig(LoggerConfig{ | ||
Output: out, | ||
SkipPaths: notlogged, | ||
}) | ||
} | ||
|
||
func LoggerWithConfig(conf LoggerConfig) gin.HandlerFunc { | ||
if conf.Output == nil { | ||
conf.Output = gin.DefaultWriter | ||
} | ||
|
||
return newLoggerMiddleware(conf) | ||
} | ||
|
||
func newLoggerMiddleware(conf LoggerConfig) gin.HandlerFunc { | ||
skip := computeSkip(conf) | ||
log.Logger = zerolog.New(conf.Output) | ||
|
||
return func(c *gin.Context) { | ||
// Start timer | ||
start := time.Now() | ||
path := c.Request.URL.Path | ||
raw := c.Request.URL.RawQuery | ||
|
||
// Process request | ||
c.Next() | ||
|
||
// Log only when path is not being skipped | ||
if _, ok := skip[path]; ok { | ||
return | ||
} | ||
|
||
log.Info(). | ||
Str("StartTimestamp", fmt.Sprintf("%d", start.Unix())). | ||
Str("ClientIP", c.ClientIP()). | ||
Str("Method", c.Request.Method). | ||
Str("Status", fmt.Sprintf("%d", c.Writer.Status())). | ||
Str("BodySize", fmt.Sprintf("%d", c.Writer.Size())). | ||
Str("ErrorMessage", c.Errors.ByType(gin.ErrorTypePrivate).String()). | ||
Str("Path", path). | ||
Str("Query", raw). | ||
Msg(path) | ||
} | ||
} | ||
|
||
func computeSkip(conf LoggerConfig) map[string]struct{} { | ||
notlogged := conf.SkipPaths | ||
|
||
var skip map[string]struct{} | ||
|
||
if length := len(notlogged); length > 0 { | ||
skip = make(map[string]struct{}, length) | ||
|
||
for _, path := range notlogged { | ||
skip[path] = struct{}{} | ||
} | ||
} | ||
|
||
return skip | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package middleware | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"io/ioutil" | ||
"net" | ||
"net/http" | ||
"net/http/httputil" | ||
"os" | ||
"runtime" | ||
"strings" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/rs/zerolog" | ||
"github.com/rs/zerolog/log" | ||
) | ||
|
||
var ( | ||
dunno = []byte("???") | ||
centerDot = []byte("·") | ||
dot = []byte(".") | ||
slash = []byte("/") | ||
) | ||
|
||
func Recovery() gin.HandlerFunc { | ||
return RecoveryWithWriter(gin.DefaultErrorWriter) | ||
} | ||
|
||
func RecoveryWithWriter(out io.Writer) gin.HandlerFunc { | ||
return func(c *gin.Context) { | ||
defer func() { | ||
if err := recover(); err != nil { | ||
log.Logger = zerolog.New(out) | ||
|
||
brokenPipe := isBrokenPipeError(err) | ||
|
||
if brokenPipe { | ||
renderBrokenPipe(c, err) | ||
|
||
return | ||
} | ||
|
||
renderRequestError(c, err) | ||
} | ||
}() | ||
c.Next() | ||
} | ||
} | ||
|
||
func renderRequestError(c *gin.Context, err interface{}) { | ||
httpRequest := extractRequest(c) | ||
|
||
if gin.IsDebugging() { | ||
log.Error(). | ||
Str("error", fmt.Sprintf("%s", err)). | ||
Str("HTTPRequest", httpRequest). | ||
Str("Headers", extractHeaders(httpRequest)). | ||
Str("Stack", stack(4)). | ||
Msg("Panic recovered") | ||
} else { | ||
log.Error(). | ||
Str("error", fmt.Sprintf("%s", err)). | ||
Str("HTTPRequest", httpRequest). | ||
Str("Stack", stack(4)). | ||
Msg("Panic recovered") | ||
} | ||
|
||
c.AbortWithStatus(http.StatusInternalServerError) | ||
} | ||
|
||
func renderBrokenPipe(c *gin.Context, err interface{}) { | ||
httpRequest := extractRequest(c) | ||
|
||
log.Error(). | ||
Str("error", fmt.Sprintf("%s", err)). | ||
Str("HTTPRequest", httpRequest). | ||
Msg("Broken pipe") | ||
|
||
c.Error(err.(error)) // nolint: errcheck | ||
c.Abort() | ||
} | ||
|
||
func extractRequest(c *gin.Context) string { | ||
httpRequest, _ := httputil.DumpRequest(c.Request, false) | ||
|
||
return string(httpRequest) | ||
} | ||
|
||
func extractHeaders(httpRequest string) string { | ||
headers := strings.Split(httpRequest, "\r\n") | ||
for idx, header := range headers { | ||
current := strings.Split(header, ":") | ||
if current[0] == "Authorization" { | ||
headers[idx] = current[0] + ": *" | ||
} | ||
} | ||
return strings.Join(headers, "\r\n") | ||
} | ||
|
||
func isBrokenPipeError(err interface{}) bool { | ||
if ne, ok := err.(*net.OpError); ok { | ||
if se, ok := ne.Err.(*os.SyscallError); ok { | ||
if strings.Contains(strings.ToLower(se.Error()), "broken pipe") { | ||
return true | ||
} | ||
|
||
if strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { | ||
return true | ||
} | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func stack(skip int) string { | ||
buf := new(bytes.Buffer) | ||
var lines [][]byte | ||
var lastFile string | ||
for i := skip; ; i++ { // Skip the expected number of frames | ||
pc, file, line, ok := runtime.Caller(i) | ||
if !ok { | ||
break | ||
} | ||
// Print this much at least. If we can't find the source, it won't show. | ||
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) | ||
if file != lastFile { | ||
data, err := ioutil.ReadFile(file) | ||
if err != nil { | ||
continue | ||
} | ||
lines = bytes.Split(data, []byte{'\n'}) | ||
lastFile = file | ||
} | ||
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line)) | ||
} | ||
return buf.String() | ||
} | ||
|
||
func source(lines [][]byte, n int) []byte { | ||
n-- // in stack trace, lines are 1-indexed but our array is 0-indexed | ||
if n < 0 || n >= len(lines) { | ||
return dunno | ||
} | ||
return bytes.TrimSpace(lines[n]) | ||
} | ||
|
||
// function returns, if possible, the name of the function containing the PC. | ||
func function(pc uintptr) []byte { | ||
fn := runtime.FuncForPC(pc) | ||
if fn == nil { | ||
return dunno | ||
} | ||
name := []byte(fn.Name()) | ||
|
||
if lastSlash := bytes.LastIndex(name, slash); lastSlash >= 0 { | ||
name = name[lastSlash+1:] | ||
} | ||
if period := bytes.Index(name, dot); period >= 0 { | ||
name = name[period+1:] | ||
} | ||
name = bytes.Replace(name, centerDot, dot, -1) | ||
return name | ||
} |