-
Notifications
You must be signed in to change notification settings - Fork 50
/
middleware_logger.go
128 lines (103 loc) · 3.33 KB
/
middleware_logger.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package httpserver
import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/justtrackio/gosoline/pkg/encoding/base64"
"github.com/justtrackio/gosoline/pkg/log"
)
func LoggingMiddleware(logger log.Logger, settings LoggingSettings) gin.HandlerFunc {
chLogger := logger.WithChannel("http")
return func(ginCtx *gin.Context) {
start := time.Now()
var requestBody []byte
if settings.RequestBody {
buf, err := io.ReadAll(ginCtx.Request.Body)
if err != nil {
chLogger.Warn("can not read request body: %s", err.Error())
} else {
requestBody = buf
ginCtx.Request.Body = io.NopCloser(bytes.NewBuffer(buf))
}
}
ginCtx.Request = ginCtx.Request.WithContext(log.InitContext(ginCtx.Request.Context()))
ginCtx.Next()
req := ginCtx.Request
ctx := req.Context()
path := req.URL.Path
pathRaw := getPathRaw(ginCtx)
referer := req.Referer()
userAgent := req.UserAgent()
status := ginCtx.Writer.Status()
queryRaw := req.URL.RawQuery
method := ginCtx.Request.Method
requestTimeNano := time.Since(start)
requestTimeSecond := float64(requestTimeNano) / float64(time.Second)
fields := getRequestSizeFields(ginCtx)
fields["bytes"] = ginCtx.Writer.Size()
fields["client_ip"] = ginCtx.ClientIP()
fields["host"] = req.Host
fields["protocol"] = req.Proto
fields["request_method"] = method
fields["request_path"] = path
fields["request_path_raw"] = pathRaw
fields["request_query"] = queryRaw
fields["request_referer"] = referer
fields["request_user_agent"] = userAgent
fields["request_time"] = requestTimeSecond
fields["scheme"] = req.URL.Scheme
fields["status"] = status
if settings.RequestBody && requestBody != nil {
if !settings.RequestBodyBase64 {
fields["request_body"] = string(requestBody)
} else {
fields["request_body"] = string(base64.Encode(requestBody))
}
}
// only log query parameters in full for successful requests to avoid logging them from bad crawlers
if status != http.StatusUnauthorized && status != http.StatusForbidden && status != http.StatusNotFound {
queryParameters := make(map[string]string)
query := req.URL.Query()
for k := range query {
queryParameters[k] = query.Get(k)
}
fields["request_query_parameters"] = queryParameters
}
if requestId := req.Header.Get("X-Request-Id"); requestId != "" {
fields["request_id"] = requestId
}
ctxLogger := chLogger.WithContext(ctx).WithFields(fields)
if len(ginCtx.Errors) == 0 {
ctxLogger.Info("%s %s %s", method, path, req.Proto)
return
}
for _, e := range ginCtx.Errors {
switch e.Type {
case gin.ErrorTypeBind:
if errors.Is(e.Err, io.EOF) || errors.Is(e.Err, io.ErrUnexpectedEOF) {
ctxLogger.Warn("%s %s %s - network error - client has gone away - %v", method, path, req.Proto, e.Err)
} else {
ctxLogger.Warn("%s %s %s - bind error - %v", method, path, req.Proto, e.Err)
}
case gin.ErrorTypeRender:
ctxLogger.Warn("%s %s %s - render error - %v", method, path, req.Proto, e.Err)
default:
ctxLogger.Error("%s %s %s: %w", method, path, req.Proto, e.Err)
}
}
}
}
func getPathRaw(ginCtx *gin.Context) string {
path := ginCtx.Request.URL.Path
for i := range ginCtx.Params {
p := ginCtx.Params[i]
k := fmt.Sprintf(":%s", p.Key)
path = strings.Replace(path, p.Value, k, 1)
}
return path
}