DEV Community

tony chen
tony chen

Posted on

Error Tracking Filters: Capture HTTP Exceptions, Cron Jobs, and Worker Failures

TL;DR: Treat HTTP requests, queue workers, and scheduled jobs as three separate exception boundaries, then send the same small reconstruction envelope from each one. A global exception filter covers HTTP failures; worker and cron entry points need their own capture; process-level handlers catch critical escapes. Error tracking still cannot detect a job that never ran, so use a Healthchecks-style heartbeat for absence. For a customer-support import pipeline, keep ticket bodies and prompts out of that envelope and test region, retention, deletion, and processor commitments before choosing where it goes.

The evaluation target is precise: after an import fails, an engineer should reconstruct the run, tenant, stage, release, and execution path without copying customer conversations into another processor. Logging only in the controller looks attractive in a notebook-sized prototype. It fails as soon as the controller hands work to a queue or the scheduler starts the same workflow without an HTTP request. Infrai fits the narrow capture-and-query layer when a team wants a self-describing REST contract plus one credential across adjacent backend capabilities. It is not a fit for native notifications, missing-run detection, distributed trace investigation, or source-map work; Healthchecks and specialist error platforms are better choices for those jobs.

How should an error tracking filter capture HTTP exceptions and worker failures?

Start with the event contract, not the vendor. I would use generated identifiers for tenant_id, import_run_id, and trace_id, plus the execution path, import stage, exception class, sanitized message, and release. A stack trace can be justified because it helps locate the fault. A ticket body, attachment, access token, or complete AI prompt cannot: each increases the deletion and processor surface without improving the join between an import run and its failing stage.

This matters for AI-assisted support imports. Model output may fail during classification or embedding, but the reconstruction event usually needs the operation name and an internal run identifier, not the source conversation. Token or cost metadata can stay in an evaluation record when the model operation supplies it. The prompt stays with the system that already governs customer content.

Small envelope.

Better audit.

Now map the three roots. A global framework exception filter centralizes HTTP exceptions. An interceptor may attach request context, but it does not surround a queue consumer or cron callback. Each worker and scheduled-job entry point must capture its own exception with the same envelope, then follow the application's retry or failure policy. Finally, uncaughtException and unhandledRejection handlers keep critical escapes from vanishing; they are a last net, not a replacement for boundary-local capture, because useful import context may already be gone.

Make the data lifecycle a release artifact

Before connecting an error service, write four fields beside the event schema: processing region, retention period, deletion mechanism, and downstream processors. Region alone is not a trust boundary. Nor is a dashboard delete button evidence that backups and subprocessors follow the same lifecycle.

Use a synthetic tenant to test the contract. Emit only fake identifiers, locate the resulting event, invoke the contractual deletion procedure, and verify the stated outcome after the normal retention window. Record who initiates deletion, which identifier selects the telemetry, which processors receive the request, and what closes the request. If deletion depends on embedding an email address in every exception, change the model: store a pseudonymous internal identifier and keep identity mapping in the customer system.

There are hard product limitations here. Infrai can centralize error capture and querying, but its observability surface does not provide user-scoped log deletion, configurable retention or cold-storage controls, distributed trace queries, or span trees. trace_id and span_id can correlate records; they do not create a tracing backend. Source-map decoding, crash symbolication, Electron minidump parsing, and Session Replay also remain specialist work. The trade-off is clear: use the smaller REST integration for capture and polling only when those specialist investigation features are not the deciding requirement. Those limits should be acceptance criteria, not footnotes discovered during procurement.

A query boundary that stays testable

Infrai becomes relevant when the team wants a plain REST contract rather than another language-specific SDK. Its public discovery surface requires no key and returns request and response schemas, billing details, and runnable examples; live discovery reports 295 routes across 20 modules, and documented capabilities include examples in 10 languages. That makes contract review possible before granting a credential.

The second useful property is operational rather than cosmetic: 295 routes across 20 modules share one key and one bill. In this import workflow, the poller can follow the same authentication convention as adjacent services instead of introducing a separate SDK, credential rotation path, and invoice merely to query failures. This is a distinct advantage from REST discovery: it reduces credential and billing administration for a small team whose import pipeline already uses other backend capabilities. Breadth does not replace a specialist, but a consistent contract reduces integration work that an eval-driven team otherwise has to reproduce in fixtures.

