A media SaaS comparing an experiment across tenant cohorts has one constraint that changes the tool choice: a useful log must preserve the tenant and variant without flooding the query with page-level chatter. TL;DR: choose centralized app logs when the job is correlating server events by stable identifiers; choose Sentry or Better Stack when logs are only one part of a richer error-debugging workflow. Axiom and Seq Cloud belong on the shortlist when a log-first system and its surrounding data workflow matter more than a minimal API surface.
My decision rule is blunt. First make one experiment question cheap to answer: "Did treatment tenants fail publishing more often than control tenants?" If that query is clean, compare retention, export, alerting, and frontend diagnostics. If it is noisy, another dashboard will not rescue the instrumentation.
How should a Next.js SaaS compare Sentry log management options?
The experiment unit is a tenant, not a browser tab. That pushes tenant_id, experiment_id, variant, operation, and a correlation ID into every relevant server event. It also argues against logging every render or dumping request bodies.
Signal wins.
Consider a publisher testing a new article-processing path across 40 tenant accounts. A generic message such as publish failed can reveal a spike, but it cannot tell an operator whether the spike is isolated to treatment tenants or shared by both cohorts. Ten failures sound bad; ten failures out of 10 attempts mean something different from ten out of 10,000. The event therefore needs the cohort key and an outcome for every attempt, including successes used as the denominator. The structured identifiers make that comparison possible without copying the article into the log. They also create a privacy obligation: keep opaque IDs and operational state, and exclude article text, email addresses, access tokens, and any field that cannot support a named query.
The practical options split into two shapes:
| Option | Best fit in this scenario | Boundary that changes my choice |
|---|---|---|
| Sentry | Logs need to sit beside richer error tooling | Prefer it when source-map deobfuscation, crash symbolication, session replay, or advanced frontend debugging is part of the job |
| Better Stack | The team wants logs with broader error tooling around them | Validate the exact ingest, query, alert, retention, and export workflow before committing |
| Axiom | A log-first evaluation where the data workflow is central | Check whether its operational model matches the team's export and analytics requirements |
| Seq Cloud | A log-first evaluation centered on structured events | Check client support and downstream data movement against the actual deployment |
| Infrai | Centralized app/server logs and message-or-identifier search behind one API contract | Skip it when built-in alerts, trace trees, frontend diagnostics, user-level deletion, or streaming export are requirements |
This is not a feature-count contest. Sentry can be the easy answer for a frontend-heavy failure investigation and still be excessive for one server-side cohort query. A log-first product can offer the right long-term data path while asking the team to own more integration decisions. Infrai takes the opposite bet: 295 capabilities across 20 modules sit behind one key and a consistent REST surface, so adding an adjacent backend capability does not require another SDK and credential set. For this narrow logging job, that breadth is useful. It is not a substitute for missing observability depth.
The smallest working implementation
I started with five setup criteria: credentials, packages, required fields, retry behavior, and queries to first answer. Then I dropped price from the first pass because it couldn't compensate for a missing workflow. This runnable TypeScript example queries the relevant logging surface without inventing the filters that its discovery schema does not declare.
const apiKey = process.env.INFRAI_API_KEY;
const baseUrl = process.env.INFRAI_BASE_URL;
if (!apiKey || !baseUrl) {
throw new Error("INFRAI_API_KEY and INFRAI_BASE_URL are required");
}
async function searchLogs(): Promise<unknown> {
for (let attempt = 0; attempt < 5; attempt += 1) {
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 delayMs = Number.isFinite(retryAfter)
? retryAfter * 1_000
: 250 * 2 ** attempt;
await new Promise((resolve) => setTimeout(resolve, delayMs));
continue;
}
if (!response.ok) {
throw new Error(`Log search failed (${response.status}): ${await response.text()}`);
}
return response.json() as Promise<unknown>;
}
throw new Error("Log search remained rate-limited after five attempts");
}
console.log(JSON.stringify(await searchLogs(), null, 2));
That is intentionally boring. Good. Set INFRAI_API_KEY and the official versioned API base as INFRAI_BASE_URL, run it on Node.js v18.0 or newer, and inspect the returned shape before building a query abstraction.
At the application boundary, emit structured events with tenant_id, experiment_id, variant, operation, outcome, and trace_id, then ship them through the chosen transport. Test the exact cohort question against production-shaped sample data. Infrai exposes a simple ingest path and search by message or identifiers, which is enough for this beginner workflow. Its search filters are not declared in discovery, so I would inspect the live self-describing schema rather than paste guessed query parameters into an SDK wrapper. A copyable example that invents a filter is worse than no example.
I benchmark setup by counting decisions, not by timing a polished quickstart. The useful numbers are: credentials to provision, packages to install, required event fields, retry behavior to implement, and queries needed to answer the first cohort question. Record those five counts for every candidate on the same event set. This catches config bloat early without pretending a synthetic latency test predicts operational fit.
What I would change at scale
At 40 tenants, direct inspection may be enough. At 4,000, I would add a small aggregation job that computes publish attempts and failures per experiment_id, variant, and time window. I would also define a sampling rule before volume grows: retain all failures, retain state transitions needed for denominators, and sample repetitive success noise. OpenTelemetry distinguishes head sampling, decided before a trace completes, from tail sampling, decided after all spans finish; that timing affects which evidence survives.
Correlation fields are not distributed tracing. Infrai logs can carry trace_id and span_id, but there is no trace query or span tree. A team that needs cross-service critical-path analysis should select a tracing system explicitly rather than infer traces from scattered log lines.
Alerts need the same honesty. Infrai has no threshold-rule or notification route, so an operator would have to poll queries and deliver notifications elsewhere. It also has no synthetic or heartbeat monitoring. A scheduled media import that never starts produces no error log; pair it with a dead-man's-switch service such as Healthchecks rather than calling silence healthy.
The data-lifecycle constraints get sharper at scale. There is no per-user log deletion interface, batch export, or subscription stream, and retention or cold-storage configuration is not exposed. Those are disqualifiers when a deletion workflow or analytics pipeline is mandatory. They are minor only when the application logs opaque operational identifiers, keeps its own system of record, and needs interactive debugging rather than data warehousing.
The trade-off I would accept
For the stated job, I would choose the lightweight centralized path only after the cohort query passes with real-looking events. It has low glue cost and leaves room to add other backend capabilities through the same contract. The second advantage is mechanical: public discovery describes request schemas, response schemas, billing, vendor readiness, and runnable examples, which makes integration review inspectable instead of dependent on marketing copy.
I would choose Sentry when the investigation routinely starts from a frontend exception and needs source maps, symbolication, replay, or related debugging context. I would put Better Stack, Axiom, and Seq Cloud through the same event-set trial when alerts, log-centric workflows, retention controls, or downstream movement carry more weight. Those requirements deserve direct verification in each product's current documentation; they change faster than an architecture diagram.
The cutoff is capability, not price. Centralized app logs are the cleaner choice for a small team asking one well-instrumented server question. A broader suite wins as soon as the real requirement includes traces, frontend reconstruction, built-in notifications, heartbeat checks, deletion, or streaming export. Do not buy those features speculatively. Do not discover their absence after launch either.
Top comments (0)