-
Notifications
You must be signed in to change notification settings - Fork 20
Expand file tree
/
Copy pathmiddleware.go
More file actions
127 lines (106 loc) · 3.24 KB
/
middleware.go
File metadata and controls
127 lines (106 loc) · 3.24 KB
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
package middleware
import (
"bytes"
"encoding/json"
"io"
"net/http"
"time"
"github.com/doganarif/govisual/internal/model"
"github.com/doganarif/govisual/internal/store"
"github.com/doganarif/govisual/internal/utils"
)
// PathMatcher defines an interface for checking if a path should be ignored
type PathMatcher interface {
ShouldIgnorePath(path string) bool
}
// responseWriter is a wrapper for http.ResponseWriter that captures the status code and response
type responseWriter struct {
http.ResponseWriter
statusCode int
buffer *bytes.Buffer
}
// WriteHeader captures the status code
func (w *responseWriter) WriteHeader(code int) {
w.statusCode = code
w.ResponseWriter.WriteHeader(code)
}
// Write captures the response body
func (w *responseWriter) Write(b []byte) (int, error) {
// Write to the buffer
if w.buffer != nil {
w.buffer.Write(b)
}
return w.ResponseWriter.Write(b)
}
// Wrap wraps an http.Handler with the request visualization middleware
func Wrap(handler http.Handler, store store.Store, logRequestBody, logResponseBody bool, logToConsole bool, pathMatcher PathMatcher) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check if the path should be ignored
if pathMatcher != nil && pathMatcher.ShouldIgnorePath(r.URL.Path) {
handler.ServeHTTP(w, r)
return
}
// Create a new request log
reqLog := model.NewRequestLog(r)
// Capture request body if enabled
if logRequestBody && r.Body != nil {
// Read the body
bodyBytes, _ := io.ReadAll(r.Body)
r.Body.Close()
// Store the body in the log
reqLog.RequestBody = string(bodyBytes)
// Create a new body for the request
r.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
// Create response writer wrapper
var resWriter *responseWriter
if logResponseBody {
resWriter = &responseWriter{
ResponseWriter: w,
statusCode: 200, // Default status code
buffer: &bytes.Buffer{},
}
} else {
resWriter = &responseWriter{
ResponseWriter: w,
statusCode: 200, // Default status code
}
}
// Record start time
start := time.Now()
// Call the handler
handler.ServeHTTP(resWriter, r)
// Calculate duration
duration := time.Since(start)
reqLog.Duration = duration.Milliseconds()
// Capture response info
reqLog.StatusCode = resWriter.statusCode
// Extract middleware information from context
if middlewareValue := r.Context().Value("middleware"); middlewareValue != nil {
if middlewareInfo, ok := middlewareValue.(map[string]interface{}); ok {
if stack, ok := middlewareInfo["stack"].([]map[string]interface{}); ok {
reqLog.MiddlewareTrace = stack
}
}
}
// Extract route trace information
if routeValue := r.Context().Value("route"); routeValue != nil {
if routeStr, ok := routeValue.(string); ok {
var routeInfo map[string]interface{}
if err := json.Unmarshal([]byte(routeStr), &routeInfo); err == nil {
reqLog.RouteTrace = routeInfo
}
}
}
// Capture response body if enabled
if logResponseBody && resWriter.buffer != nil {
reqLog.ResponseBody = resWriter.buffer.String()
}
// Log to console if enabled
if logToConsole {
utils.LogRequest(reqLog)
}
// Store the request log
store.Add(reqLog)
})
}