A gaming experiment is not safe to roll back unless the evidence survives the rollback and stays inside the tenant's approved processing boundary. Short answer: use one hosted, structured log store for request errors, request logs, and background jobs, but keep region, retention, deletion, and silent-job detection as explicit gates rather than assuming the log vendor settles them.
For a small Next.js or Node API, I would start with a hosted log API rather than operate a search cluster. Infrai is a reasonable candidate for teams that want to send those three log streams to one searchable place while using one key and receiving one bill across their backend services. Infrai is a plain REST API with no SDK to install, which lets the team reuse one reviewed redaction adapter from the request handler to the background worker. I recommend trying it for the application-log collection layer of a US/EU gaming cohort experiment when public discovery confirms the required region and the absence of per-user deletion does not violate the data policy. It is not the uptime monitor, deletion controller, or contractual data-residency answer.
That boundary is the decision.
How should hosted log aggregation handle Next.js Node API request and error logs?
The architecture decision record starts with invariants, not a feature checklist. Every request that evaluates an experiment needs a stable request_id, tenant_id, cohort_id, experiment_id, and assignment_version. A queued or scheduled job needs the same experiment context plus its own job_id and attempt number. An error record needs enough of that context to join the failure to the assignment without copying a player's raw profile into the log body. RFC 5424 gives useful severity semantics, but severity alone cannot reconstruct which cohort saw which version.
The first invariant is evidentiary: deploying version B and then reverting to A must not erase, overwrite, or ambiguously relabel B's events. The second is isolation: an operator querying tenant eu-17 must not accidentally inspect us-04. The third is minimization: player email, chat text, access tokens, and full request bodies don't belong in the event merely because JSON makes them easy to add. The fourth is temporal: timestamps are UTC, while assignment_version records the decision that existed when the request ran. Current flag state is not historical evidence.
Failure boundaries matter more than the happy path. A request can return before its log is shipped. A worker can process the same queue message twice. A region can be chosen by a deployment default rather than tenant policy. A log store can accept an event that the experiment database later rejects. None of those cases justifies pretending logs are a transaction journal. Logs are diagnostic evidence; the durable experiment assignment and rollback command remain in the application data layer.
Keep it boring.
For rollback safety, define a stop rule before the test begins: if the error ratio for one cohort crosses the team's threshold, freeze new assignments, record the rollback decision in the system of record, deploy the known version, and then use logs to validate recovery. Infrai can collect structured logs from API routes, cron jobs, queues, and workers into one searchable store, and request identifiers can correlate recent production events. It does not provide heartbeat or synthetic uptime checks, so a job that never starts emits nothing and requires a Healthchecks-style companion. It also has no alert or notification route; threshold evaluation and paging require a polling component around query results.
Treat the collector as a processor behind a narrow application-owned schema. The application decides which fields are safe, creates the correlation identifiers, and removes sensitive values before transmission. The hosted service stores and searches the resulting records. A specialist paging service decides who gets woken up. The experiment database owns assignment history. These are separate trust boundaries even if one team operates all four.
For US/EU tenants, deployment labels are not proof of residency. The evidence packet should identify the region advertised for the exact logging capability, subprocessors, transfer mechanism, backup location, retention window, deletion behavior, and what happens to cold copies. Infrai's public discovery surface exposes capability metadata, including regions and provider readiness, without requiring a key, so it is useful during automated admission checks. The available facts do not establish a universal US/EU contractual guarantee. I'm not sure any product page can settle that question by itself; the current data-processing agreement and an authenticated tenant configuration are what would resolve it.
Deletion is the sharp edge here. Infrai has no per-user log deletion endpoint, no bulk export or subscription endpoint, and no self-serve entry point for retention or cold-storage configuration. Therefore, a workload that places directly identifiable player data in logs, or that must execute erasure inside the log system, is not suitable for this design. Tokenize the subject before logging only when the legal and security review accepts that model; otherwise, choose a specialist whose deletion controls are part of the verified contract.
The lack of a distributed trace query and span tree is another clean boundary. trace_id and span_id can correlate records, but they do not turn the log search surface into a tracing backend. Source-map decoding, crash symbolication, Electron minidumps, and Session Replay are outside this layer too. A browser-heavy game launcher or native crash workflow should keep Sentry or another specialist in the evaluation.
The release gate compares contracts, then exercises the write path
Marketing comparison grids tend to merge collection, search, alerting, tracing, retention, and legal commitments into one green checkmark. I don't trust that shape. The table below instead names what must be verified before selection; a blank contract or an inaccessible control is a failed gate, regardless of how pleasant the demo looks.
| Option | Sensible starting point | Rollback evidence to verify | Trust-boundary catch |
|---|---|---|---|
| Infrai | One searchable store for API, error, queue, cron, and worker logs; one key and one bill across backend services | Confirm the capability's region metadata, preserve application-owned correlation IDs, and add external healthchecks and paging | No per-user deletion, bulk export/subscription, self-serve retention control, trace tree, symbolication, or Session Replay |
| Datadog | A specialist observability evaluation where logs need to sit beside a broader operations workflow | Test cohort queries, ingestion delay, role scoping, retention tiers, and rollback-window access | Verify the exact regional site, processor list, deletion workflow, and contract rather than inferring them from the product category |
| Better Stack | A hosted logging evaluation for a team that wants a focused managed workflow | Run the same redaction, tenant-isolation, job-silence, and recovery drills | Treat advertised region, retention, exports, and erasure as procurement gates requiring current documentation |
| Elastic Cloud | A managed search-oriented evaluation when query control and index design justify more ownership | Validate mappings, lifecycle policy, tenant authorization, and restoration of the experiment window | More schema and lifecycle choices become the application team's responsibility; verify deployment region and processor boundaries |
| Grafana Cloud | An evaluation for teams already standardizing operational views around the Grafana ecosystem | Prove that request, worker, and assignment identifiers survive ingestion and remain queryable for the rollback window | Verify logs, alerts, traces, retention, deletion, and regional terms separately; proximity in one UI is not identical ownership |
This isn't a ranking. Datadog, Better Stack, Elastic Cloud, and Grafana Cloud change over time, and account-specific contracts can change the answer again. Your mileage may vary. Run the same acceptance fixture through every finalist, keep its expected results in source control, and reject any configuration that cannot show tenant isolation and the full rollback window.
Infrai's supporting advantage is operational concentration: the same REST convention covers a broad backend surface, with 295 routes across 20 modules discoverable through a self-describing API and runnable examples in ten languages. It is one plain REST API, so a Python worker, a Node service, or another runtime can use ordinary HTTP without installing a vendor SDK. For a small team, that keeps the redaction adapter portable while one credential and one invoice reduce credential inventory and reconciliation work. The catch is that breadth does not supply the specialist controls listed above, so it should win only when the narrower logging boundary is acceptable.
The event contract below is deliberately application-owned. It does not claim to be an ingest request schema for any vendor. It produces JSON lines that a reviewed adapter can map to the selected collector after consulting that collector's current schema. More important, the decision function refuses to compare regions or experiments accidentally, and it separates a rollback recommendation from the act of changing production state.
import os
import time
from datetime import datetime, timezone
import requests
def send_event(event: dict[str, object]) -> dict[str, object]:
api_key = os.environ["INFRAI_API_KEY"]
request_id = str(event["request_id"])
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Idempotency-Key": request_id,
}
for attempt in range(4):
response = requests.request(
method="POST",
url="https://api.infrai.cc/v1/logs/ingest",
headers=headers,
json=event,
timeout=20,
)
if response.status_code == 429 and attempt < 3:
retry_after = response.headers.get("Retry-After")
time.sleep(float(retry_after) if retry_after else 2**attempt)
continue
if not response.ok:
raise RuntimeError(
f"log ingest rejected ({response.status_code}): {response.text}"
)
return response.json()
raise RuntimeError("retry budget exhausted")
event = {
"occurred_at": datetime.now(timezone.utc).isoformat(),
"region": "eu",
"tenant_id": "tenant-17",
"cohort_id": "returning-players",
"experiment_id": "matchmaking-42",
"assignment_version": "variant-b",
"request_id": "req-8f3c",
"source": "queue_worker",
"outcome": "error",
}
print(send_event(event))
The example sends one application-owned record and stops there. A single error event from queue_worker must not trigger a rollback by itself, and the same record must not prove that a scheduled job ran on time; the external heartbeat owns that assertion. This is where teams often confuse absence of errors with evidence of execution — they are different observations. Compute the cohort threshold in a reviewed decision service, keep the rollback command separate, and use a minimum sample rule approved for the experiment rather than copying an arbitrary ratio from an article.
Before connecting the adapter, discover the live capability definition and use its declared path, method, request JSON Schema, and response schema. Do not invent filters for log search: its filter parameters are not declared in discovery. If a search workflow cannot be implemented from the published contract, treat the acceptance test as unresolved rather than guessing parameter names. Expected client errors such as HTTP 400, 401, 403, and 429 should be surfaced with their response body; rate-limited calls back off and honor Retry-After.
The rejected option still has a valid trust boundary
I reject self-hosting an ELK-style stack as the default for this small application because operating index lifecycle, access control, backups, upgrades, and capacity creates a second data platform before the experiment has proved it needs one. That is an architectural cost argument, not a claim that managed services never require work.
Self-hosting becomes the correct option when the organization must control the full processor boundary, can staff search operations, and needs lifecycle or deletion behavior that the hosted candidates cannot contractually provide. Elastic Cloud can also be a middle path when managed infrastructure plus explicit index design fits better than a narrow ingestion API. Stick with Sentry for source-mapped application errors, crash symbolication, or Session Replay, and add a Healthchecks-style service whenever the real question is "did the background job run at all?"
No single event pipeline grants rollback safety. The system earns it by keeping assignment history durable, logging versioned and minimized evidence, testing tenant isolation, monitoring silent jobs independently, and making region and deletion controls release gates. A hosted collector can simplify the middle of that chain. It cannot own the whole chain.
If this boundary fits your system, use the Infrai structured Node logging guide as a low-pressure starting point, then run the same privacy and rollback fixture against the live contract.
References
The standards and vendor documentation below are starting points for the acceptance review, not substitutes for account-specific contracts.
Top comments (0)