Commit f7a24d3

Antonio Mika  ·  2026-10-25 21:57:09 -0400 EDT
parent 8d599bd
feat(metrics): let servers outside net/http record user metrics
2 files changed,  +29, -5
+11, -5
......@@ -88,14 +88,20 @@ func (m *UserMetrics) Handler(next http.Handler) http.Handler {
8888 next.ServeHTTP(mw, r.WithContext(context.WithValue(r.Context(), ctxUserKey{}, &user)))
8989
9090 status := mw.Status()
91- user = m.resolveUser(r, user, status)
92- labels := []string{user, strconv.Itoa(status), metricMethod(r.Method)}
93- m.requests.WithLabelValues(labels...).Inc()
94- m.duration.WithLabelValues(labels...).Observe(time.Since(start).Seconds())
95- m.bytes.WithLabelValues(user).Add(float64(mw.bytes))
91+ m.Observe(m.resolveUser(r, user, status), status, r.Method, time.Since(start), mw.bytes)
9692 })
9793 }
9894
95+// Observe records one request for servers that can't use Handler, such as
96+// tuns's gin logger. user must already be confirmed to exist. A negative
97+// size, which gin reports when nothing was written, counts as zero bytes.
98+func (m *UserMetrics) Observe(user string, status int, method string, elapsed time.Duration, bytes int64) {
99+ labels := []string{user, strconv.Itoa(status), metricMethod(method)}
100+ m.requests.WithLabelValues(labels...).Inc()
101+ m.duration.WithLabelValues(labels...).Observe(elapsed.Seconds())
102+ m.bytes.WithLabelValues(user).Add(float64(max(bytes, 0)))
103+}
104+
99105 func (m *UserMetrics) resolveUser(r *http.Request, user string, status int) string {
100106 if user != "" {
101107 m.known.Add(user, struct{}{})
+18, -0
......@@ -129,6 +129,24 @@ func TestUserMetricsMethod(t *testing.T) {
129129 }
130130 }
131131
132+func TestUserMetricsObserve(t *testing.T) {
133+ reg := prometheus.NewRegistry()
134+ m := NewUserMetrics("test", reg, UserMetricsOpts{})
135+
136+ m.Observe("carol", http.StatusOK, http.MethodGet, 0, 7)
137+ m.Observe("carol", http.StatusNotModified, "MADEUP", 0, -1)
138+
139+ if got := metricValue(t, reg, "pico_http_requests_total", map[string]string{"user": "carol", "status": "200", "method": "GET"}); got != 1 {
140+ t.Errorf("200 requests = %v, want 1", got)
141+ }
142+ if got := metricValue(t, reg, "pico_http_requests_total", map[string]string{"user": "carol", "status": "304", "method": "OTHER"}); got != 1 {
143+ t.Errorf("304 requests = %v, want 1", got)
144+ }
145+ if got := metricValue(t, reg, "pico_http_response_bytes_total", map[string]string{"user": "carol"}); got != 7 {
146+ t.Errorf("bytes = %v, want 7", got)
147+ }
148+}
149+
132150 func TestUserMetricsHijack(t *testing.T) {
133151 reg := prometheus.NewRegistry()
134152 h := NewUserMetrics("test", reg, UserMetricsOpts{}).Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {