-
Notifications
You must be signed in to change notification settings - Fork 227
/
access_log.go
107 lines (89 loc) · 3.03 KB
/
access_log.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
package handlers
import (
"io"
"log/slog"
"net/http"
"sync/atomic"
"time"
"github.com/urfave/negroni/v3"
"code.cloudfoundry.org/gorouter/accesslog"
"code.cloudfoundry.org/gorouter/accesslog/schema"
router_http "code.cloudfoundry.org/gorouter/common/http"
log "code.cloudfoundry.org/gorouter/logger"
"code.cloudfoundry.org/gorouter/proxy/utils"
)
type accessLog struct {
accessLogger accesslog.AccessLogger
extraHeadersToLog []string
logAttemptsDetails bool
logger *slog.Logger
}
// NewAccessLog creates a new handler that handles logging requests to the
// access log
func NewAccessLog(
accessLogger accesslog.AccessLogger,
extraHeadersToLog []string,
logAttemptsDetails bool,
logger *slog.Logger,
) negroni.Handler {
return &accessLog{
accessLogger: accessLogger,
extraHeadersToLog: extraHeadersToLog,
logAttemptsDetails: logAttemptsDetails,
logger: logger,
}
}
func (a *accessLog) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
proxyWriter := rw.(utils.ProxyResponseWriter)
alr := &schema.AccessLogRecord{
Request: r,
ExtraHeadersToLog: a.extraHeadersToLog,
LogAttemptsDetails: a.logAttemptsDetails,
}
requestBodyCounter := &countingReadCloser{delegate: r.Body}
r.Body = requestBodyCounter
next(rw, r)
reqInfo, err := ContextRequestInfo(r)
if err != nil {
log.Panic(a.logger, "request-info-err", log.ErrAttr(err))
return
}
reqInfo.FinishedAt = time.Now()
alr.HeadersOverride = reqInfo.BackendReqHeaders
alr.RouteEndpoint = reqInfo.RouteEndpoint
alr.RequestBytesReceived = requestBodyCounter.GetCount()
alr.BodyBytesSent = proxyWriter.Size()
alr.StatusCode = proxyWriter.Status()
alr.RouterError = proxyWriter.Header().Get(router_http.CfRouterError)
alr.FailedAttempts = reqInfo.FailedAttempts
alr.RoundTripSuccessful = reqInfo.RoundTripSuccessful
alr.ReceivedAt = reqInfo.ReceivedAt
alr.AppRequestStartedAt = reqInfo.AppRequestStartedAt
alr.LastFailedAttemptFinishedAt = reqInfo.LastFailedAttemptFinishedAt
alr.DnsStartedAt = reqInfo.DnsStartedAt
alr.DnsFinishedAt = reqInfo.DnsFinishedAt
alr.DialStartedAt = reqInfo.DialStartedAt
alr.DialFinishedAt = reqInfo.DialFinishedAt
alr.TlsHandshakeStartedAt = reqInfo.TlsHandshakeStartedAt
alr.TlsHandshakeFinishedAt = reqInfo.TlsHandshakeFinishedAt
alr.AppRequestFinishedAt = reqInfo.AppRequestFinishedAt
alr.FinishedAt = reqInfo.FinishedAt
a.accessLogger.Log(*alr)
}
type countingReadCloser struct {
delegate io.ReadCloser
count uint64
}
func (crc *countingReadCloser) Read(b []byte) (int, error) {
n, err := crc.delegate.Read(b)
// #nosec G115 - we should never have a negative number of bytes read, so no overflow issues here
atomic.AddUint64(&crc.count, uint64(n))
return n, err
}
func (crc *countingReadCloser) GetCount() int {
// #nosec G115 - we would only have overflow issues here if an http response was more than 9,223,372,036,854,775,807 bytes.
return int(atomic.LoadUint64(&crc.count))
}
func (crc *countingReadCloser) Close() error {
return crc.delegate.Close()
}