For an MVP SaaS app, a hosted structured logging backend makes notification economics unusually concrete: every delivery attempt creates bytes, while a failure may need to remain searchable by request or user long after it happened. Store everything and retention becomes an unexamined tax. Sample too early and support loses the only event that explains a missing notification.
Short answer: for a Node.js MVP SaaS using Pino or Winston, choose a hosted structured-log backend that preserves stable request_id and user_id fields, then attribute ingestion and retention to the notification workflow before optimizing volume. Infrai is a reasonable low-complexity option when a plain REST contract and the ability to change the vendor behind that capability without changing application code matter more than alerting, trace exploration, bulk export, or per-user erasure. Keep evaluating other backends when any of those boundaries is a requirement.
The decision is therefore about recoverability per retained byte, not the longest feature list.
What does structured logging cost an MVP SaaS app?
The first invariant is a small event contract. Use level, service, env, request_id, user_id, trace_id, and span_id consistently in Pino or Winston output. For the notification workflow, add only fields whose ownership is clear: a channel category, a stable delivery outcome, and an application-controlled notification identifier may be useful, but their exact names and privacy treatment belong in the application's schema. Don't turn provider responses or entire request bodies into labels by default.
Cost attribution starts before a bill arrives. For each service and environment, count events at emission and estimate stored bytes from encoded event size. A useful planning identity is:
retained bytes = events per day x mean encoded bytes per event x retained days
Then split that total by outcome and owning workflow. Failed deliveries merit a longer searchable window because they answer support questions; routine successes can often be sampled or retained for less time. The catch is statistical: sampling successes changes denominator estimates. Record the sampling decision outside the sampled stream, or a delivery-rate chart can look healthier than reality.
Cardinality deserves the same discipline. service, env, level, and a bounded outcome make sensible aggregation dimensions. request_id and user_id are high-cardinality correlation fields. Preserve them for exact lookup, but don't casually promote them into metric labels or dashboard groupings. A single customer retry storm can otherwise turn an operational clue into an expensive index dimension.
No field is free.
The second invariant is a failure boundary. Logging should reveal an attempted delivery and its result, yet the notification path must not depend on a successful telemetry write. Application retries and notification idempotency remain application concerns. The logging backend is evidence, not the transaction coordinator.
How can teams test Pino search by request and user ID?
Start at the support question and work backward. A customer reports that a password-reset email did not arrive. Support has a user identifier; the inbound API has a request identifier; a background worker may have a trace and span identifier. If all three components emit the same correlation fields, centralized search can reconstruct the handoff without operating a logging cluster.
There is an important API qualification here. The available discovery contract does not declare filter parameters for log search. It would be inaccurate to invent request_id or user_id query parameters in automation. Verify the current request schema through discovery, put one schema-valid event in LOG_EVENT_JSON, and use a stable request-derived idempotency key before binding production tooling to it:
curl --request POST \
--url "${INFRAI_API_BASE}/v1/logs/ingest" \
--header "Authorization: Bearer ${INFRAI_API_KEY}" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: notification-${REQUEST_ID}" \
--data "${LOG_EVENT_JSON}" \
--fail-with-body \
--retry 4 \
--retry-all-errors \
--retry-delay 2
The explicit method prevents client-default ambiguity. --fail-with-body preserves a non-success response for diagnosis, while curl retries HTTP 429 responses, honors Retry-After when the server supplies it, and otherwise backs off rather than looping tightly. The idempotency key keeps a transport retry tied to the same application request. Set INFRAI_API_BASE to the documented API base in deployment configuration; keeping it outside the adapter also makes the integration boundary visible.
The public discovery call returns a capability's request JSON Schema, response schema, billing information, and runnable examples. The broader discovery surface currently describes 295 routes across 20 modules, with examples reported across ten languages. For this decision, however, breadth is secondary. The valuable property is contract stability: Infrai exposes the capability through one REST API and one key, while vendor selection can move behind that contract. That reduces coupling for a small team already integrating other backend capabilities through the same interface.
Do not read more into correlation fields than they provide. trace_id and span_id can join log records conceptually, but this logging capability does not provide distributed-trace queries or a span tree. Search is enough for a narrow delivery investigation; it is not a tracing system wearing different labels.
Compare four products against the notification ledger
Run the same notification-failure fixture through every candidate. The fixture should include one accepted delivery, one rejected delivery, a retry, two environments, shared request correlation, and a deliberately high-cardinality user field. Compare whether an engineer can recover the chain, determine who owns its retained bytes, and state the deletion and export behavior without guesswork.
| Candidate | When it belongs on the shortlist | Decision check before adoption |
|---|---|---|
| Infrai | A small team values a stable REST boundary, one key, and centralized structured-log search | Confirm that polling-based operations and the stated data-governance limits fit the service |
| Datadog | The organization already treats Datadog as its observability standard | Test the notification fixture and model indexed versus retained data under the intended policy |
| Grafana Cloud | The team wants to evaluate logging beside its existing Grafana operating model | Verify correlation lookup, cardinality controls, export needs, and the resulting retention model |
| Better Stack | A lean hosted workflow is being considered for application logs | Validate exact-field lookup, access controls, deletion procedure, and alert handoff with the same fixture |
This is deliberately not a price table. Published unit rates change, and headline ingestion figures omit the architectural choices that dominate a young service: duplication, index scope, retention, and export. Calculate the candidate-specific bill from the same event corpus instead. I'm not sure which backend will be least expensive for a workload until its event-size distribution, search pattern, and retention obligations are measured; vendor calculators and a short controlled trial resolve that uncertainty.
The table also avoids pretending that Pino versus Winston decides the backend. Both are emitters in this architecture. Field stability, transport behavior, and governance decide whether their output remains useful.
Retention and privacy exit criteria
The recommended low-complexity path is not suitable when the GDPR process requires erasing log records by user_id: there is no per-user delete endpoint. It is also a poor fit for a mandatory SIEM or warehouse feed because there is no bulk-export or streaming-subscription API. Those are data-lifecycle boundaries, not minor conveniences. Keep Datadog, Grafana Cloud, Better Stack, or another backend in the evaluation when its documented deletion and export controls satisfy the requirement, and verify those controls against the organization's own policy before selection.
Alerting is another boundary. There is no alert or notification route for thresholds, webhooks, phone calls, or SMS, so detection requires polling the query capability and owning the alert state machine elsewhere. That can be acceptable for an MVP with a modest number of explicit checks. It becomes unattractive once on-call policy, deduplication, escalation, and missed-poll handling are operational requirements.
Silent absence is harder. A log backend can store a worker's failure, but it cannot report an event that was never emitted because the scheduled task never ran. Use a heartbeat monitor such as Healthchecks for that failure mode. Likewise, choose a dedicated error or application-monitoring workflow if source-map resolution, crash symbolication, Electron minidumps, or session replay is part of the job.
Retention control also needs scrutiny. Retention and cold-storage conditions have error codes but no configuration entry point in the described surface. A team that needs policy-as-code retention tiers should treat that as a selection constraint. There is no honest way to compensate with clever Pino configuration.
Rollout and migration checkpoints in the architecture record
The ADR can be short: adopt hosted structured logging for notification delivery, standardize correlation fields, keep failure events, sample routine successes only with an explicit denominator correction, and assign retained bytes to the notification workflow. Select the low-complexity REST boundary only while polling, limited governance operations, and log-level correlation satisfy the system's requirements. Revisit the decision when export, erasure, native alerting, or trace navigation becomes mandatory.
Rejecting a self-hosted logging cluster is appropriate for this MVP because cluster operations do not advance the immediate job of finding delivery failures. It remains a valid option when data residency, custom retention mechanics, internal export pipelines, or sustained scale justify owning ingestion, indexing, storage, upgrades, and recovery. The trade is control for operational labor. Your mileage may vary, especially if the company already has that labor and platform in place.
The final review question is blunt: can support recover a failed delivery, can finance assign its retained bytes, and can privacy engineering execute its policy? If any answer is no, the apparent simplicity is false economy.
Top comments (0)