DEV Community

UrielDonovan6839
UrielDonovan6839

Posted on

Node.js App Logging Platform Comparison: Reconstructing Freight Incidents with Hosted Logs

Short answer: for a small Node.js logistics product, start with hosted logs when the job is reconstructing customer incidents; choose Datadog for deeper alerting and trace exploration, or self-hosted ELK when operating the stack is an intentional part of the business.

Choice Setup burden Incident reconstruction fit Main trade-off
Simple hosted log API Low Good when structured events carry shipment, request, and trace identifiers You may have to build alerts and correlation yourself
Datadog Medium Strong when logs must connect to advanced alerts and trace exploration More platform than a small team may need
Self-hosted Elastic Stack (ELK) High Strong when the team needs direct control of the logging stack Patching, storage, and operations stay with your team
Better Stack Low Worth evaluating as another hosted logging option Compare its current workflow against your evidence checklist
Grafana Loki Medium to high Worth evaluating when you already operate the surrounding stack Self-management can compete with feature work

My default is the first row. A solo SaaS has to ship weekly, and hours spent tending logging are hours not spent fixing the customer workflow. The deciding test isn't the longest feature list. It's whether one support ticket can be turned into a trustworthy timeline.

Can hosted app logging reconstruct a junior Node.js developer's customer incident?

Compare the evidence you can recover, then compare the work required to keep that evidence available. For a logistics incident, I want to answer a compact set of questions: which shipment was involved, what request initiated the change, which state transition happened, which external handoff followed, and what the application knew at that moment. A dashboard is useful only after those facts exist in the log stream.

This changes the usual platform comparison. Datadog-class products make sense when advanced alert routing, trace exploration, and ecosystem integrations justify the added platform surface. Self-hosted ELK makes sense when control of the stack is a requirement and somebody has time to operate it. A simpler hosted service wins when fast setup and low operational burden matter more than those enterprise features. Run a reconstruction drill before comparing dashboards: hand a developer only the customer report and the identifiers support would really have, then ask for a timestamped account of the shipment change, the initiating request, the downstream handoff, and the recorded outcome. Write down every point where the developer has to guess. Those gaps are requirements; a longer feature list is not.

Start there.

Keep the evidence model portable. Emit JSON from the application, use stable field names, and avoid placing the only copy of an important business transition in a dashboard annotation. I would also keep raw customer secrets and unnecessary personal data out of logs. This is especially important here because the hosted option described below has no per-user log deletion route or bulk export/subscription interface, while retention and cold-storage configuration are not exposed.

I'm not sure any vendor's default retention will match your policy without a written retention requirement. Decide that requirement before ingestion, not during an erasure request.

The evidence test is first. Owner time is second. Self-hosting transfers control to you, along with setup and maintenance; for a one-person company, that bill is paid in interrupted product hours. Hosted logging outsources the undifferentiated storage and query work. Good trade. It becomes a bad trade when compliance, export, or deep observability requirements exceed the hosted service's boundary.

Every event that matters should carry a timestamp, event name, shipment identifier, request identifier, and an outcome. Add trace_id and span_id when they already exist, but understand what that buys: manual correlation through fields, not a distributed-tracing span tree. Ordering also needs thought. Two services can report close timestamps, so a domain sequence or transition version is more useful than pretending arrival order is business order.

Infrai is one plausible simple-hosted choice because one API key and one bill cover 295 routes across 20 modules behind a consistent REST contract; adding another supported capability is another plain HTTP integration, with no SDK required. Its public discovery surface describes request schemas, response schemas, billing, and runnable examples. For this logging decision, though, the catch is material: log-pattern alerts require polling search results and adding your own notification step, and trace correlation remains field-based.

That last sentence is the decision line. Don't buy breadth when the missing depth is the part your incident process depends on.

Put the reconstruction contract in TypeScript

