Short answer: capture every exception at the shared server boundary, attach only bounded rollback context, and sample repetitive success telemetry before shortening error retention.
For a B2B SaaS team releasing a new pricing rule behind a flag, the least complex useful setup has three parts: one exception-capture function, one small event schema used by API routes and server actions, and one rollback view grouped by release and flag variant. The objective isn't to collect the richest possible incident record. It is to preserve enough evidence to answer one urgent question: should this pricing rule stay on?
Start with the bill. Error tracking cost is approximately event count multiplied by average encoded bytes, retention duration, and any indexing or query multiplier imposed by the storage design. Cardinality changes the other side of the equation: it determines how many distinct groups the team must index, scan, and reason about. A production setup that ignores either term can be technically correct and still be operationally unaffordable.
What should a Next.js API routes and server actions error tracking setup capture?
Capture the exception class, a scrubbed message or stable error fingerprint, operation name, deployment identifier, pricing-rule identifier, flag variant, and a pseudonymous request correlation identifier. That is enough to compare the candidate rule with the control and connect a failed server action to the relevant API request without turning each customer, invoice, or request into a permanent metric label.
Do not put raw account IDs, invoice IDs, email addresses, stack traces, or request IDs into metric labels. Prometheus instrumentation guidance explicitly warns against labels with high cardinality, such as user IDs and email addresses, because each distinct label set creates another time series. Keep low-cardinality dimensions in metrics. Put detailed, access-controlled diagnostic context in the error event, after redaction, and use the correlation ID to find it.
This division matters during rollback. A counter grouped by operation, release, rule, variant, and a bounded error_class can show whether failure rates diverge. The corresponding sampled exception event can carry a scrubbed stack and correlation ID for diagnosis. Metrics answer “is the candidate worse?” Events answer “where did it fail?” Trying to make either data type do both jobs raises cost and often makes the rollback signal harder to read.
The capture boundary should be shared even when the framework entry points differ. Wrap the body of every API route and every server action in the same policy: catch an exception, normalize it, submit the event, then preserve the application's intended failure semantics. Don't let telemetry code choose the customer-facing response or convert an expected validation result into an exception. Validation failures need a bounded outcome counter; unexpected failures need exception capture.
Keep it narrow.
For this pricing rollout, a practical event contract might contain timestamp, operation, release, rule, variant, error_class, fingerprint, and correlation_id. The contract deliberately excludes plan price, customer name, complete request body, and arbitrary flag payloads. Those fields add bytes, privacy exposure, and unbounded dimensions without improving the first rollback decision.
Count bytes before choosing retention
Consider a capacity-planning example, not a benchmark. Suppose the two server boundaries receive 12 million calls in 30 days. If 0.4% produce captured exceptions, that is 48,000 error events. At an assumed 3.5 KB after encoding, the raw error payload is about 168 MB for the month before replication, indexing, and storage overhead. Keeping those errors for 30 days is unlikely to dominate the bill.
Now add a verbose 2 KB “action completed” event to every successful call. The same arithmetic produces roughly 24 GB of raw success events, more than 140 times the raw bytes of the exception stream. The useful change is obvious: stop retaining routine success events, or sample them aggressively, before trimming the comparatively small failure record needed for rollback. Your mileage may vary because encoding, traffic shape, indexing, and storage architecture determine the real multipliers; measure encoded event size and observed volume in your own pipeline.
| Stream | Illustrative monthly count | Assumed size | Raw volume | Retention decision |
|---|---|---|---|---|
| Unexpected exceptions | 48,000 | 3.5 KB | 168 MB | Keep through the rollback and investigation window |
| Routine successes | 11,952,000 | 2 KB | about 24 GB | Prefer counters; sample only for a stated analysis |
| Pricing validation outcomes | 12,000,000 | counter increments | storage-dependent | Keep bounded labels; never label by account or invoice |
The table's numbers are inputs to a model, not claims about a particular service. Recalculate them from four measurements: calls per day, exception rate, encoded bytes per event, and required retention days. Then add the storage system's documented indexing and replication factors. I'm not sure which of those factors dominates in an unfamiliar deployment; a seven-day measurement of ingested bytes by stream resolves that uncertainty better than estimating from JSON source length. Cardinality needs its own budget alongside that byte calculation. Five rule names, two variants, eight operations, six releases, and ten bounded error classes permit 4,800 combinations before other dimensions. Adding one label containing a million possible account IDs changes the design qualitatively: a bounded operational comparison has become a customer-level index whose growth follows the business rather than the number of releases. The theoretical product isn't a forecast that every combination will exist, and inactive combinations generally should not be treated as stored series. It is a review alarm. Write every proposed dimension and its maximum credible value count beside the schema, multiply the bounded counts, and challenge any value set that grows with customers or requests. Those dimensions belong outside metric labels even when they look convenient during the first small rollout, because the same schema will outlive that rollout and accumulate releases, rule names, and exception classes.
Make rollback evidence survive partial failures
Exception capture must be on the execution path, but delivery should not become a second reason for the request to fail. Give the telemetry submission a finite time budget and a bounded local or process queue appropriate to the deployment model. If synchronous delivery is required for a narrow class of financial events, document that decision separately; don't silently impose it on every route and action.
Use one correlation ID across the route, action, error event, and response metadata that is safe to expose. Generate it at the first trusted server boundary when an acceptable one is absent. Avoid putting the ID in a metric label. It is a lookup key with request-level cardinality, so it belongs in event storage and logs with controlled retention.
Test the rollback path as a behavior, not as a dashboard screenshot. The following shell probe represents two requests to a pseudonymous production-like endpoint: one request uses the control, and the other activates the candidate pricing rule. The endpoint and payload are illustrative; substitute the actual route and a non-customer fixture in a staging or controlled verification environment.
curl --fail-with-body --silent --show-error \
--request POST \
--header 'content-type: application/json' \
--header 'x-correlation-id: pricing-probe-control-001' \
--header 'x-pricing-variant: control' \
--data '{"fixture":"renewal-standard"}' \
https://example.invalid/api/pricing/preview
curl --fail-with-body --silent --show-error \
--request POST \
--header 'content-type: application/json' \
--header 'x-correlation-id: pricing-probe-candidate-001' \
--header 'x-pricing-variant: candidate' \
--data '{"fixture":"renewal-standard"}' \
https://example.invalid/api/pricing/preview
The verification should prove that an unexpected exception produces exactly one normalized event, preserves the correlation ID, identifies the release and variant, and does not include the submitted body. It should also prove that an expected validation rejection increments the bounded outcome counter without entering the exception stream. Finally, exercise flag rollback and verify that new calls select the control while already captured candidate events remain queryable for comparison.
There is a catch: a correlation-based design is not suitable when legal or audit policy requires durable, complete business-event records. In that case, keep an append-only audit stream with its own schema, access policy, and retention schedule; do not stretch error tracking into an accounting ledger. Likewise, stick with aggregate counters rather than event sampling when every individual success is irrelevant and only a service-level rate is needed.
Sampling without blinding the rollout
Sample routine successes first.
For exceptions, begin with complete capture during the small rollout window, then consider deterministic sampling only for a repetitive fingerprint after its volume is understood. Deterministic sampling by stable fingerprint keeps comparisons reproducible, while random event sampling can make a rare but important class disappear from a short interval.
Protect the rollback dimensions from sampling. The control and candidate variants need comparable denominators, so count all invocations and all bounded outcomes even if detailed success events are absent. Retain unexpected exception events long enough to cover rollout, rollback, and the team's realistic investigation delay. A 30-day default has no special authority; the correct duration follows the operating calendar and incident process.
What do we deliberately stop keeping? Routine success payloads, raw request bodies, customer-level metric labels, arbitrary flag metadata, and duplicate copies of the same exception at every layer. The loss is real — after an incident, the team may be unable to reconstruct every successful request or explore an unanticipated customer-level segment. Accept that loss only after the bounded counters, redacted exception events, release markers, and audit obligations are covered. This is the central trade-off: preserve rollback evidence, not ambient detail.
Top comments (0)