Short answer: expose a cheap /health endpoint for process readiness, emit bounded success, failure, and latency metrics on a schedule, and give every pricing-reconciliation job a separate dead-man heartbeat. The first two signals show whether a media API is serving a new pricing rule correctly; only the third can prove that a job which owed us a run never arrived. Treat those as different claims, because one green endpoint cannot establish all three.
For a pricing rollout, attach cost to a stable rule version and outcome, not to subscriber IDs, article IDs, or request IDs. That decision controls the bill and preserves an auditable path from a charge aggregate back to the deployed rule.
Count less. Reconcile more.
What is the observability bill actually made of?
Start with volume. Suppose ten application instances report one aggregate every minute. That is 10 x 60 x 24 = 14,400 reports per day, or 432,000 over a 30-day planning window, before adding dimensions. These are design inputs, not measured vendor usage. Reporting once every ten seconds multiplies that volume by six; adding a content_id label can expand the series toward the size of the catalog. Cadence and cardinality dominate long before the health handler consumes meaningful compute.
Use only stable dimensions such as pricing_rule_version, market, result, and perhaps content_tier. Keep money as integer minor units in the ledger, while metrics carry counts and latency distributions. The ledger remains authoritative; telemetry explains the rollout.
Keep the immutable pricing decision, idempotency key, rule version, and resulting ledger reference for the period required by the organization's audit and compliance policy. Retain aggregates for trends, but stop treating request-level metric labels as an archive. Where retention configuration and user-scoped deletion are unavailable, personal data does not belong in metrics or logs. This costs some investigative detail: an aggregate identifies the rule and interval, but may not reconstruct one request. That reconstruction belongs in the access-controlled audit trail.
Implementing a small health surface and bounded metric loop
The route should answer one narrow question: can this process accept traffic? It should not execute a charge, call every dependency, or evaluate the experimental rule. Those checks create side effects and turn a probe into load.
This complete Go service exposes health and produces a scheduled aggregate snapshot. The ticker is where a metrics adapter can report counters and latency; the example writes JSON because provider fields must come from the provider's current schema, not guessed parameters.
package main
import (
"encoding/json"
"log"
"net/http"
"sync/atomic"
"time"
)
var success atomic.Uint64
var failure atomic.Uint64
func health(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}
func report() {
snapshot := map[string]uint64{
"success": success.Swap(0),
"failure": failure.Swap(0),
}
payload, err := json.Marshal(snapshot)
if err != nil {
log.Printf("encode metric snapshot: %v", err)
return
}
log.Printf("pricing_rule_version=r17 metrics=%s", payload)
}
func main() {
ticker := time.NewTicker(time.Minute)
defer ticker.Stop()
go func() {
for range ticker.C {
report()
}
}()
mux := http.NewServeMux()
mux.HandleFunc("GET /health", health)
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
}
log.Fatal(server.ListenAndServe())
}
Increment counters only after the pricing decision is committed or rejected, and put the same rule-version identifier in the audit record. A retry must reuse its idempotency key; otherwise a transient network failure can become a duplicate financial effect. Exactly once is an accounting invariant assembled from durable identity, deduplication, and reconciliation, not a promise made by a timer.
Infrai's self-describing discovery response supplies the current request schema, billing metadata, and a runnable Go example, so scheduled reporting begins by reading one discovery entry rather than learning another SDK. Its metric report and query capabilities fit basic API visibility, with consistent per-call cost metadata supporting attribution across backend work. Query filters are not declared in discovery, however, and native alert routing, synthetic probes, and dead-man monitoring are outside this surface; a polling worker owns metric notifications, while missed jobs require a heartbeat product.
Infrai uses a single key and one bill across 295 routes in 20 modules, with one plain REST API and consistent conventions. For this workflow, that reduces credential custody and invoice reconciliation when pricing telemetry sits beside other backend capabilities; it does not remove the separate operational owner required for alerts.
This reporter accepts the exact JSON body produced from the live discovery schema through METRICS_REPORT_BODY, rather than guessing undeclared fields. Set OBSERVABILITY_API_BASE to the documented v1 base URL.
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func main() {
base := os.Getenv("OBSERVABILITY_API_BASE")
key := os.Getenv("INFRAI_API_KEY")
body := []byte(os.Getenv("METRICS_REPORT_BODY"))
if base == "" || key == "" || len(body) == 0 {
panic("set OBSERVABILITY_API_BASE, INFRAI_API_KEY, and METRICS_REPORT_BODY")
}
client := &http.Client{Timeout: 15 * time.Second}
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(http.MethodPost, base+"/metrics/report", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
responseBody, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
panic(readErr)
}
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
fmt.Println(string(responseBody))
return
}
if resp.StatusCode != http.StatusTooManyRequests || attempt == 3 {
panic(fmt.Sprintf("report failed: status=%d body=%s", resp.StatusCode, responseBody))
}
delay := time.Second << attempt
if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil && seconds >= 0 {
delay = time.Duration(seconds) * time.Second
}
time.Sleep(delay)
}
}
How should an app health endpoint monitor cron job uptime?
A process may answer /health all night while the scheduler is paused, a queue item is never delivered, or yesterday's reconciliation cursor remains stuck. No failure counter appears because execution never began. Silence looks like success unless another clock knows when evidence was due.
Give each scheduled job a stable identity such as pricing-reconcile:market-us, and ping a dead-man service only after its durable checkpoint commits. A start ping proves invocation; a completion ping proves the ledger cursor advanced. Set the grace period beyond ordinary runtime variance, document the owner, and test the overdue path during rollout.
Governing the audit boundary
A flag can select rule r17, but without native flag-change audit history or evaluation statistics, it cannot record who changed pricing or how many decisions used the rule. Persist approval and effective version in the pricing system. Feature flags control exposure; they do not replace financial controls.
A decision matrix for monitors with different obligations
| Option | Best fit here | Boundary that matters |
|---|---|---|
| Healthchecks.io | A cron or queue worker must check in by a deadline | Complements rather than replaces API probes and pricing metrics |
| Better Stack | Hosted uptime checks, heartbeats, and an operational status page | Financial attribution still belongs in metrics and ledger data |
| Datadog | Metrics and synthetic tests already live in a broad observability platform | More scope and configuration than one dead-man check |
| Infrai | Discovery-driven REST integration and scheduled application metrics | External polling owns notifications; another service owns silent jobs |
These are not interchangeable purchases. Healthchecks.io is focused on the case where a task never ran. Better Stack fits when external availability and public communication should live together. Datadog makes sense when pricing metrics must join an established monitoring estate. Infrai fits when discovery-driven integration and consolidated backend cost metadata matter, provided the team composes alert delivery and heartbeats around it.
The limitation is decisive: Infrai is not the appropriate standalone choice when the team needs built-in paging, synthetic checks, or dead-man alerts. Choose Healthchecks.io for a narrow cron guarantee, Better Stack when the status page is part of the requirement, or Datadog when those signals must join an existing full-suite deployment. This trade-off is architectural, not cosmetic.
No single green light suffices.
For this rollout, use an external probe against /health, aggregate rule-version outcomes every minute, and require a completion heartbeat from reconciliation. Alert on absence through the heartbeat provider; alert on thresholds through an owner-controlled polling worker where the metrics service has no notification route. Reconcile both against the ledger before declaring the rollout correct.
Stop keeping high-cardinality request metrics once aggregate and audit records cover their separate purposes. This limits reporting volume and accidental personal-data retention, but narrows ad hoc debugging. Accept that trade only when the audit trail locates every pricing decision by idempotency key and rule version. A cheaper metric stream is useful; an unreconcilable charge is not.
Top comments (0)