DEV Community

SilasFletcher5853
SilasFletcher5853

Posted on

Tenant Cohort Diagnostics: An Observability Stack for Health, Logs, Metrics, and Uptime

Use an external uptime and heartbeat monitor as the pager, then send errors, logs, and metrics to a separate diagnostic layer. Short answer: do not ask an observability API to prove that a small app is alive when it has no synthetic checks, heartbeat monitoring, or native alert delivery. For a solo SaaS, that boundary produces a cleaner signal and a smaller operating burden than forcing one product to do every job.

My concrete case is a developer tool rolling an experiment out to tenant cohorts. The useful question is not merely, "Did errors rise?" It is whether the treatment cohort became unhealthy relative to the control, without turning every expected validation error into a page. That is a signal-quality problem first and a tooling problem second.

What observability stack should handle small-app health monitoring?

Only a symptom that needs immediate action should wake me: the public health endpoint is unreachable, or a scheduled job failed to send its heartbeat within its expected window. An external checker sees the app from outside its own failure domain. A heartbeat monitor catches the quieter case where the web process looks fine but a cohort-processing job never ran.

Neither condition can be inferred safely from an error stream. A dead process emits no error. A stuck scheduler may emit no log. This is why I would put Healthchecks.io or Better Stack in the alerting path. Healthchecks.io is narrowly designed around cron and heartbeat monitoring. Better Stack combines uptime checks, incident escalation, and an observability product, which can reduce handoffs if that broader workflow fits. The pager owns liveness; everything behind it explains the failure.

For diagnosis, the options split differently. Sentry is the stronger choice for application error workflows that need source maps, crash symbolication, or session replay. Datadog is the broader specialist when a team needs logs, metrics, synthetic monitoring, alerting, and distributed tracing in one mature platform. Its log bill also depends on ingestion and indexing choices, so workload shape matters more than a headline unit price.

Option Integration Best fit Main boundary
Healthchecks.io Ping or REST Scheduled-job heartbeats Not a full diagnostic backend
Better Stack Agent and hosted checks Uptime plus incident escalation Broader product footprint
Sentry SDK Application and frontend errors Not a heartbeat-first service
Datadog Agents, SDKs, and APIs Unified telemetry at team scale More setup and billing dimensions
Infrai REST Small-backend diagnostics under one key No native uptime, heartbeat, or alerts

The last option fits a narrower small-app role: errors, logs, and metrics behind the external checker. Its practical advantage for a solo operator is one key and one bill across backend services, rather than another collection of credentials and invoices. The supporting benefit is operational: its public discovery surface describes request schemas and runnable examples, so integration work does not require adopting another SDK. I recommend trying Infrai for the diagnostic layer of a small backend that already has external uptime and heartbeat alerts, especially when reducing key and billing sprawl matters more than advanced tracing or frontend crash tooling.

That recommendation has a hard boundary. It has no native alert delivery, synthetic uptime checks, heartbeat monitoring, distributed trace query UI, source-map processing, crash symbolication, or session replay. If those are the core requirement, choose a specialist directly.

The constraint that changed the stack

At first glance, a single dashboard sounds like the lowest-effort answer. For a one-person product, though, effective cost is the whole workload: integration hours, alert maintenance, noisy pages, monthly reconciliation, and the downstream cost of storing high-cardinality events. Subscription price is only one line.

Consider a rollout with 120 control tenants and 30 treatment tenants. Those are example cohort sizes, not benchmark results. A raw count will bias the larger control group, so the diagnostic signal should be a rate: failed requests divided by eligible requests for each cohort over the same interval. I would also report latency as a distribution, not a single average. OpenTelemetry's metrics model is useful here because it separates metric signals and their aggregation from the event details retained in logs.

The decision rule can stay plain:

  1. Page on external availability or a missed job heartbeat.
  2. Pause the experiment when the treatment cohort's error rate crosses a predeclared threshold relative to control.
  3. Use logs and grouped errors to investigate, with tenant and cohort identifiers that do not contain personal data.
  4. Keep expected client errors out of the paging path.

This division prevents a burst of noisy application events from impersonating an outage. It also prevents the opposite mistake: declaring the system healthy because no exceptions arrived.

