feat: add response time histogram metric
Some checks are pending
Cadoles/bouncer/pipeline/pr-master Build started...

This commit is contained in:
2025-08-25 10:40:51 +02:00
parent bc8ad02c13
commit a9a3bdbee7
3 changed files with 63 additions and 15 deletions

View File

@ -24,6 +24,31 @@ Chaque layer associé à un proxy peut également ses propres métriques spécif
bouncer_proxy_director_proxy_requests_total{proxy="cadoles"} 64
```
#### `bouncer_proxy_director_proxy_responses_duration_seconds{proxy=<proxyName>}`
- **Type:** `histogram`
- **Description**: Histogramme du temps de traitement du cycle requête/réponse par proxy
- **Exemple**
```
# HELP bouncer_proxy_director_proxy_responses_duration_seconds Bouncer proxy responses duration
# TYPE bouncer_proxy_director_proxy_responses_duration_seconds histogram
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.005"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.01"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.025"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.05"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.1"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.25"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="0.5"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="1"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="2.5"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="5"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="10"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_bucket{proxy="dummy",le="+Inf"} 13
bouncer_proxy_director_proxy_responses_duration_seconds_sum{proxy="dummy"} 0.009556884
bouncer_proxy_director_proxy_responses_duration_seconds_count{proxy="dummy"} 13
```
### Serveur d'administration
_Pas de métrique supplémentaire._

View File

@ -4,6 +4,7 @@ import (
"context"
"net/http"
"sort"
"time"
"forge.cadoles.com/Cadoles/go-proxy"
"forge.cadoles.com/Cadoles/go-proxy/wildcard"
@ -28,12 +29,12 @@ type Director struct {
const proxiesCacheKey = "proxies"
func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
func (d *Director) rewriteRequest(r *http.Request) (*http.Request, []store.ProxyName, error) {
ctx := r.Context()
proxies, _, err := d.cachedProxies.Get(ctx, proxiesCacheKey)
if err != nil {
return r, errors.WithStack(err)
return r, nil, errors.WithStack(err)
}
url := getRequestURL(r)
@ -42,12 +43,16 @@ func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
layers := make([]*store.Layer, 0)
matchingProxies := make([]store.ProxyName, 0)
for _, p := range proxies {
for _, from := range p.From {
if matches := wildcard.Match(url.String(), from); !matches {
continue
}
matchingProxies = append(matchingProxies, p.Name)
proxyCtx := logger.With(ctx,
logger.F("proxy", p.Name),
logger.F("host", r.Host),
@ -58,7 +63,7 @@ func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
proxyLayers, _, err := d.cachedLayers.Get(proxyCtx, string(p.Name))
if err != nil {
return r, errors.WithStack(err)
return r, nil, errors.WithStack(err)
}
layers = append(layers, proxyLayers...)
@ -69,7 +74,7 @@ func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
toURL, err := url.Parse(p.To)
if err != nil {
return r, errors.WithStack(err)
return r, nil, errors.WithStack(err)
}
r.URL.Host = toURL.Host
@ -87,14 +92,14 @@ func (d *Director) rewriteRequest(r *http.Request) (*http.Request, error) {
})
}
return r, nil
return r, matchingProxies, nil
}
}
ctx = withLayers(ctx, layers)
r = r.WithContext(ctx)
return r, nil
return r, matchingProxies, nil
}
func (d *Director) getProxies(ctx context.Context, key string) ([]*store.Proxy, error) {
@ -215,17 +220,26 @@ func (d *Director) ResponseTransformer() proxy.ResponseTransformer {
func (d *Director) Middleware() proxy.Middleware {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
sentry.ConfigureScope(func(scope *sentry.Scope) {
ctx := withHandleError(r.Context(), d.handleError)
ctx = withSentryScope(ctx, scope)
r = r.WithContext(ctx)
r, err := d.rewriteRequest(r)
r, matched, err := d.rewriteRequest(r)
if err != nil {
HandleError(ctx, w, r, http.StatusInternalServerError, errors.Wrap(err, "could not rewrite request"))
return
}
defer func() {
elapsed := time.Since(start)
for _, p := range matched {
metricProxyResponsesDurationSeconds.WithLabelValues(string(p)).Observe(elapsed.Seconds())
}
}()
ctx = r.Context()
layers, err := ctxLayers(ctx)

View File

@ -18,3 +18,12 @@ var metricProxyRequestsTotal = promauto.NewCounterVec(
},
[]string{metricLabelProxy},
)
var metricProxyResponsesDurationSeconds = promauto.NewHistogramVec(
prometheus.HistogramOpts{
Name: "proxy_responses_duration_seconds",
Help: "Bouncer proxy responses duration",
Namespace: metricNamespace,
},
[]string{metricLabelProxy},
)