Skip to content

Commit 505c556

Browse files
committed
feat: add api http metrics
- measure api http request duration Signed-off-by: Siavash Safi <[email protected]>
1 parent 23bfa1b commit 505c556

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

api/api.go

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import (
1919
"log/slog"
2020
"net/http"
2121
"runtime"
22+
"strings"
2223
"time"
2324

2425
"github.com/prometheus/client_golang/prometheus"
26+
"github.com/prometheus/client_golang/prometheus/promhttp"
2527
"github.com/prometheus/common/model"
2628
"github.com/prometheus/common/promslog"
2729
"github.com/prometheus/common/route"
@@ -40,6 +42,7 @@ type API struct {
4042
v2 *apiv2.API
4143
deprecationRouter *V1DeprecationRouter
4244

45+
requestDuration *prometheus.HistogramVec
4346
requestsInFlight prometheus.Gauge
4447
concurrencyLimitExceeded prometheus.Counter
4548
timeout time.Duration
@@ -75,6 +78,8 @@ type Options struct {
7578
// Registry is used to register Prometheus metrics. If nil, no metrics
7679
// registration will happen.
7780
Registry prometheus.Registerer
81+
// RequestDuration is used to measure the duration of HTTP requests.
82+
RequestDuration *prometheus.HistogramVec
7883
// GroupFunc returns a list of alert groups. The alerts are grouped
7984
// according to the current active configuration. Alerts returned are
8085
// filtered by the arguments provided to the function.
@@ -132,8 +137,6 @@ func New(opts Options) (*API, error) {
132137
return nil, err
133138
}
134139

135-
// TODO(beorn7): For now, this hardcodes the method="get" label. Other
136-
// methods should get the same instrumentation.
137140
requestsInFlight := prometheus.NewGauge(prometheus.GaugeOpts{
138141
Name: "alertmanager_http_requests_in_flight",
139142
Help: "Current number of HTTP requests being processed.",
@@ -156,6 +159,7 @@ func New(opts Options) (*API, error) {
156159
return &API{
157160
deprecationRouter: NewV1DeprecationRouter(l.With("version", "v1")),
158161
v2: v2,
162+
requestDuration: opts.RequestDuration,
159163
requestsInFlight: requestsInFlight,
160164
concurrencyLimitExceeded: concurrencyLimitExceeded,
161165
timeout: opts.Timeout,
@@ -181,13 +185,17 @@ func (api *API) Register(r *route.Router, routePrefix string) *http.ServeMux {
181185
if routePrefix != "/" {
182186
apiPrefix = routePrefix
183187
}
184-
// TODO(beorn7): HTTP instrumentation is only in place for Router. Since
185-
// /api/v2 works on the Handler level, it is currently not instrumented
186-
// at all (with the exception of requestsInFlight, which is handled in
187-
// limitHandler below).
188188
mux.Handle(
189189
apiPrefix+"/api/v2/",
190-
api.limitHandler(http.StripPrefix(apiPrefix, api.v2.Handler)),
190+
api.instrumentHandler(
191+
apiPrefix,
192+
api.limitHandler(
193+
http.StripPrefix(
194+
apiPrefix,
195+
api.v2.Handler,
196+
),
197+
),
198+
),
191199
)
192200

193201
return mux
@@ -226,3 +234,17 @@ func (api *API) limitHandler(h http.Handler) http.Handler {
226234
"Exceeded configured timeout of %v.\n", api.timeout,
227235
))
228236
}
237+
238+
func (api *API) instrumentHandler(prefix string, h http.Handler) http.Handler {
239+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
240+
path, _ := strings.CutPrefix(r.URL.Path, prefix)
241+
// avoid high cardinality label values by replacing the actual silence IDs with a placeholder
242+
if strings.HasPrefix(path, "/api/v2/silence/") {
243+
path = "/api/v2/silence/{silenceID}"
244+
}
245+
promhttp.InstrumentHandlerDuration(
246+
api.requestDuration.MustCurryWith(prometheus.Labels{"handler": path}),
247+
h,
248+
).ServeHTTP(w, r)
249+
})
250+
}

cmd/alertmanager/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,12 @@ var (
6868
prometheus.HistogramOpts{
6969
Name: "alertmanager_http_request_duration_seconds",
7070
Help: "Histogram of latencies for HTTP requests.",
71-
Buckets: []float64{.05, 0.1, .25, .5, .75, 1, 2, 5, 20, 60},
71+
Buckets: prometheus.DefBuckets,
7272
NativeHistogramBucketFactor: 1.1,
7373
NativeHistogramMaxBucketNumber: 100,
7474
NativeHistogramMinResetDuration: 1 * time.Hour,
7575
},
76-
[]string{"handler", "method"},
76+
[]string{"handler", "method", "code"},
7777
)
7878
responseSize = prometheus.NewHistogramVec(
7979
prometheus.HistogramOpts{
@@ -377,6 +377,7 @@ func run() int {
377377
Concurrency: *getConcurrency,
378378
Logger: logger.With("component", "api"),
379379
Registry: prometheus.DefaultRegisterer,
380+
RequestDuration: requestDuration,
380381
GroupFunc: groupFn,
381382
})
382383
if err != nil {

0 commit comments

Comments
 (0)