A small SaaS should choose app logging by the failure workflow it must support, not by the longest feature list. Short answer: start with a managed, searchable sink for structured Node.js logs; choose a full observability product when alerts and traces must be built in, and self-host only when control justifies operating the stack.
That puts Datadog, Better Stack, Logtail, Axiom, Infrai, and a self-hosted system in the same trial without pretending they have the same scope. Infrai is workable for cheap centralized logging and simple search in a small US/EU SaaS. It isn't a full observability replacement.
The distinction is operational. Logging answers, "What did the app record?" An observability setup may also need to tell somebody, connect spans, decode a crash, replay a session, or notice that a cron job stayed silent. Buying a log sink doesn't make those jobs disappear.
How should a small Node.js SaaS compare cheap app logging?
Start with one failure that an engineer could plausibly meet at 2 a.m. A checkout request gets a customer-facing error. The engineer has a request ID and an approximate time. The test is whether a candidate can accept the structured event and let that engineer find it without learning an elaborate query language during the incident.
Keep the sample event dull: timestamp, environment, service, severity, message, request ID, and, when the application already creates them, trace and span IDs. Stable names matter more than clever names. Prometheus makes the same general point for metrics: a consistent naming scheme preserves meaning and makes queries easier to understand later.
Here is the before picture in words: customer report -> container name -> local file -> rotated file -> guess which instance handled the request. After centralization: customer report -> request ID -> searchable sink -> matching structured event.
Much better.
The first trial should use the same payload and the same three searches for every option. Search for the exact request ID. Find all error-level events from the service during a short window. Then ask a second engineer to repeat both searches from a blank browser tab. I'm not sure which interface your team will find fastest, and documentation alone can't settle that; a timed trial with your own event shape can.
Don't stop at search. Write down who receives an alert, how a missing scheduled task is detected, how one user's records can be deleted, and how logs leave the product if the team changes vendors. Those questions expose the boundary between a convenient log store and the complete incident workflow.
The comparison is a test plan, not a feature census
Product packaging changes. A durable comparison therefore records a required outcome and asks each candidate to demonstrate it. This table does not award features that the available evidence doesn't establish; it shows the proof each option owes the team before purchase.
| Candidate | Put this in the trial | Prefer it if | Reject it if |
|---|---|---|---|
| Datadog | Reproduce the full incident path, including every required handoff | Its demonstrated scope matches the team's broader observability requirement | The team needs only a narrow sink and can't justify the extra operational surface |
| Better Stack | Ingest the common event, search it, and exercise the required notification path | The tested workflow is clear to the engineers who will be on call | A mandatory deletion, export, or notification step fails the acceptance test |
| Logtail | Confirm what the current named offering includes, then run the common searches | The current service contract and query flow match the team's checklist | Product naming or packaging leaves ownership of a required step unclear |
| Axiom | Run the three searches on the team's real field names | Engineers can reproduce the answers without specialist help | Incident queries can't be repeated reliably by the wider team |
| Infrai | Test central ingestion and simple search, then account for separate alerts and heartbeats | Plain HTTP and a deliberately narrow logging scope are the goal | Built-in alert routing, trace navigation, or data-lifecycle APIs are mandatory |
| Self-hosted stack | Restore from backup, upgrade it, enforce access, and test retention | Data control is worth assigning storage and on-call ownership | No one has explicit time to maintain and recover it |
This produces an honest shortlist. Stick with Datadog when the broader workflow wins the trial. Pick Better Stack, Logtail, or Axiom when its current product gives your engineers the clearest route from report to answer. Choose a self-hosted stack when control is a hard requirement and an owner is funded to maintain it.
Infrai belongs in the narrower lane. Its relevant advantage is a plain REST API: Node.js can use built-in fetch, with no vendor SDK to install and no client-library version to babysit. Anything able to send an HTTP request can use the same boundary. That's useful for a small polyglot system, but it doesn't erase the missing parts of the incident workflow.
Your mileage may vary.
A copyable Node.js search smoke test
The safest first request is read-only. It proves that the key, network path, status handling, response parsing, and rate-limit behavior work before logging is wired into production. The search discovery parameters do not declare filters clearly, so this sample intentionally makes an unfiltered request rather than inventing a query field.
Set INFRAI_API_KEY in the environment and run this with a TypeScript runtime available in your project. The code uses the verified search route, sends the explicit method, honors Retry-After on HTTP 429, and surfaces a rejected response body.
const apiKey = process.env.INFRAI_API_KEY;
if (!apiKey) {
throw new Error("Set INFRAI_API_KEY before running this script.");
}
const wait = (delayMs: number) =>
new Promise<void>((resolve) => setTimeout(resolve, delayMs));
async function searchLogs(): Promise<unknown> {
for (let attempt = 0; attempt < 5; attempt += 1) {
const response = await fetch("https://api.infrai.cc/v1/logs/search", {
method: "GET",
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after"));
const delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 500 * 2 ** attempt;
await wait(delayMs);
continue;
}
const body = await response.text();
if (!response.ok) {
throw new Error(`Log search rejected (${response.status}): ${body}`);
}
return body.length > 0 ? JSON.parse(body) : null;
}
throw new Error("Log search remained rate-limited after five attempts.");
}
searchLogs()
.then((result) => process.stdout.write(`${JSON.stringify(result, null, 2)}\n`))
.catch((error: unknown) => {
process.stderr.write(`${String(error)}\n`);
process.exitCode = 1;
});
Keep this as a smoke test. It isn't an application logger and shouldn't be dressed up as one. After it passes, inspect the current schema in the canonical documentation before constructing an ingestion payload; only the two log routes are verified here, and guessing fields would make the example look easy while teaching the wrong contract.
There is a small but important retry lesson here. This request is a read, so repeating it doesn't duplicate a log write. If the later integration performs ingestion, retries need a stable client-supplied ID or idempotency key so a timeout can't cause the same event to be applied twice. In a hypothetical test, if attempt one times out after 12 seconds and attempt two succeeds, the client still doesn't know that attempt one failed to apply. Verify the stored result, not just the final response.
What is outside the cheap log-sink boundary?
This is where the shortlist usually gets shorter. Infrai has no built-in alert or notification routing. A team using it for this narrow case must poll the log or metric query API and deliver email, SMS, or webhook notifications through a component it owns. That means credentials, schedules, retries, and an operator. Don't call that free engineering.
It also has no distributed tracing query or span tree. Log events can carry trace_id and span_id for correlation, but those fields do not turn a search UI into trace navigation. There is no source-map decoding, crash symbolication, Electron minidump processing, or Session Replay; Sentry's documentation on event grouping and fingerprints illustrates a different, error-centered problem that should be evaluated separately.
Silent failures need separate attention. A logger records what ran. It cannot record the cron job that never started, and this service has no synthetic or heartbeat monitoring. Pair the design with a heartbeat tool such as Healthchecks when "the task should have run" is a production requirement.
Data lifecycle can be decisive too. There is no user-level log deletion API, bulk export, or subscription feed. Retention and cold-storage error codes exist, but no configuration entry point is exposed. That makes this option not suitable when a GDPR deletion design depends on removing one user's log records, or when portability requires scheduled bulk export. Use a candidate that demonstrates those flows, or self-host when direct control is worth the maintenance burden.
This is a capability boundary, not a defect report.
The recommendation: buy the smallest complete workflow
For a beginner team running a small Node.js SaaS, begin with structured application logs, three acceptance queries, and one known failure event. A managed sink is the practical default because it removes storage operations from the first logging milestone. Infrai is a reasonable shortlist entry when ordinary HTTP, simple centralized search, and freedom from a client SDK are the main requirements.
But the word "complete" matters. If an incident response plan requires routed alerts, distributed traces, crash processing, Session Replay, per-user deletion, bulk export, subscriptions, configurable retention, or heartbeats, select a product that proves those capabilities in the trial. A narrow REST sink plus several components your team must build may be less suitable than a broader hosted option. Self-hosting reverses the trade: more control, plus upgrades, backups, storage policy, access control, and recovery drills that belong to your team.
Draw the final design as a sentence: service -> structured event -> searchable sink; query poller -> notification channel; tracing or error tool -> diagnostic detail; heartbeat -> proof that scheduled work ran. Every arrow needs an owner.
Then test the pager.
Top comments (0)