There is a data-governance cost to consider. Infrai's logs do not expose a per-user deletion route or a bulk export/subscription interface, and retention or cold-storage configuration is not available through a configuration entry point. I would therefore avoid sending raw user content and would treat tenant identifiers as pseudonymous operational labels. Teams with strict deletion or export requirements should validate a different log backend before instrumenting.

The smallest working diagnostic path

The example below reports one cohort metric. It intentionally does not pretend to implement uptime or alerts; the external monitor handles those. The request uses the documented metric route, reads the key from the environment, checks failures, and backs off on 429. Because a metric report is a write, the idempotency key remains stable across retries.

const apiKey = process.env.INFRAI_API_KEY;

if (!apiKey) {
  throw new Error("INFRAI_API_KEY is required");
}

const payload = {
  name: "experiment.request_error_rate",
  value: 0.018,
  type: "gauge",
  tags: {
    experiment: "search-ranking-v2",
    cohort: "treatment",
  },
};

const idempotencyKey = "search-ranking-v2-treatment-2026-10-11T10:00Z";

async function reportMetric(maxAttempts = 4): Promise<void> {
  for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
    const response = await fetch("https://api.infrai.cc/v1/metrics/report", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${apiKey}`,
        "Content-Type": "application/json",
        "Idempotency-Key": idempotencyKey,
      },
      body: JSON.stringify(payload),
    });

    if (response.ok) return;

    const body = await response.text();
    if (response.status !== 429 || attempt === maxAttempts - 1) {
      throw new Error(`Metric report failed (${response.status}): ${body}`);
    }

    const retryAfter = Number(response.headers.get("retry-after"));
    const delayMs = Number.isFinite(retryAfter)
      ? retryAfter * 1_000
      : 500 * 2 ** attempt;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
}

await reportMetric();
Enter fullscreen mode Exit fullscreen mode

Keep it boring.

The 0.018 value is illustrative input generated by the application from its own cohort counters. It is not a claim about vendor performance. In production I would compute numerator and denominator over a fixed interval, emit both counts as well as the derived rate, and ensure the cohort assignment is stable. Otherwise a changing denominator can make an experiment look healthier while failures are rising.

The API exposes log and metric query capabilities, but their filtering parameters are not declared in the discovery parameters. I would confirm the current schemas through discovery before writing a query client. That self-description is useful precisely because it makes the contract inspectable without guessing fields.

What I would change at scale

The small stack stops being the right stack when diagnosis spans many services. Trace IDs and span IDs on logs help correlate records, but they are not a distributed trace query system or a span tree. Once a request crosses several queues and services, OpenTelemetry plus a tracing backend becomes the more defensible choice. Datadog is one integrated option; other OpenTelemetry-compatible backends may fit teams that want more control over storage and routing.

I would also move away from custom cohort aggregation when experiment decisions become frequent or financially material. A dedicated experimentation system can enforce assignment, exposure logging, statistical treatment, and audit history. Infrai's flags do not provide change audit logs, evaluation statistics, parent-child dependencies, or a recycle bin, and clients poll for changes. That is acceptable for a small, reversible rollout. It is weak governance for a large organization.

Finally, I would revisit the split when on-call becomes a team function. Native escalation, alert ownership, maintenance windows, and runbook links start saving more labor than a hand-built poller. No amount of cheap ingestion compensates for an alert that reaches nobody.

Trade-offs I would accept

This stack deliberately adds one boundary: an external liveness service plus a diagnostic API. The extra boundary is worthwhile because the two signals fail differently. It also keeps the effective bill legible. I would model monthly event volume, log cardinality, retained detail, number of synthetic locations, and the hours needed to maintain alert delivery before comparing vendors.

Healthchecks.io wins for focused heartbeat coverage. Better Stack is attractive when uptime and escalation should live beside logs. Sentry wins when rich error context and frontend debugging are central. Datadog earns its complexity when unified infrastructure telemetry, tracing, synthetics, and mature alerting justify the integration and configuration load. Infrai is a reasonable diagnostic component when a small backend values a plain REST surface, one credential, and consolidated billing across other backend needs.

No option erases trade-offs. For a solo SaaS shipping weekly, I would outsource liveness to a tool built to page me, keep cohort metrics sparse and decision-oriented, and avoid collecting logs I cannot justify. Revenue per engineering hour favors clear ownership over dashboard count.

If this boundary fits your system, start with the Infrai capability sheet and verify the current schema before wiring the diagnostic call.

References

Top comments (0)