Skip to content

Commit

Permalink
some middlewares and prometheus
Browse files Browse the repository at this point in the history
  • Loading branch information
chaowen112 committed Oct 18, 2023
1 parent 5a33f68 commit d9d57e8
Showing 10 changed files with 1,475 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -19,3 +19,7 @@

# Go workspace file
go.work

.vscode
__debug_bin*
.DS_Store
61 changes: 61 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
module github.com/chaowen112/gin-lib

go 1.20

require (
github.com/chaowen112/go-lib v0.0.0-20231015051636-6a161872fa6b
github.com/gin-gonic/gin v1.9.1
github.com/prometheus/client_golang v1.17.0
github.com/sirupsen/logrus v1.9.3
github.com/spf13/viper v1.17.0
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.10.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/net v0.15.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
571 changes: 571 additions & 0 deletions go.sum

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions middleware/Recovery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package middlewares

import (
"fmt"
"net"
"os"
"strings"

log "github.com/sirupsen/logrus"

"github.com/gin-gonic/gin"
)

func Recovery(version string) gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// avoid broken pipe crash here
// if connect is broken, don't response
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
serr := strings.ToLower(se.Error())
if strings.Contains(serr, "broken pipe") ||
strings.Contains(serr, "connection reset by peer") {
brokenPipe = true
}
}
}

errResp := NewError(ErrorPanic, fmt.Sprintf("Panic error: %v", err))

url := c.Request.URL.Path
if c.Request.URL.RawQuery != "" {
url += "?" + c.Request.URL.RawQuery
}

log.WithFields(log.Fields{
"ip": c.ClientIP(),
"method": c.Request.Method,
"url": url,
"body": GetBodyString(c),
"traceback": errResp.Traceback,
}).Error("http_request")

if !brokenPipe {
c.JSON(errResp.ToStatusCode(), &Response{
Version: version,
Success: false,
Result: nil,
Error: errResp,
})
} else {
c.Error(err.(error)) // nolint: errcheck
c.Abort()
}
}
}()

c.Next()
}
}
64 changes: 64 additions & 0 deletions middleware/jsonifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package middlewares

import (
"fmt"
"net/http"

"github.com/gin-gonic/gin"
)

func SetResp(c *gin.Context, value interface{}) {
c.Set(ContextKeyResp, value)
}

func SetErr(c *gin.Context, err *Error) {
c.Set(ContextKeyErr, err)
}

func Default404(c *gin.Context) {
SetErr(
c,
NewError(
ErrorNotFound,
fmt.Sprintf("Could not find route for URL '%v'", c.Request.URL),
),
)
}

func Jsonifier(version string) gin.HandlerFunc {
return func(c *gin.Context) {
// Process request.
c.Next()

resp := &Response{
Version: version,
}

shouldJsonify := false
statusCode := http.StatusOK

// Jsonify the response.
value, exists := c.Get(ContextKeyResp)
if exists {
resp.Success = true
resp.Result = value
resp.Error = nil
shouldJsonify = true
}

value, exists = c.Get(ContextKeyErr)
if exists {
err := value.(*Error)
statusCode = err.ToStatusCode()

resp.Success = false
resp.Result = nil
resp.Error = value
shouldJsonify = true
}

if shouldJsonify {
c.JSON(statusCode, resp)
}
}
}
93 changes: 93 additions & 0 deletions middleware/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package middlewares

import (
"fmt"
"net/http"

json "github.com/chaowen112/go-lib/jsonutils"

"github.com/chaowen112/go-lib/byteutils"
"github.com/chaowen112/go-lib/timeutils"

"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
)

const (
DefaultMaxPayloadSize = 8000
)

func GetBody(c *gin.Context) []byte {
var body []byte
if cb, ok := c.Get(gin.BodyBytesKey); ok {
if cbb, ok := cb.([]byte); ok {
body = cbb
}
}
if body == nil && c.Request.Body != nil {
body, _ = c.GetRawData()
}

return body
}

func GetBodyString(c *gin.Context) string {
return byteutils.ToString(GetBody(c))
}

func GetRequestURL(c *gin.Context) string {
path := c.Request.URL.Path
raw := c.Request.URL.RawQuery

// Log requests.
url := path
if raw != "" {
url += "?" + raw
}
return url
}

func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
// Start timer.
start := timeutils.UnixMilli()
// Process request.
c.Next()

// Stop timer.
end := timeutils.UnixMilli()
elapsed := int((end - start) * 1000)

statusCode := c.Writer.Status()

var resp interface{}

// Log the response of the non GET request.
if c.Request.Method != http.MethodGet {
contextKey := ContextKeyResp
if statusCode >= http.StatusBadRequest {
contextKey = ContextKeyErr
}

value, exists := c.Get(contextKey)
if exists {
resp, _ = json.MarshalToString(value)
}
}

bodyString := GetBodyString(c)
if len(bodyString) > DefaultMaxPayloadSize {
bodyString = fmt.Sprintf("<request payload too long(>%v characters) for log>", DefaultMaxPayloadSize)
}

log.WithFields(log.Fields{
"ip": c.ClientIP(),
"elapsed": elapsed,
"method": c.Request.Method,
"url": GetRequestURL(c),
"body": bodyString,
"status_code": statusCode,
"resp": resp,
}).Info("http_request")
}
}
Loading

0 comments on commit d9d57e8

Please sign in to comment.