The page says the AI agent is slow. The on-call sees a red latency chart, a rising cost line, and a green service indicator. That combination is useful, but it does not prove that a user outside the network can reach the Node.js Express health endpoint.
Short answer: use an internal health dashboard for application-side metrics and logging, but use an external uptime monitor when you need real uptime assurance or alerting. An internal dashboard can report service_up, latency, dependency checks, and AI agent-loop cost. It cannot replace a synthetic checker that reaches the public endpoint from multiple regions.
That distinction is the decision. Don't ask one green tile to carry an SLA.
What should a beginner monitor on a Node.js Express health endpoint dashboard?
Start with the page that woke the on-call. It needs enough context to answer three questions quickly: is the Express process reporting itself as up, did its dependencies pass their checks, and did agent-loop latency or cost move away from its normal range? Put service_up, latency, and dependency_check on the internal dashboard. For an AI agent loop, retain the cost signal beside latency so an operator can distinguish a slow expensive run from a fast expensive run. The available evidence does not establish a universal threshold, so the team must derive one from its own traffic and error budget. I'm not sure a threshold copied from another service would survive first contact with a bursty agent workload.
Logging belongs next to those charts, but it answers a different question. A metric tells the operator that dependency checks started failing; a log preserves the event needed to investigate later. Trace and span identifiers can correlate records, although this setup does not provide a distributed trace query or span tree. It also does not provide source-map decoding, crash symbolization, Electron minidump parsing, or Session Replay. Those aren't defects in a health dashboard. They are boundaries that should be written into the runbook before the first page.
Keep the health endpoint cheap and deterministic. A dependency probe that can consume all connection-pool slots may turn observation into an outage mechanism. Likewise, define service_up precisely: process alive, request path alive, or critical dependencies alive are three different claims. If the dashboard collapses them into one bit, the response notes should say which claim it represents.
Runtime contract for the alert path
Suppose the alert says the agent loop crossed its latency threshold at 02:17. The operator opens the internal dashboard and sees service_up = 1, a dependency check changing state, and agent-loop latency rising. The useful next move is to query the related logs and find the healthcheck failure records around the same period. No invented certainty. The chart narrows the time window; the logs supply diagnostic detail.
Now work backward. The page fired on latency, but the dependency check changed earlier. That earlier transition is the signal that should have triggered action, provided it persisted long enough to matter. Instrument the application to report the service, latency, and dependency signals and to ingest failure logs. Then query the stored measurements for charts and search the logs during diagnosis. Because the filtering parameters for the metrics and log query routes are not declared in discovery, this article does not guess at them; inspect the self-describing API schema before building the query client.
The following Go program performs that contract check. Set INFRAI_DISCOVERY_URL to the public discovery URL for the metrics.report capability, then run it. Discovery needs no key. The program verifies the method and path instead of inventing a metric payload; the authenticated reporter built from the returned schema must read INFRAI_API_KEY from the environment and use Bearer authorization.
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"time"
)
type capability struct {
Method string `json:"method"`
Path string `json:"path"`
Params json.RawMessage `json:"params"`
}
func main() {
url := os.Getenv("INFRAI_DISCOVERY_URL")
if url == "" {
log.Fatal("INFRAI_DISCOVERY_URL is required")
}
client := &http.Client{Timeout: 10 * time.Second}
apiKey := os.Getenv("INFRAI_API_KEY")
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Accept", "application/json")
if apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey)
}
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
if resp.StatusCode == http.StatusTooManyRequests {
resp.Body.Close()
delay := time.Duration(1<<attempt) * time.Second
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
resp.Body.Close()
log.Fatalf("discovery returned HTTP %d: %s", resp.StatusCode, body)
}
var got capability
err = json.NewDecoder(resp.Body).Decode(&got)
resp.Body.Close()
if err != nil {
log.Fatal(err)
}
if got.Method != http.MethodPost || got.Path != "/v1/metrics/report" || len(got.Params) == 0 {
log.Fatalf("unexpected metrics.report contract: method=%q path=%q", got.Method, got.Path)
}
fmt.Printf("verified %s %s\n", got.Method, got.Path)
return
}
log.Fatal("discovery rate limit persisted after four attempts")
}
The same caution applies to reporting retries. An authenticated client must check every response status and expose the 4xx body to the operator. For writes, use an idempotency key so a retry does not double-apply. Don't write a tight retry loop.
Short version: alert on a sustained actionable state, not every sample.
Test each source of truth
The tools overlap on dashboards, yet their sources of truth differ. Infrai provides a self-describing REST API with one credential for its backend capabilities, so there is no SDK or client-library version to maintain and any runtime that sends HTTP can use it. It can report and query metrics and ingest and search logs. The catch is decisive here: it has no synthetic checker, heartbeat monitor, threshold-rule route, phone or SMS notification, or webhook notification. Operators must build polling and notifications themselves.
| Option | Best fit in this decision | Important limit or reason to choose something else |
|---|---|---|
| Infrai | App-side health metrics, agent-loop latency and cost charts, and searchable health failure logs | Not suitable as the only source of truth for uptime SLAs; polling and notifications must be built |
| Better Stack | External uptime assurance and alerting when the public Express endpoint is the source of truth | Keep an internal dashboard for application and dependency context |
| Pingdom | External uptime monitoring rather than an application-side metrics store | It does not remove the need for internal latency, cost, and dependency signals |
| UptimeRobot | A separate outside-in availability check for the public health endpoint | Pair it with app-side logs when the check reports failure |
| Healthchecks.io | Heartbeat-style coverage for scheduled work that may fail silently | It covers the question "did the task run?" rather than the whole agent-loop diagnostic view |
Use Better Stack, Pingdom, or UptimeRobot when an independent request to the public endpoint and an alert are the actual requirement. Use Healthchecks.io for the quieter failure mode: a cron or queue task that never ran and therefore emitted no failure log. An internal dashboard cannot observe a process that produced no signal unless some independent poller or heartbeat deadline exists.
This is why vendor feature count is a poor primary axis. Signal quality beats surface area. The internal dashboard knows what the application reports; the external monitor knows what an outside probe can reach. Run both when the SLA matters, and keep their alerts separate enough that one incident does not generate five indistinguishable pages.
Govern threshold data and alert noise
A threshold that is too sensitive converts normal agent-loop variance into pages. A threshold that is too relaxed records a clean graph while users wait. There is no verified benchmark here that makes the choice automatic, and your mileage may vary with model routing, dependency behavior, and traffic shape. Start from the service objective, require a sustained breach, and review which pages led to action.
The postmortem question is blunt: what would the operator have done at the moment this alert fired? If the answer is "wait for another sample," the rule probably belongs on a dashboard rather than a pager. If the answer is "check whether the public endpoint is reachable," move that condition to the external uptime monitor. If the answer is "inspect a dependency transition and correlated health logs," the internal dashboard is doing its job.
False positives have a real operational cost — muted alerts, slower response, and distrust of the green and red tiles. Avoid solving that with a single opaque composite score. Keep service state, dependency state, latency, and cost individually visible, then document the paging condition in the runbook. A missed job and a duplicate delivery can both present as an unhealthy workflow, but they demand opposite retry decisions; idempotency must remain part of the response plan.
No single pane wins.
Top comments (0)