forked from ory/oathkeeper
-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleware.go
100 lines (84 loc) · 2.53 KB
/
middleware.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
package metrics
import (
"net/http"
"strings"
"sync"
"time"
"github.com/urfave/negroni"
)
type timer interface {
Now() time.Time
Since(time.Time) time.Duration
}
type realClock struct{}
func (rc *realClock) Now() time.Time {
return time.Now()
}
func (rc *realClock) Since(t time.Time) time.Duration {
return time.Since(t)
}
// Middleware is a middleware handler that logs the request as it goes in and the response as it goes out.
type Middleware struct {
// Name is the name of the application as recorded in latency metrics
Name string
// Prometheus repository
Prometheus *PrometheusRepository
clock timer
// Silence metrics for specific URL paths
// it is protected by the mutex
mutex sync.RWMutex
silencePaths map[string]bool
collapsePaths bool
}
// NewMiddleware returns a new *Middleware, yay!
func NewMiddleware(prom *PrometheusRepository, name string) *Middleware {
return &Middleware{
Name: name,
Prometheus: prom,
clock: &realClock{},
silencePaths: map[string]bool{},
collapsePaths: true,
}
}
// ExcludePaths adds new URL paths to be ignored during logging. The URL u is parsed, hence the returned error
func (m *Middleware) ExcludePaths(paths ...string) *Middleware {
for _, path := range paths {
m.mutex.Lock()
m.silencePaths[path] = true
m.mutex.Unlock()
}
return m
}
// CollapsePaths if set to true, forces the value of the "request" label
// of the prometheus request metrics to be collapsed to the first context path segment only.
// eg. (when set to true):
// - /decisions/service/my-service -> /decisions
// - /decisions -> /decisions
func (m *Middleware) CollapsePaths(flag bool) *Middleware {
m.mutex.Lock()
m.collapsePaths = flag
m.mutex.Unlock()
return m
}
func (m *Middleware) getFirstPathSegment(requestURI string) string {
// Will split /my/example/uri in []string{"", "my", "example/uri"}
uriSegments := strings.SplitN(requestURI, "/", 3)
if len(uriSegments) > 1 {
return "/" + uriSegments[1]
}
return "/"
}
func (m *Middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
start := m.clock.Now()
next(rw, r)
latency := m.clock.Since(start)
res := rw.(negroni.ResponseWriter)
if _, silent := m.silencePaths[r.URL.Path]; !silent {
requestURI := r.RequestURI
if m.collapsePaths {
requestURI = m.getFirstPathSegment(requestURI)
}
m.Prometheus.RequestDurationObserve(m.Name, requestURI, r.Method, res.Status())(float64(latency.Seconds()))
m.Prometheus.UpdateRequest(m.Name, requestURI, r.Method, res.Status())
}
}