add RetryAttempts to AccessLog in JSON format
This commit is contained in:
parent
23cdb37165
commit
dae7e7a80a
11 changed files with 130 additions and 90 deletions
|
@ -68,6 +68,8 @@ const (
|
|||
GzipRatio = "GzipRatio"
|
||||
// Overhead is the map key used for the processing time overhead caused by Traefik.
|
||||
Overhead = "Overhead"
|
||||
// RetryAttempts is the map key used for the amount of attempts the request was retried.
|
||||
RetryAttempts = "RetryAttempts"
|
||||
)
|
||||
|
||||
// These are written out in the default case when no config is provided to specify keys of interest.
|
||||
|
@ -110,6 +112,7 @@ func init() {
|
|||
allCoreKeys[GzipRatio] = struct{}{}
|
||||
allCoreKeys[StartLocal] = struct{}{}
|
||||
allCoreKeys[Overhead] = struct{}{}
|
||||
allCoreKeys[RetryAttempts] = struct{}{}
|
||||
}
|
||||
|
||||
// CoreLogData holds the fields computed from the request/response.
|
||||
|
|
|
@ -180,9 +180,11 @@ func usernameIfPresent(theURL *url.URL) string {
|
|||
|
||||
// Logging handler to log frontend name, backend name, and elapsed time
|
||||
func (l *LogHandler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
|
||||
|
||||
core := logDataTable.Core
|
||||
|
||||
if core[RetryAttempts] == nil {
|
||||
core[RetryAttempts] = 0
|
||||
}
|
||||
if crr != nil {
|
||||
core[RequestContentSize] = crr.count
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ var (
|
|||
testMethod = "POST"
|
||||
testReferer = "testReferer"
|
||||
testUserAgent = "testUserAgent"
|
||||
testRetryAttempts = 2
|
||||
)
|
||||
|
||||
func TestLoggerCLF(t *testing.T) {
|
||||
|
@ -91,6 +92,7 @@ func TestLoggerJSON(t *testing.T) {
|
|||
RequestCount,
|
||||
Duration,
|
||||
Overhead,
|
||||
RetryAttempts,
|
||||
"time",
|
||||
"StartLocal",
|
||||
"StartUTC",
|
||||
|
@ -150,6 +152,8 @@ func TestLoggerJSON(t *testing.T) {
|
|||
assertCount++
|
||||
assert.NotZero(t, jsonData[Overhead].(float64))
|
||||
assertCount++
|
||||
assert.Equal(t, float64(testRetryAttempts), jsonData[RetryAttempts].(float64))
|
||||
assertCount++
|
||||
assert.NotEqual(t, "", jsonData["time"].(string))
|
||||
assertCount++
|
||||
assert.NotEqual(t, "", jsonData["StartLocal"].(string))
|
||||
|
@ -275,4 +279,5 @@ func logWriterTestHandlerFunc(rw http.ResponseWriter, r *http.Request) {
|
|||
logDataTable.Core[BackendURL] = testBackendName
|
||||
logDataTable.Core[OriginStatus] = testStatus
|
||||
logDataTable.Core[OriginContentSize] = testContentSize
|
||||
logDataTable.Core[RetryAttempts] = testRetryAttempts
|
||||
}
|
||||
|
|
19
middlewares/accesslog/save_retries.go
Normal file
19
middlewares/accesslog/save_retries.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package accesslog
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// SaveRetries is an implementation of RetryListener that stores RetryAttempts in the LogDataTable.
|
||||
type SaveRetries struct{}
|
||||
|
||||
// Retried implements the RetryListener interface and will be called for each retry that happens.
|
||||
func (s *SaveRetries) Retried(req *http.Request, attempt int) {
|
||||
// it is the request attempt x, but the retry attempt is x-1
|
||||
if attempt > 0 {
|
||||
attempt--
|
||||
}
|
||||
|
||||
table := GetLogDataTable(req)
|
||||
table.Core[RetryAttempts] = attempt
|
||||
}
|
48
middlewares/accesslog/save_retries_test.go
Normal file
48
middlewares/accesslog/save_retries_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package accesslog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSaveRetries(t *testing.T) {
|
||||
tests := []struct {
|
||||
requestAttempt int
|
||||
wantRetryAttemptsInLog int
|
||||
}{
|
||||
{
|
||||
requestAttempt: 0,
|
||||
wantRetryAttemptsInLog: 0,
|
||||
},
|
||||
{
|
||||
requestAttempt: 1,
|
||||
wantRetryAttemptsInLog: 0,
|
||||
},
|
||||
{
|
||||
requestAttempt: 3,
|
||||
wantRetryAttemptsInLog: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
t.Run(fmt.Sprintf("%d retries", test.requestAttempt), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
saveRetries := &SaveRetries{}
|
||||
|
||||
logDataTable := &LogData{Core: make(CoreLogData)}
|
||||
req := httptest.NewRequest(http.MethodGet, "/some/path", nil)
|
||||
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
|
||||
|
||||
saveRetries.Retried(reqWithDataTable, test.requestAttempt)
|
||||
|
||||
if logDataTable.Core[RetryAttempts] != test.wantRetryAttemptsInLog {
|
||||
t.Errorf("got %v in logDataTable, want %v", logDataTable.Core[RetryAttempts], test.wantRetryAttemptsInLog)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -56,6 +56,6 @@ type MetricsRetryListener struct {
|
|||
}
|
||||
|
||||
// Retried tracks the retry in the RequestMetrics implementation.
|
||||
func (m *MetricsRetryListener) Retried(attempt int) {
|
||||
func (m *MetricsRetryListener) Retried(req *http.Request, attempt int) {
|
||||
m.retryMetrics.RetriesCounter().With("backend", m.backendName).Add(1)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
@ -8,10 +10,11 @@ import (
|
|||
)
|
||||
|
||||
func TestMetricsRetryListener(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
retryMetrics := newCollectingRetryMetrics()
|
||||
retryListener := NewMetricsRetryListener(retryMetrics, "backendName")
|
||||
retryListener.Retried(1)
|
||||
retryListener.Retried(2)
|
||||
retryListener.Retried(req, 1)
|
||||
retryListener.Retried(req, 2)
|
||||
|
||||
wantCounterValue := float64(2)
|
||||
if retryMetrics.retryCounter.counterValue != wantCounterValue {
|
||||
|
|
|
@ -60,7 +60,7 @@ func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
attempts++
|
||||
log.Debugf("New attempt %d for request: %v", attempts, r.URL)
|
||||
retry.listener.Retried(attempts)
|
||||
retry.listener.Retried(r, attempts)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,18 @@ func (DefaultNetErrorRecorder) Record(ctx context.Context) {
|
|||
type RetryListener interface {
|
||||
// Retried will be called when a retry happens, with the request attempt passed to it.
|
||||
// For the first retry this will be attempt 2.
|
||||
Retried(attempt int)
|
||||
Retried(req *http.Request, attempt int)
|
||||
}
|
||||
|
||||
// RetryListeners is a convenience type to construct a list of RetryListener and notify
|
||||
// each of them about a retry attempt.
|
||||
type RetryListeners []RetryListener
|
||||
|
||||
// Retried exists to implement the RetryListener interface. It calls Retried on each of its slice entries.
|
||||
func (l RetryListeners) Retried(req *http.Request, attempt int) {
|
||||
for _, retryListener := range l {
|
||||
retryListener.Retried(req, attempt)
|
||||
}
|
||||
}
|
||||
|
||||
// retryResponseRecorder is an implementation of http.ResponseWriter that
|
||||
|
|
|
@ -89,6 +89,21 @@ func TestDefaultNetErrorRecorderNilValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRetryListeners(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
retryListeners := RetryListeners{&countingRetryListener{}, &countingRetryListener{}}
|
||||
|
||||
retryListeners.Retried(req, 1)
|
||||
retryListeners.Retried(req, 1)
|
||||
|
||||
for _, retryListener := range retryListeners {
|
||||
listener := retryListener.(*countingRetryListener)
|
||||
if listener.timesCalled != 2 {
|
||||
t.Errorf("retry listener was called %d times, want %d", listener.timesCalled, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// networkFailingHTTPHandler is an http.Handler implementation you can use to test retries.
|
||||
type networkFailingHTTPHandler struct {
|
||||
netErrorRecorder NetErrorRecorder
|
||||
|
@ -116,6 +131,6 @@ type countingRetryListener struct {
|
|||
timesCalled int
|
||||
}
|
||||
|
||||
func (l *countingRetryListener) Retried(attempt int) {
|
||||
func (l *countingRetryListener) Retried(req *http.Request, attempt int) {
|
||||
l.timesCalled++
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue