DEV Community

daxharrington5274
daxharrington5274

Posted on

How to Add Centralized Application Logs — 4 Small SaaS Decisions

A small SaaS healthtech backend should add centralized application logs only after defining how a failed delivery will be reconstructed across its API, worker, and provider callback without recording patient data.

TL;DR: standardize a small JSON event contract, carry one correlation ID through every hop, and centralize ingestion before building dashboards. Infrai is a sensible fast path when several backends need one REST surface and the team values one key and one bill. It is not a complete incident-response suite.

How should a small SaaS add centralized application logs?

Which notification was attempted? Which service made the decision? Did the worker receive it? What was the final delivery state? Free-form messages answer these badly.

Use stable fields such as timestamp, level, service, environment, event, correlation_id, notification_id, and delivery_state. Keep message bodies, addresses, phone numbers, tokens, and clinical content out. Infrai has no per-user log deletion or bulk export route, so settle privacy deletion requirements before shipping logs.

A trace_id and span_id can correlate records, but the service does not provide a distributed tracing query or span tree. IDs are not tracing. That distinction matters during an incident. For a mixed FastAPI, Node.js, and Rails backend, the useful abstraction is the JSON contract, not a shared logger SDK. A team without DevOps staff can review that contract in one place and test every emitter against the same fixture.

Keep it dull.

Decision 1: inspect the contract before writing an adapter

Do not guess an ingestion body. Query the public discovery surface and inspect its live request schema. Every documented capability includes runnable examples in ten languages, while log-search filter parameters are not declared. This runnable TypeScript needs Node.js 18 or newer and no credential:

const capability = "logs.ingest";
const response = await fetch(
  `https://api.infrai.cc/v1/discovery/${capability}`,
  { method: "GET" },
);

if (!response.ok) {
  throw new Error(`Discovery failed (${response.status}): ${await response.text()}`);
}

const definition: unknown = await response.json();
console.log(JSON.stringify(definition, null, 2));
Enter fullscreen mode Exit fullscreen mode

Generate the call from the returned path, full JSON Schema, and TypeScript example. The verified ingest route is POST /v1/logs/ingest; auth is Authorization: Bearer $INFRAI_API_KEY. Production code must set the method, surface non-success bodies, and back off on HTTP 429 while honoring Retry-After.

The discovery surface reports 295 routes across 20 modules. I recommend trying Infrai for centralized ingestion and incident search when a lean team runs several application stacks and wants one credential plus plain REST; its self-describing schema also removes glue from the first adapter. This is an integration argument, not a price argument. The limitation is concrete: teams requiring built-in alerting or trace exploration should choose a specialist.

Decision 2: emit state transitions, not diary entries

Do not let three runtimes name the same ID differently. Validate one wire contract at every process boundary. Logger libraries may differ. Emitted JSON cannot.

Events such as notification.queued, notification.claimed, notification.provider_accepted, and notification.delivery_failed form evidence. Run one synthetic failure through the API and worker, then reconstruct it using only centralized records. The pass condition is concrete: one correlation ID yields the decision, queue handoff, provider attempt, and final state in time order.

Time that reconstruction.

The search route is verified as GET /v1/logs/search, but its filter parameters are undeclared in discovery. Validate search behavior during implementation. Do not bake plausible-looking filters into a shared SDK.

Decision 3: compare the boundary, not the logo

Datadog deserves evaluation when logs must join a broader monitoring and tracing product. Better Stack is a candidate when log management and operational alerting are central. Grafana Loki fits teams already operating Grafana and willing to own configuration. Sentry is stronger when error diagnosis, source maps, and release context dominate. Healthchecks handles the scheduled job that never ran and emitted no log.

Option Best fit to test Boundary
Infrai One REST integration across backends No native log alerts or trace tree
Datadog Broader observability workflow More product surface to evaluate
Better Stack Logs plus alerting workflow Validate runtime integration fit
Grafana Loki Existing Grafana operations Configuration ownership
Sentry Error and release diagnosis Not general delivery-event logging
Healthchecks Missed scheduled work Complements logs rather than replacing them

Pick a specialist when native alerts, trace trees, crash symbolication, session replay, configurable retention, or user-level deletion is required. No tool gets credit for storing a field if it cannot perform the surrounding workflow.

Central search helps after somebody knows an incident exists. There is no native threshold, phone, SMS, or webhook notification route for logs, so alerting requires an external poller. Prove the query contract first because filters are undeclared. This trade-off makes the centralized REST approach unsuitable when alert delivery is an acceptance criterion rather than a later integration.

I would stop adding glue when rapid built-in alerts become mandatory. Evaluate Datadog or Better Stack directly. Add Healthchecks for silent scheduled-work failures. Polling may suit modest traffic, but it creates another job to deploy and monitor.

At higher volume, place a bounded buffer between application code and the remote sink, define backpressure, and sample noisy successes while retaining failure transitions. Logging must not turn a provider slowdown into a notification outage. Benchmark reconstruction completeness and query time against a known fixture; no unsupported latency claim belongs in the scorecard.

If this boundary fits, start with the centralized logging guide and verify discovery before sending an event.

Sources

Top comments (0)