The application should create the reconstruction record before a transport or vendor enters the picture. Use a client-supplied event ID, an explicit schema version, and both request and trace identifiers when producing events. The retrieval side below calls the verified Infrai log-search route. Its filtering parameters are not declared, so the request deliberately sends none; inventing shipment_id as a query parameter would make the sample look convenient and teach an unsupported API. Set INFRAI_BASE_URL to the service's v1 API root and keep the key in INFRAI_API_KEY.

const apiKey = process.env.INFRAI_API_KEY;
const baseUrl = process.env.INFRAI_BASE_URL;

if (!apiKey || !baseUrl) {
  throw new Error("Set INFRAI_API_KEY and INFRAI_BASE_URL");
}

const wait = (milliseconds: number) =>
  new Promise((resolve) => setTimeout(resolve, milliseconds));

async function searchLogs(attempt = 0): Promise<unknown> {
  const response = await fetch(`${baseUrl}/logs/search`, {
    method: "GET",
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (response.status === 429 && attempt < 4) {
    const retryAfter = Number(response.headers.get("retry-after"));
    const delay = Number.isFinite(retryAfter)
      ? retryAfter * 1_000
      : 500 * 2 ** attempt;
    await wait(delay);
    return searchLogs(attempt + 1);
  }

  if (!response.ok) {
    const body = await response.text();
    throw new Error(`Log search failed (${response.status}): ${body}`);
  }

  return response.json() as Promise<unknown>;
}

searchLogs()
  .then((result) => process.stdout.write(`${JSON.stringify(result, null, 2)}\n`))
  .catch((error: unknown) => {
    process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
    process.exitCode = 1;
  });
Enter fullscreen mode Exit fullscreen mode

Run it, inspect the returned shape, and rehearse the actual support question: given shipment shp_8142, can an engineer recover its transition and connect it to request req_01JAB4Q9 without opening three systems? Because server-side search filters are undeclared, production incident tooling must inspect the discovery contract rather than assume a filter. If the answer depends on a field that wasn't emitted, fix the event contract first. More tooling won't recreate evidence that never existed.

This is deliberately plain.

Reject the easy setup when the runner-up owns the missing evidence

Stick with Datadog when alert routing, trace exploration, or a large integration ecosystem is central to the incident workflow. If the on-call response must move directly from a log pattern to a trace tree, a simple hosted log search plus manual trace_id correlation is not suitable. Datadog is the stronger fit on that axis even if setup is not the smallest.

Choose self-hosted ELK when the business needs direct control over storage, retention, data movement, or stack-level customization and can fund the operational work. That usually means logging has become an owned subsystem rather than a utility. For a junior developer working alone, I would require a concrete policy or product requirement before accepting that maintenance load.

Better Stack deserves a separate trial if a hosted-first workflow appeals but you need a different feature balance. Grafana Loki deserves one when Grafana operations are already normal work for the team. The available evidence here doesn't establish which of those two will satisfy a specific retention, export, or alert contract, so verify those requirements in their current documentation and run the same shipment-reconstruction drill. A trial passes only when the junior developer can start from the customer report, locate the relevant shipment events, explain their order, connect the request identifiers, and identify any missing execution signal without privileged platform knowledge. This is a deliberately narrow acceptance test. It measures the job the logging system was hired to do.

Also add a dedicated heartbeat service such as Healthchecks when the incident is “the scheduled task never ran.” Logs cannot report an execution that never started. Source-map decoding, crash symbolication, Electron minidump parsing, and Session Replay are separate needs too; this simple hosted choice does not cover them.

Write one representative incident narrative and test each candidate against it. Time how many systems must be consulted, list every identifier needed for the join, and identify who owns each missing alert or retention control. Don't turn the exercise into a generic feature census — the revenue-per-hour question is whether the platform shortens a real investigation without creating a second product to operate.

For a small logistics SaaS, the hosted option remains my starting point. Move to Datadog when advanced investigation features become requirements. Move to self-hosted ELK when control becomes a requirement and the maintenance has a named owner. The platform can change later; a disciplined, portable event record is the part worth getting right now.

References

Further reading

Top comments (0)