feat: add bounded process metrics and private monitoring endpoint

This commit is contained in:
2026-09-18 13:17:33 +08:00
parent 1f1d833f69
commit a7be32f661
24 changed files with 1467 additions and 19 deletions

View File

@@ -0,0 +1,93 @@
package observability
import (
"context"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/coder/websocket"
)
func TestWebSocketHandshakeIsNotConnectionLifetime(t *testing.T) {
r := NewRegistry()
released := make(chan struct{})
done := make(chan struct{})
h := r.Middleware(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer close(done)
c, err := websocket.Accept(w, req, nil)
if err != nil {
return
}
defer c.CloseNow()
<-released
}))
server := httptest.NewTLSServer(h)
defer server.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
c, _, err := websocket.Dial(ctx, strings.Replace(server.URL, "https://", "wss://", 1)+"/v1/worker/connect", &websocket.DialOptions{HTTPClient: server.Client()})
if err != nil {
close(released)
t.Fatal(err)
}
defer c.CloseNow()
// Dial returns after the upgrade response, while the server handler stays open.
deadline := time.Now().Add(time.Second)
for !strings.Contains(r.Render(), `orbit_websocket_connections{route="worker_connect"} 1`) {
if time.Now().After(deadline) {
close(released)
t.Fatal("upgrade not observed")
}
time.Sleep(time.Millisecond)
}
out := r.Render()
close(released)
<-done
if !strings.Contains(out, `orbit_http_requests_total{method="GET",route="worker_connect",status_class="1xx"} 1`) || !strings.Contains(out, "orbit_http_requests_in_flight 0\n") {
t.Fatal("upgrade counted as unfinished HTTP")
}
// done closes inside the handler; wait for middleware's defer to publish closure.
deadline = time.Now().Add(time.Second)
for !strings.Contains(r.Render(), `orbit_websocket_connections{route="worker_connect"} 0`) {
if time.Now().After(deadline) {
t.Fatal("connection gauge leaked")
}
time.Sleep(time.Millisecond)
}
if !strings.Contains(r.Render(), `orbit_http_requests_total{method="GET",route="worker_connect",status_class="1xx"} 1`) {
t.Fatal("upgrade counted twice")
}
}
func TestInformationalResponseAndStreamingPreserveHTTP(t *testing.T) {
r := NewRegistry()
server := httptest.NewServer(r.Middleware(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(103)
w.WriteHeader(201)
_, _ = io.WriteString(w, "first")
if err := http.NewResponseController(w).Flush(); err != nil {
panic(err)
}
_, _ = io.WriteString(w, "second")
})))
defer server.Close()
resp, err := server.Client().Get(server.URL + "/v1/tasks")
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil || resp.StatusCode != 201 || string(body) != "firstsecond" {
t.Fatal("wrapper changed HTTP response", resp.StatusCode, err)
}
deadline := time.Now().Add(time.Second)
for !strings.Contains(r.Render(), `route="tasks",status_class="2xx"} 1`) {
if time.Now().After(deadline) {
t.Fatal("103 blocked final status metric")
}
time.Sleep(time.Millisecond)
}
}