Short answer: for a junior developer running a small Node.js business, start with hosted logs when the priority is the easiest setup and a clear bill; choose a self-hosted ELK-style stack only when control, retention, or query customization justifies operating it. Treat Datadog as a broader observability option, not an automatic answer to an app logging problem. The choice should be made against one test: can the team detect that a scheduled import stopped producing results, identify the affected tenant, and explain the cost of the investigation?
The comparison is really about operations
“Hosted logs vs Datadog vs self-hosted ELK” sounds like a feature checklist. For a small business, the first decision is less glamorous: who owns the work after the first log line arrives?
A hosted logging platform owns more of the ingestion, storage, indexing, and search machinery. That is usually the easiest path for a junior developer because the application can emit structured records while the team spends its time on the import workflow. The trade-off is a service boundary: retention, export behavior, access controls, regional handling, and the usage bill need to fit the business.
Datadog can make sense when logs belong beside metrics, traces, dashboards, and incident workflows that a team already uses. It can also be more system than a small Node.js service needs. “More features” is not the same as “easier setup” when the real requirement is finding one failed import and assigning its cost to a customer or job.
Self-hosted ELK gives the team control over the deployment and data path. It also gives the team the deployment. Someone must plan Elasticsearch capacity, ship logs into the stack, manage access, decide retention, watch disk pressure, and keep the search experience usable. The software may be available without a hosted subscription, but the operator time is still part of the cost.
That is the first useful distinction: hosted logs buy less operational ownership; self-hosted ELK buys more control; a broader platform buys more integrated signals. None of those properties proves that one choice is correct.
Start with the incident, not the dashboard.
What should a junior developer choose for Node.js app logging?
Use the smallest architecture that preserves the fields needed for an investigation. For the scheduled import scenario, every event should make these questions answerable without guessing:
- Which import job emitted this record?
- Which tenant or store did it belong to?
- Was the job started, completed, or rejected?
- How many records were expected and produced?
- Which deployment and code version handled it?
Plain text can carry some of this information, but structured JSON makes the fields explicit. A logger adapter keeps application code independent from the transport, which makes a hosted destination, a self-hosted collector, or a later change easier to evaluate.
type ImportLog = {
event: "import_started" | "import_completed" | "import_failed";
jobId: string;
tenantId: string;
produced?: number;
expected?: number;
release: string;
at: string;
};
function writeImportLog(record: ImportLog): void {
process.stdout.write(`${JSON.stringify(record)}\n`);
}
writeImportLog({
event: "import_completed",
jobId: "orders-2026-08-11",
tenantId: "store-42",
expected: 1200,
produced: 1198,
release: "2026.08.11-1",
at: new Date().toISOString(),
});
The important design decision is not the exact logger package. It is the stable event shape. Keep secrets, tokens, payment data, and unnecessary personal data out of the record. Give each job a correlation field, and make a missing completion event detectable rather than relying only on an exception.
A small experiment beats a broad feature matrix
The failed approach is to compare product pages before defining the incident. That produces a long list of ingestion methods and dashboard features, then leaves the developer unsure what to deploy. I would run one focused evaluation instead.
Send the same 24-hour sample of import events through each candidate architecture. Include a successful import, a partial import, a rejected import, and a job that emits a start event but no completion event. Ask a second person to answer three queries from the resulting records: “Which tenant was affected?”, “What changed in the latest release?”, and “How much log volume came from this job?” Record setup time, query time, missing fields, and the work required to remove or export data.
The cost attribution test matters here. A single undifferentiated application log stream hides whether a noisy tenant, a retry loop, or a verbose release generated the bill. Add tenantId, jobId, release, event, and an approximate payload class before comparing ingestion totals. Do not treat a lower invoice as proof of a lower total cost if somebody is manually maintaining the cluster.
For example, imagine that the 02:00 import starts normally, writes 1,200 expected rows into its log context, and then loses its database connection after 1,198 writes. A useful record can show the tenant, release, attempt, and partial result; a useful alert can tell the team that completion never arrived. If the retry runs three times, the records should preserve that attempt history without making the team infer it from timestamps. When the bill arrives, the same fields should let the team separate one tenant's unusually chatty retry loop from ordinary traffic. That is a small amount of schema work, but it changes the comparison from “which search page feels nicer?” to “which system preserves the evidence and lets us attribute the work?”
Your mileage may vary. The result depends on event volume, retention, compliance requirements, and how much existing infrastructure the team can operate. I’m not sure any generic comparison can predict those inputs accurately, which is why this small test is more useful than a universal “best platform” label.
What are the failure modes after the easiest setup?
The easiest first deploy can still create a weak incident trail. Here are the failure modes I would check before calling the system finished:
- The import writes a success message before the database commit. A crash then leaves an optimistic log with no durable result.
- Retries reuse the same message without an attempt number. Search results look like many imports when there was one troubled job.
- A tenant identifier is missing from background work. The team can see that imports stopped, but cannot attribute impact.
- A retention rule deletes the evidence needed for a customer question. “Searchable today” is not the same as “available for the required investigation window.”
- Alerting watches errors only. A scheduled import can produce zero results without throwing an error, so the absence of a completion event or a zero-result metric needs its own check.
The logging contract should be tested in CI with representative events and checked at the deployment boundary. In production, alert on the business condition: no completed import for the expected interval, or produced results below the known threshold. Logs explain the event; a metric or heartbeat makes the absence visible.
The appender model is a useful mental model here: application code emits an event, while an appender or transport decides where that event goes. Keeping that boundary explicit prevents a logging destination from becoming a hidden dependency throughout the codebase.
The decision rule for a small business
Choose hosted logs when the team has few operators, wants searchable Node.js logs quickly, and can accept the provider's retention and export boundaries. It is not suitable when strict data residency, custom storage control, or offline investigation is a hard requirement.
Choose a self-hosted ELK-style stack when the team already operates search infrastructure, needs control over the data path, and will assign a real owner to upgrades, capacity, security, and retention. Stick with hosted logs when that owner would otherwise be the junior developer during a customer incident.
Choose a broader observability platform when correlated logs, metrics, traces, and alert workflows are genuinely required in the same operating model. Do not choose it only because the product list is longer.
Before committing, measure four things with the import experiment: time to first useful search, time to reconstruct a missing completion, total operator hours, and cost attribution by tenant or job. Put the findings beside the retention and export constraints. Then choose the option whose assumptions your team can actually keep true.
Top comments (0)