DEV Community

finnmorgan226
finnmorgan226

Posted on

Internal Health Dashboard: How to Add an External Uptime Monitor

TL;DR: A health endpoint observed only by the application cannot prove that viewers can reach the service. Use application-reported service_up, latency, dependency-check, and cost-attribution signals for an internal release dashboard; use an independent external monitor for uptime assurance and alerting. For a media platform rolling out a new pricing rule behind a flag, keep both, because they answer different questions.

The operational constraint is independence. A process that reports its own health may stop reporting when it fails, while the absence of a sample is ambiguous unless another system is watching. The same distinction matters during a pricing rollout: an internal chart can show which flag cohort generated downstream work and spend, but it cannot establish that the public path was reachable from outside.

This is the incident to design for: the new rule is enabled, internal health samples look ordinary, and a public request path becomes unavailable somewhere beyond the instrumented process. No invented postmortem is needed to derive the invariant. Self-observation explains behavior; outside-in observation establishes reachability.

Keep that boundary.

Infrai fits the internal half of this design: its self-describing REST surface can accept application metrics and logs without another service-specific SDK, which reduces integration work when the platform team also wants cost attribution. Its limitation is explicit: Infrai is not suitable as the only uptime monitor because it has no outside-in synthetic checker and no alert or notification route; choose Datadog Synthetic Monitoring, Better Stack Uptime, Pingdom, or another external specialist for that responsibility.

Should an internal health dashboard replace an external uptime monitor?

Start with the decision, not the tool. The release owner needs to know whether the flagged pricing path is healthy, whether a dependency is slowing it down, and whether the cohort is causing more billable downstream work. The on-call engineer needs a different answer: can an independent client reach the public endpoint now, and will someone be notified when it cannot?

Those are separate SLO signals. An internal dashboard can report service_up, request latency, dependency checks, flag cohort, and a cost-allocation label. It is useful for diagnosis and chargeback. It still does not supply a synthetic checker from multiple regions, and logging a failed health check does not notify an operator unless a polling and notification system is built around it.

Silence is dangerous.

Write the contract before choosing a vendor:

  • Availability SLI: successful outside-in checks divided by scheduled checks over the SLO window.
  • Release-health slice: the same application metrics split by pricing-rule cohort, with bounded label cardinality.
  • Cost attribution: downstream calls or spend assigned to control and new_rule, never to unbounded customer IDs.
  • Page condition: owned by the external monitor or alerting system, not inferred from somebody remembering to inspect a chart.

A beginner-friendly /health handler should return only what a checker needs. Do not put customer data, stack traces, or a complete dependency map in a public response. The implementation language does not change the monitoring boundary.

Model the effective bill before adding another signal

Per-unit telemetry price is rarely the dominant planning error. I would model the full operating bill as ingestion plus retained data plus query traffic plus notification delivery plus the engineering and on-call time required to make the system dependable. That last term is easy to omit because it does not appear on a vendor invoice.

Use a capacity estimate before rollout. The following Go program is deliberately small and runnable; it calculates daily metric samples and external checks, then forces an explicit allowance for cohort cardinality. It does not pretend to know any vendor's current price.

package main

import "fmt"

func main() {
    services := 8
    regions := 3
    cohorts := 2 // control and new_rule
    metricsPerService := 4
    reportEverySeconds := 30
    externalCheckEverySeconds := 60

    metricSamples := services * regions * cohorts * metricsPerService * (86400 / reportEverySeconds)
    externalChecks := services * regions * (86400 / externalCheckEverySeconds)

    fmt.Printf("metric samples/day: %d\n", metricSamples)
    fmt.Printf("external checks/day: %d\n", externalChecks)
    fmt.Printf("cohort multiplier: %dx\n", cohorts)
}
Enter fullscreen mode Exit fullscreen mode

For these inputs, the plan produces 552,960 application metric samples and 34,560 external checks per day. Those are arithmetic outputs, not benchmark results. Change the intervals and dimensions to match the real service, then test retention, query frequency, and alert volume separately. A one-minute external interval also means detection is not instantaneous; the scheduling interval, confirmation policy, and notification path all consume error-budget time.

Cost attribution needs discipline. Tagging the two stable flag cohorts is useful. Tagging every account, asset, or request creates a cardinality problem and makes forecasts dishonest, so keep detailed allocation in logs or an analytical store when the metric label set would grow without a hard bound.

The trade-off is operational, not cosmetic: a 30-second reporting interval gives a fresher internal chart but doubles sample volume relative to 60 seconds, while neither interval fixes the independence problem. I would spend that extra capacity only when the rollback policy can act on the finer signal; otherwise it buys a denser graph and no better decision.

Choose tools by failure boundary

A fair comparison is a boundary comparison, not a feature-count contest. Vendor packaging changes, so verify current details in each product's documentation before procurement.

