// Package metrics provides in-process Prometheus-format instrumentation. // All counters and gauges use atomic operations and are safe for concurrent use. package metrics import ( "fmt" "net/http" "sync/atomic" "time" ) var startTime = time.Now() // Counters and gauges — updated by callers in ws.go and api.go. var ( wsConns atomic.Int64 // current open WebSocket connections (gauge) msgIn atomic.Int64 // total WS messages received (counter) msgOut atomic.Int64 // total WS messages sent (counter) writeOps atomic.Int64 // total signal write operations (counter) historyReqs atomic.Int64 // total history requests served (counter) ) // IncWsConns increments the active WebSocket connection gauge. func IncWsConns() { wsConns.Add(1) } // DecWsConns decrements the active WebSocket connection gauge. func DecWsConns() { wsConns.Add(-1) } // IncMsgIn increments the total inbound WS message counter. func IncMsgIn() { msgIn.Add(1) } // IncMsgOut increments the total outbound WS message counter. func IncMsgOut() { msgOut.Add(1) } // IncWrites increments the signal write operation counter. func IncWrites() { writeOps.Add(1) } // IncHistoryReqs increments the history request counter. func IncHistoryReqs() { historyReqs.Add(1) } // Handler returns an http.HandlerFunc that renders Prometheus-format metrics. // activeSubs is called each request to read the current number of unique signal // subscriptions from the broker; pass nil to omit the metric. func Handler(activeSubs func() int) http.HandlerFunc { return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8") uptime := time.Since(startTime).Seconds() subs := 0 if activeSubs != nil { subs = activeSubs() } fmt.Fprintf(w, "# HELP uopi_uptime_seconds Seconds elapsed since the server started\n") fmt.Fprintf(w, "# TYPE uopi_uptime_seconds counter\n") fmt.Fprintf(w, "uopi_uptime_seconds %.3f\n\n", uptime) fmt.Fprintf(w, "# HELP uopi_ws_connections Current number of open WebSocket connections\n") fmt.Fprintf(w, "# TYPE uopi_ws_connections gauge\n") fmt.Fprintf(w, "uopi_ws_connections %d\n\n", wsConns.Load()) fmt.Fprintf(w, "# HELP uopi_signal_subscriptions Current number of unique upstream signal subscriptions\n") fmt.Fprintf(w, "# TYPE uopi_signal_subscriptions gauge\n") fmt.Fprintf(w, "uopi_signal_subscriptions %d\n\n", subs) fmt.Fprintf(w, "# HELP uopi_ws_messages_in_total Total WebSocket messages received from clients\n") fmt.Fprintf(w, "# TYPE uopi_ws_messages_in_total counter\n") fmt.Fprintf(w, "uopi_ws_messages_in_total %d\n\n", msgIn.Load()) fmt.Fprintf(w, "# HELP uopi_ws_messages_out_total Total WebSocket messages sent to clients\n") fmt.Fprintf(w, "# TYPE uopi_ws_messages_out_total counter\n") fmt.Fprintf(w, "uopi_ws_messages_out_total %d\n\n", msgOut.Load()) fmt.Fprintf(w, "# HELP uopi_write_ops_total Total signal write operations\n") fmt.Fprintf(w, "# TYPE uopi_write_ops_total counter\n") fmt.Fprintf(w, "uopi_write_ops_total %d\n\n", writeOps.Load()) fmt.Fprintf(w, "# HELP uopi_history_requests_total Total historical data requests served\n") fmt.Fprintf(w, "# TYPE uopi_history_requests_total counter\n") fmt.Fprintf(w, "uopi_history_requests_total %d\n\n", historyReqs.Load()) } }