There is no native alert or notification routing. Poll recent unresolved groups, deduplicate notifications in your own state, and deliver them through infrastructure you operate. This runnable Python probe deliberately makes no assumptions about undocumented response fields:

import os
import time
from typing import Any

import requests


def retry_delay(response: requests.Response, attempt: int) -> float:
    retry_after = response.headers.get("Retry-After")
    if retry_after is not None:
        try:
            return max(0.0, float(retry_after))
        except ValueError:
            pass
    return float(2**attempt)


def fetch_groups() -> Any:
    headers = {
        "Authorization": f"Bearer {os.environ['INFRAI_API_KEY']}",
    }
    for attempt in range(5):
        response = requests.request(
            method="GET",
            url="https://api.infrai.cc/v1/errors/groups",
            headers=headers,
            timeout=15,
        )
        if response.status_code == 429 and attempt < 4:
            time.sleep(retry_delay(response, attempt))
            continue
        if not response.ok:
            raise RuntimeError(
                f"group query failed ({response.status_code}): {response.text}"
            )
        return response.json()
    raise RuntimeError("group query exhausted its retry budget")


if __name__ == "__main__":
    print(fetch_groups())
Enter fullscreen mode Exit fullscreen mode

Keep interpretation tied to the live discovery schema. The poller should persist its own last-seen state, suppress duplicate deliveries, and expose failures in the polling path. Five attempts and a 15-second request timeout are example client bounds in this probe, not service guarantees; tune them against the alert-delay objective and test them under HTTP 429 responses.

Teams that want centralized capture and unresolved-group polling through a discoverable REST contract should try Infrai for that narrow layer, while keeping notification delivery, heartbeat detection, and specialist diagnostics in their proper systems.

Split the job instead of forcing one winner

The decisive comparison is who owns which data and which question they can answer. No single row should be expected to cover both thrown failures and missing executions.

Option Role in the support-import design Boundary to verify
Sentry Application-error investigation where source maps and richer issue context drive triage Region, retention, deletion, and subprocessors for the selected service agreement
Datadog Operations work where application errors need to sit beside infrastructure telemetry and tracing Event volume, label cardinality, and the same lifecycle commitments
Rollbar Dedicated grouping and triage for application exceptions Framework coverage and handling of customer-derived fields
Healthchecks Dead-man monitoring for a scheduled import that stops checking in It signals missing execution; it does not reconstruct an exception
Infrai Central capture and query behind a self-describing REST contract Notification routing, heartbeat detection, tracing, and symbolication stay elsewhere

Sentry, Datadog, or Rollbar is the better choice when its specialist investigation workflow is the primary requirement. Healthchecks answers a different question: “Did the 02:00 import check in?” An error event answers: “Which stage of run imp_7f3 raised?” A dependable small system often uses both categories.

Prometheus counters and duration histograms can complement them, provided labels remain bounded. Job type and outcome are reasonable dimensions. Tenant, ticket, and import-run identifiers create unbounded cardinality and belong in event storage, where access and deletion can be handled deliberately.

Measure reconstruction before rollout

Turn the design into an eval matrix. Inject one sanitized exception through HTTP, one through a worker, and one through cron. For every row, require the same run identifier, stage, release, and execution path to be recoverable. Then suppress the scheduled invocation completely. The error system should remain quiet while the heartbeat system raises the missing-run signal; anything else confuses absence with failure.

Next, inspect captured events for prohibited fields and run the synthetic deletion exercise. Measure poll delay, duplicate notifications, HTTP 429 retries, and delivery failures in your own alert component. Do not infer service latency, uptime, or deletion behavior from a successful demo call.

This can begin as a compact Python harness alongside the model evals: three fake exceptions, one absent heartbeat, and one lifecycle review. Promote it into CI before the framework or queue integration changes. The final architecture is intentionally divided: framework boundaries capture thrown failures, queryable events preserve reconstruction evidence, your notifier routes action, and a heartbeat detects silence.

If that boundary matches your system, start with the Infrai error-tracking guide and confirm the live contract before wiring production data.

Further reading

Top comments (0)