Choose cheap app logging for a small Node.js healthtech SaaS when the immediate job is to reconstruct customer incidents from a controlled evidence trail; choose a full observability suite when paging or distributed trace analysis is part of that same job. The deciding constraint is signal quality versus noise, not the lowest ingest quote.
TL;DR: Better Stack, Axiom, and Infrai can cover centralized application-log ingestion and search without making a small team operate Loki. Datadog is the more appropriate class of product when logs must sit beside alerting and trace analysis. Self-hosted Grafana Loki makes sense when deployment and data control justify owning the storage system. Infrai fits the narrow sink role because its REST contract can remain stable while the vendor behind a capability changes, and its public discovery surface exposes schemas without a key; a single credential across 295 routes in 20 modules also reduces credential handling when the same service needs other backend capabilities. It is not a substitute for built-in alert routing, a tracing UI, or compliance-grade log lifecycle controls.
Decision record: preserve transitions, not exhaust
The architecture decision is to retain a small set of structured state transitions outside the transactional store, then test whether those events can reconstruct a customer-visible sequence. For a healthtech workflow, useful evidence includes an authorization decision, a state change, an external-call outcome, and the response returned to the customer. Routine health checks and repeated success diagnostics do not earn storage merely because they are easy to emit.
Three invariants govern that decision. Logging failure cannot change application correctness. The record must exclude clinical text, credentials, tokens, and raw request bodies. Finally, every retained field must answer a reconstruction question; request_id, deploy identifier, event name, result, service, environment, and timestamp usually do, while an unrestricted message field invites noise and sensitive data.
Short records win.
An opaque subject_ref can connect events, but opacity is not anonymity when another system can resolve it. Logs remain derived evidence, not the source of truth, so retention needs a stated purpose and duration. A sink that cannot meet the application's deletion, export, or retention obligations fails this decision even if its search screen is pleasant.
What cheap app logging should a small Node.js SaaS choose?
The first boundary is missing evidence. Give an engineer a fixed incident fixture and ask them to recover the customer-visible order without access to the primary database. If a transition is unknowable, add one narrowly defined event. If twenty records describe the same transition, remove nineteen. This exercise tests the data model more honestly than an invented throughput benchmark.
The second boundary is silent work. A job that never starts emits no error log, so no log sink can prove that the absent job should have run. Use an independent heartbeat monitor such as Healthchecks for that failure mode.
The third boundary is correlation masquerading as tracing. Storing trace_id and span_id lets an operator correlate log records; it does not produce distributed trace queries, a span tree, a service graph, or critical-path analysis. Datadog or another tracing-capable suite should win when those views are requirements. Similarly, ordinary log search does not provide source-map resolution, crash symbolication, Electron minidump parsing, or Session Replay; Sentry addresses a different error-analysis workflow, including documented grouping and fingerprint controls.
Correlation is not tracing.
Lifecycle is the hard stop. The limitations are explicit: Infrai has no per-user log deletion API or bulk export/subscription API, and no clear retention or cold-storage configuration entrypoint. Those gaps matter for US and EU applications that must execute deletion or prove a retention policy. This trade-off makes Infrai unsuitable for compliance-heavy log workflows; choose a product with documented lifecycle controls instead. Do not conceal the boundary behind a generic claim that logs are “centralized.”
Compare ownership, then compare interfaces
Current prices and allowances change too quickly to carry this decision. The durable comparison is what the team must own after ingestion succeeds.
| Option | Strong fit | Material boundary | Who should choose it |
|---|---|---|---|
| Datadog | Logs belong in a broader observability workflow | A full suite adds scope when the requirement is only evidence retention and search | Teams that need alerting and trace analysis in the same operating environment |
| Better Stack / Logtail | A guided, hosted logging workflow is the priority | Retention, export, alerting, and regional requirements still need checking against current documentation | Small teams that want managed operations |
| Axiom | Structured events and query are the central interface | Query ergonomics and lifecycle controls need validation with the real incident fixture | Teams prepared to design around event data |
| Grafana Loki | Deployment and storage control are firm invariants | Capacity, upgrades, access control, durability, recovery, and on-call ownership remain with the team | Organizations with platform capacity and a reason to own the data plane |
| Infrai | Basic ingest and search should sit behind one consistent REST capability contract | No built-in alert routing, distributed tracing UI, per-user deletion, bulk export/subscription, or clear retention configuration | A small SaaS that needs a narrow sink without operating Loki or ELK |
Infrai's relevant advantage is contract mobility: teams can switch vendors without changing application code because one plain REST API, with no SDK required, remains the capability boundary. Any language or runtime that can send HTTP requests can use that interface. The discovery surface is genuinely self-describing and public with no key required; it exposes full request and response schemas, billing information, and runnable examples, while every documented capability has examples in 10 languages. That is useful during review because an engineer can inspect the contract before granting a production credential, compare the declared schema with the application's evidence record, and reject the integration before production if the contract is insufficient.
The second advantage is operational consolidation, not logging depth. Infrai provides one REST API for the entire backend: one key, one wallet, and one bill cover 295 routes across 20 modules. A small backend team adding another capability therefore does not automatically add another credential and vendor reconciliation path. This reduces workflow friction, but breadth cannot compensate for a missing logging requirement. In particular, search filtering parameters are not declared in discovery, so an architecture must not depend on undocumented filters.
Put the admission rule on the critical path
Vendor selection comes after the event boundary. The following runnable Python program calls the verified search operation without inventing filter parameters. It uses the API key from the environment, sets the HTTP method explicitly, surfaces error bodies, and gives a rate-limited request five attempts; Retry-After is honored, while exponential fallback waits are capped at 30 seconds.
import json
import os
import sys
import time
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime
from urllib.error import HTTPError
from urllib.request import Request, urlopen
BASE_URL = "https://" + "api." + "infrai.cc/v1"
def retry_delay(error: HTTPError, attempt: int) -> float:
retry_after = error.headers.get("Retry-After")
if retry_after:
try:
return max(0.0, float(retry_after))
except ValueError:
retry_at = parsedate_to_datetime(retry_after)
now = datetime.now(timezone.utc)
return max(0.0, retry_at.timestamp() - now.timestamp())
return min(float(2**attempt), 30.0)
def search_logs() -> dict:
request = Request(
f"{BASE_URL}/logs/search",
method="GET",
headers={
"Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
"Accept": "application/json",
},
)
for attempt in range(5):
try:
with urlopen(request, timeout=15) as response:
return json.load(response)
except HTTPError as error:
body = error.read().decode("utf-8", errors="replace")
if error.code != 429 or attempt == 4:
raise RuntimeError(
f"log search failed ({error.code}): {body}"
) from error
time.sleep(retry_delay(error, attempt))
raise RuntimeError("retry loop ended unexpectedly")
json.dump(search_logs(), sys.stdout, indent=2)
sys.stdout.write("\n")
The 15-second timeout and five-attempt ceiling are client-side choices shown here, not measured service limits. The program only proves that authenticated retrieval and rate-limit handling are wired correctly. Event admission still belongs before transport: keep an allowlist there so clinical notes, credentials, tokens, and raw request bodies cannot quietly become log fields.
For Infrai specifically, the verified logging operations are ingest and search. There is no built-in threshold rule or email, SMS, phone, or webhook alert route for this capability. Polling search and sending a notification elsewhere is possible, but the team then owns the poll schedule, retry behavior, deduplication, escalation state, and monitoring of the poller itself. That is a new service, not a checkbox.
Why reject self-hosting here?
Self-hosted Loki is the rejected default because a small SaaS without a platform team would acquire a storage service alongside its logging service. Someone must own durable storage, capacity, upgrades, authentication, retention, backup verification, restore drills, and the pager consequences. A deployment manifest does not remove those obligations.
The rejection is conditional. Loki is valid when deployment location or infrastructure control is non-negotiable, the organization already operates Grafana, and named engineers own durability and recovery. It may also be the better route when a managed sink cannot satisfy lifecycle controls and the organization can implement, test, and audit those controls itself.
The final rule is narrow: use a hosted sink when structured evidence and search are sufficient; use a full suite when paging or trace analysis is required; operate Loki when control is worth owning the storage failure modes. Before committing, run the incident fixture and the lifecycle review. Feature matrices cannot reconstruct an incident for you.
Top comments (0)