Monorepo for Tangled tangled.org
858
fork

Configure Feed

Select the types of activity you want to include in your feed.

appview/metrics: add prometheus HTTP metrics, middleware, and endpoint #303

open opened by paeth.xyz targeting master from paeth.xyz/tangled-core: appview-metrics

The default metrics listener port is 127.0.0.1:7101, so it should only be accessible from the host itself, not the web. Port 7000 is where knotmirror currently exports, so 7101 is chosen to not collide. Lastly, this incidentally exposes lower-level go client metrics (e.g., garbage collection info) as well as AT and Jetstream metrics already calculated by the bsky indigo and jetstream packages - which could be very useful.

Signed-off-by: Kevin tangled-jj@paeth.xyz

Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:dzif5zfmzlbs6cdc67gzlv65/sh.tangled.repo.pull/3mkve73laua22
+95
Diff #1
+1
appview/config/config.go
··· 14 14 CookieSecret string `env:"COOKIE_SECRET, default=00000000000000000000000000000000"` 15 15 DbPath string `env:"DB_PATH, default=appview.db"` 16 16 ListenAddr string `env:"LISTEN_ADDR, default=0.0.0.0:3000"` 17 + MetricsListenAddr string `env:"METRICS_LISTEN_ADDR, default=127.0.0.1:7101"` 17 18 AppviewHost string `env:"APPVIEW_HOST, default=tangled.org"` 18 19 AppviewName string `env:"APPVIEW_Name, default=Tangled"` 19 20 Dev bool `env:"DEV, default=false"`
+74
appview/metrics/http.go
··· 1 + package metrics 2 + 3 + import ( 4 + "net/http" 5 + "strconv" 6 + "time" 7 + 8 + "github.com/go-chi/chi/v5" 9 + "github.com/prometheus/client_golang/prometheus" 10 + "github.com/prometheus/client_golang/prometheus/promauto" 11 + ) 12 + 13 + var ( 14 + httpRequestsTotal = promauto.NewCounterVec( 15 + prometheus.CounterOpts{ 16 + Name: "appview_http_requests_total", 17 + Help: "Total HTTP requests handled by the appview, partitioned by route, method, and status.", 18 + }, 19 + []string{"route", "method", "status"}, 20 + ) 21 + 22 + httpRequestDuration = promauto.NewHistogramVec( 23 + prometheus.HistogramOpts{ 24 + Name: "appview_http_request_duration_seconds", 25 + Help: "HTTP request latency in seconds, partitioned by route and method.", 26 + Buckets: prometheus.DefBuckets, 27 + }, 28 + []string{"route", "method"}, 29 + ) 30 + 31 + httpRequestsInFlight = promauto.NewGauge( 32 + prometheus.GaugeOpts{ 33 + Name: "appview_http_requests_in_flight", 34 + Help: "Currently in-flight HTTP requests.", 35 + }, 36 + ) 37 + ) 38 + 39 + // statusRecorder wraps http.ResponseWriter to capture the status code written by the handler. 40 + // The stdlib doesn't expose it once written, so we intercept WriteHeader. 41 + type statusRecorder struct { 42 + http.ResponseWriter 43 + status int 44 + } 45 + 46 + func (s *statusRecorder) WriteHeader(code int) { 47 + s.status = code 48 + s.ResponseWriter.WriteHeader(code) 49 + } 50 + 51 + // HTTPMiddleware records RED metrics (rate, errors, duration) for every HTTP request. 52 + // Mount it as the outermost middleware so it sees the full request lifecycle including 53 + // the final status code the handler writes. 54 + func HTTPMiddleware(next http.Handler) http.Handler { 55 + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 56 + httpRequestsInFlight.Inc() 57 + defer httpRequestsInFlight.Dec() 58 + 59 + start := time.Now() 60 + rec := &statusRecorder{ResponseWriter: w, status: http.StatusOK} 61 + 62 + next.ServeHTTP(rec, r) 63 + 64 + // chi populates the matched route pattern during routing, so we read it after 65 + // the handler runs. Unmatched requests get a stable label to bound cardinality. 66 + route := chi.RouteContext(r.Context()).RoutePattern() 67 + if route == "" { 68 + route = "unmatched" 69 + } 70 + 71 + httpRequestsTotal.WithLabelValues(route, r.Method, strconv.Itoa(rec.status)).Inc() 72 + httpRequestDuration.WithLabelValues(route, r.Method).Observe(time.Since(start).Seconds()) 73 + }) 74 + }
+2
appview/state/router.go
··· 11 11 "tangled.org/core/appview/issues" 12 12 "tangled.org/core/appview/knots" 13 13 "tangled.org/core/appview/labels" 14 + "tangled.org/core/appview/metrics" 14 15 "tangled.org/core/appview/middleware" 15 16 "tangled.org/core/appview/notifications" 16 17 "tangled.org/core/appview/pipelines" ··· 26 27 27 28 func (s *State) Router() http.Handler { 28 29 router := chi.NewRouter() 30 + router.Use(metrics.HTTPMiddleware) 29 31 middleware := middleware.New( 30 32 s.oauth, 31 33 s.db,
+10
cmd/appview/main.go
··· 5 5 "net/http" 6 6 "os" 7 7 8 + "github.com/prometheus/client_golang/prometheus/promhttp" 8 9 "tangled.org/core/appview/config" 9 10 "tangled.org/core/appview/state" 10 11 tlog "tangled.org/core/log" ··· 33 34 os.Exit(-1) 34 35 } 35 36 37 + go func() { 38 + mux := http.NewServeMux() 39 + mux.Handle("/metrics", promhttp.Handler()) 40 + logger.Info("starting metrics server", "address", c.Core.MetricsListenAddr) 41 + if err := http.ListenAndServe(c.Core.MetricsListenAddr, mux); err != nil { 42 + logger.Error("failed to start metrics server", "err", err) 43 + } 44 + }() 45 + 36 46 logger.Info("starting server", "address", c.Core.ListenAddr) 37 47 38 48 if err := http.ListenAndServe(c.Core.ListenAddr, state.Router()); err != nil {
+8
nix/modules/appview.nix
··· 33 33 description = "Listen address for the appview service"; 34 34 }; 35 35 36 + metricsListenAddr = mkOption { 37 + type = types.str; 38 + default = "127.0.0.1:7101"; 39 + example = "0.0.0.0:7101"; 40 + description = ''Listen address for the Prometheus metrics endpoint (`/metrics`).''; 41 + }; 42 + 36 43 dbPath = mkOption { 37 44 type = types.str; 38 45 default = "/var/lib/appview/appview.db"; ··· 286 293 { 287 294 TANGLED_DB_PATH = cfg.dbPath; 288 295 TANGLED_LISTEN_ADDR = cfg.listenAddr; 296 + TANGLED_METRICS_LISTEN_ADDR = cfg.metricsListenAddr; 289 297 TANGLED_APPVIEW_HOST = cfg.appviewHost; 290 298 TANGLED_APPVIEW_NAME = cfg.appviewName; 291 299 TANGLED_DEV =

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
appview/metrics: add prometheus HTTP metrics, middleware, and endpoint
merge conflicts detected
expand
  • appview/config/config.go:14
  • appview/state/router.go:11
  • cmd/appview/main.go:5
  • nix/modules/appview.nix:33
expand 0 comments
paeth.xyz submitted #0
1 commit
expand
appview/metrics: add prometheus HTTP metrics, middleware, and endpoint
expand 0 comments