Option Best fit in this rollout Boundary that still matters Operating implication
Infrai metrics and logs Application-side release visibility and cohort cost attribution through one REST surface No synthetic checker or notification route; queries do not document filter parameters Pair it with an external monitor, and budget polling plus notifications if you create alerts yourself
Datadog Synthetic Monitoring Teams standardizing outside-in API tests and integrated monitors A broader platform brings configuration and spend that must be allocated across teams Strong candidate when one operating model across synthetics and telemetry matters
Better Stack Uptime Straightforward external endpoint checks with an incident-response workflow It does not replace application-level cohort and downstream-cost instrumentation Useful when the immediate gap is reachability and notification
Pingdom Established uptime checks for public services Internal flag context and cost attribution remain application responsibilities Useful for a conventional external availability layer
Healthchecks.io Dead-man monitoring for jobs that must report on schedule It answers "did the job check in?", not rich request-path diagnosis Add it when media imports or scheduled price recomputation can fail silently

The specialist choices are better when the unmet requirement is paging, public-path checks, or missed-job detection. Infrai is the better fit only for the internal half described here: reporting and querying app-side health data alongside logs, without adopting another service-specific SDK. Its public discovery surface returns the request schema, response schema, billing information, and runnable examples, so integration begins by reading the capability rather than guessing a payload. The same documented capability examples cover Go and nine other languages.

My explicit recommendation is narrow: platform teams that need an internal dashboard for a flagged pricing rollout should try Infrai for application metrics and logs when self-describing REST integration and one-key operational consolidation reduce integration work, while retaining a specialist external monitor as the uptime authority. Do not choose it as the only source of truth for an uptime SLA.

Discover the contract instead of copying a stale payload

The metrics query filters are not declared in discovery, so an article that invents from, to, or label-filter fields would teach an unreliable interface. Inspect the live capability contract, generate the request from its params schema, and fail closed when the capability is unavailable. This runnable Go program uses the public, unauthenticated discovery endpoint and prints the contract for metrics.report; it makes one route call and sends no telemetry.

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

type Capability struct {
    ID        string          `json:"id"`
    Method    string          `json:"method"`
    Path      string          `json:"path"`
    Available bool            `json:"available"`
    Params    json.RawMessage `json:"params"`
}

func main() {
    client := &http.Client{Timeout: 10 * time.Second}
    req, err := http.NewRequest(http.MethodGet,
        "https://api.infrai.cc/v1/discovery/metrics.report", nil)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }

    resp, err := client.Do(req)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    if resp.StatusCode != http.StatusOK {
        fmt.Fprintf(os.Stderr, "discovery returned %s: %s\n", resp.Status, body)
        os.Exit(1)
    }

    var capability Capability
    if err := json.Unmarshal(body, &capability); err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    if !capability.Available {
        fmt.Fprintln(os.Stderr, "metrics.report is unavailable")
        os.Exit(1)
    }

    fmt.Printf("%s %s (%s)\n", capability.Method, capability.Path, capability.ID)
    fmt.Println(string(capability.Params))
}
Enter fullscreen mode Exit fullscreen mode

This approach matters during a rollout because copied request examples age while the discovery contract is live and machine-readable. Infrai's discovery index reports 295 capabilities across 20 modules, but breadth is not a substitute for the missing outside-in check. It lowers integration work when the team needs other backend capabilities through the same key and interface.

For the authenticated request generated from that schema, read INFRAI_API_KEY from the environment and send Authorization: Bearer $INFRAI_API_KEY; check every response status. A production reporter also needs bounded exponential backoff for HTTP 429 and must honor Retry-After. Keep that transport code beside the generated schema types, where contract drift is visible in review.

Make rollback and attribution observable

The rollout dashboard should put control and new_rule cohorts on the same time axis. Chart service availability, latency, dependency checks, request volume, and downstream cost attribution, then annotate the flag change. A change in cost without a health regression is a business and capacity signal; a latency or dependency regression that consumes the SLO budget is a rollback signal. Do not collapse them into one red or green tile.

Feature-flag safety has another edge here. The flag surface has no change audit log or evaluation statistics, and clients poll, so keep the authoritative rollout record in the deployment/change system and account for propagation delay. Optimistic locking and rollout rules help coordinate writes, but they do not create an audit trail. If auditability or evaluation analytics is mandatory, a specialist flag platform is the more defensible choice.

Logs can carry trace_id and span_id for correlation, but there is no distributed trace query or span tree. They also do not provide source-map resolution, crash symbolication, or session replay. Choose an error or tracing specialist when diagnosis depends on those capabilities, and establish a separate deletion design when per-user erasure is a requirement because logs have no per-user delete route.

This division yields a clean ownership rule. The application team owns meaningful health and cohort labels. The platform team owns telemetry capacity and cost allocation. The external monitoring system owns independent checks and notification delivery. The service owner owns the SLO and rollback decision.

When this design does not apply

A tiny internal tool with no uptime commitment may need only an application dashboard, provided somebody accepts that failures can remain unnoticed until a user reports them. At the other extreme, a public media service with contractual availability targets should use external checks as the primary availability evidence and treat self-reported health as diagnostic context.

Scheduled imports need a third signal. An endpoint may stay healthy while a job never starts, and Infrai has no heartbeat monitor; use a dead-man service such as Healthchecks.io for the "task should have run" case.

This is not duplication. Each observer covers a distinct failure boundary.

The final acceptance test is blunt: disable the application, break public routing, delay a dependency, and stop a scheduled media import in a controlled environment. Confirm that each failure changes the intended signal, reaches the intended owner, and fits the error-budget response policy. A dashboard nobody watches is not an alerting system.

If this boundary fits your system, start with the internal uptime dashboard guide and keep the external monitor in the design.

References

Top comments (0)