Serve Prometheus metrics behind SWWAF_METRICS_TOKEN (closes #23)
check / check (push) Successful in 4m12s
check / check (push) Successful in 4m12s
GET /_smallwebwaf/metrics answers in the Prometheus text format for a request carrying SWWAF_METRICS_TOKEN, 401 without it and 404 while it is unset. Every request under /_smallwebwaf/ but the health check now goes through the checks and is answered where it would be forwarded, 404 for any path but the metrics, so none reaches the app. SWWAF_METRICS_TOP_N bounds the series by country, the rest counted as other. Judgement call: a request answered at smallwebwaf's own endpoints is neither forwarded nor refused in the client's history. Deviation: go.mod and go.sum written by hand from the module proxy and sum.golang.org, as go runs only through make. Deviation: no metrics yet for state files read again after an edit or edits set aside; that work is not merged. Model: opus-5-5
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/golang-lru/v2/simplelru"
|
||||
"sneak.berlin/go/smallwebwaf/internal/metrics"
|
||||
)
|
||||
|
||||
// URL is GeoJS's country endpoint. Asked about several addresses at once,
|
||||
@@ -64,6 +65,9 @@ type Params struct {
|
||||
Now func() time.Time
|
||||
// ProcessLog receives GeoJS's failures.
|
||||
ProcessLog *slog.Logger
|
||||
// Metrics count the requests to GeoJS, those that failed, and the
|
||||
// clients that go without an answer.
|
||||
Metrics *metrics.Metrics
|
||||
}
|
||||
|
||||
// GeoJS looks up clients' countries through GeoJS. At most one request
|
||||
@@ -73,6 +77,7 @@ type GeoJS struct {
|
||||
url string
|
||||
now func() time.Time
|
||||
processLog *slog.Logger
|
||||
metrics *metrics.Metrics
|
||||
// httpClient follows no redirect, so that visitors' addresses go to
|
||||
// GeoJS alone: a redirect is a failure.
|
||||
httpClient *http.Client
|
||||
@@ -121,6 +126,7 @@ func New(params Params) *GeoJS {
|
||||
url: params.URL,
|
||||
now: params.Now,
|
||||
processLog: params.ProcessLog,
|
||||
metrics: params.Metrics,
|
||||
httpClient: &http.Client{
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
@@ -160,6 +166,9 @@ func (g *GeoJS) Country(ctx context.Context, client netip.Prefix) string {
|
||||
defer g.mu.Unlock()
|
||||
|
||||
country, found := g.kept(client)
|
||||
if !found {
|
||||
g.metrics.GeoJSUnanswered.Inc()
|
||||
}
|
||||
|
||||
w, waiting := g.waiting[client]
|
||||
if !found && waiting {
|
||||
@@ -234,6 +243,8 @@ func (g *GeoJS) answerOrWait(
|
||||
g.ask(ctx)
|
||||
|
||||
if w == nil {
|
||||
g.metrics.GeoJSUnanswered.Inc()
|
||||
|
||||
return "", nil // too many clients wait already
|
||||
}
|
||||
|
||||
@@ -243,6 +254,8 @@ func (g *GeoJS) answerOrWait(
|
||||
}
|
||||
|
||||
if w.late {
|
||||
g.metrics.GeoJSUnanswered.Inc()
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -355,6 +368,8 @@ func (g *GeoJS) keep(
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
g.metrics.GeoJSFailures.Inc()
|
||||
|
||||
g.retryDelay = min(max(retryDelayFactor*g.retryDelay, firstRetryDelay),
|
||||
maxRetryDelay)
|
||||
g.retryAt = now.Add(g.retryDelay)
|
||||
@@ -399,6 +414,8 @@ func (g *GeoJS) request(
|
||||
|
||||
req.URL.RawQuery = "ip=" + strings.Join(addrs, ",")
|
||||
|
||||
g.metrics.GeoJSRequests.Inc()
|
||||
|
||||
res, err := g.httpClient.Do(req)
|
||||
if err != nil {
|
||||
// Do's error names the URL, and so the visitors' addresses, which
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/smallwebwaf/internal/lookup"
|
||||
"sneak.berlin/go/smallwebwaf/internal/metrics"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -194,6 +195,7 @@ func TestFailureIsLoggedWithoutTheAddressesAskedAbout(t *testing.T) {
|
||||
URL: lookup.URL,
|
||||
Now: time.Now,
|
||||
ProcessLog: slog.New(slog.NewTextHandler(&log, nil)),
|
||||
Metrics: metrics.New(1),
|
||||
})
|
||||
g.SetTransport(geojs)
|
||||
|
||||
@@ -493,6 +495,7 @@ func start() (*standIn, *testClock, *lookup.GeoJS) {
|
||||
URL: lookup.URL,
|
||||
Now: clock.Now,
|
||||
ProcessLog: slog.New(slog.DiscardHandler),
|
||||
Metrics: metrics.New(1),
|
||||
})
|
||||
g.SetTransport(geojs)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user