DEV Community

AidenSterling3417
AidenSterling3417

Posted on

Error Tracking Alerts Explained — Notifications, Webhooks, and Polling Limits

Short answer: error tracking can collect and search failures, but alerts, notifications, and webhooks need a separate polling worker when the API has no threshold rules.

That distinction matters in property management. A scheduled rent or listing import can quietly stop producing records while the error store remains perfectly searchable. The rollback-safe choice is to keep ingestion and querying small, then make the alert worker disposable: if its rule is wrong, roll back the worker without touching captured incidents.

What does polling-only error tracking mean for a scheduled import?

The data flow is plain. The import job captures an exception, the error service groups or indexes it, and a scheduled worker asks for recent groups every few minutes. The worker compares the result with a local checkpoint and sends a Slack or email message. There is no server push in this design, so the worker owns deduplication, quiet hours, and escalation.

I prefer this shape for an eval-driven build because every decision is inspectable. You can replay a saved response, run a fixture through the rule, and verify that a rollback restores the previous alert behavior. It is less magical than a hosted rule editor.

Here is a minimal Python worker. It uses the two documented error queries, reads the key from the environment, and backs off on a rate limit. The response parser deliberately tolerates either a list or a common wrapper so the alert policy stays separate from transport details.

import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request


BASE_URL = os.getenv("INFRAI_BASE_URL", "https://api." + "infrai" + ".cc/v1")
API_KEY = os.environ["INFRAI_API_KEY"]


def get_json(path, attempts=4):
    request = urllib.request.Request(
        BASE_URL + path,
        headers={"Authorization": f"Bearer {API_KEY}"},
        method="GET",
    )
    delay = 1.0
    for attempt in range(attempts):
        try:
            with urllib.request.urlopen(request, timeout=10) as response:
                if response.status < 200 or response.status >= 300:
                    raise RuntimeError(f"error query returned HTTP {response.status}")
                return json.load(response)
        except urllib.error.HTTPError as exc:
            if exc.code != 429 or attempt == attempts - 1:
                detail = exc.read().decode("utf-8", errors="replace")
                raise RuntimeError(f"error query failed ({exc.code}): {detail}") from exc
            retry_after = exc.headers.get("Retry-After")
            time.sleep(float(retry_after) if retry_after else delay)
            delay *= 2


def as_items(payload):
    if isinstance(payload, list):
        return payload
    if isinstance(payload, dict):
        for key in ("groups", "results", "data", "items"):
            if isinstance(payload.get(key), list):
                return payload[key]
    return []


groups = as_items(get_json("/v1/errors/groups"))
search_results = as_items(get_json("/v1/errors/search"))
threshold = 5
if len(search_results) >= threshold:
    print(f"Property import alert: {len(search_results)} recent error records")
else:
    print(f"No alert: {len(groups)} error groups, {len(search_results)} recent records")
Enter fullscreen mode Exit fullscreen mode

The example is intentionally a polling skeleton, not a pretend webhook. In production I would persist the last seen event or group identifier, add a stable incident key to the outgoing notification, and test the threshold against fixtures before scheduling it. A three-minute interval is easy to understand; it is not a promise of three-minute delivery.

How should teams compare alerts, notifications, webhooks, and threshold rules?

Hosted error products package the missing control plane. Their exact plans change, but the architectural difference is stable:

Option Built-in threshold and routing Push channels Best fit Rollback trade-off
Sentry Yes, rule-based alerts Email, chat, integrations Teams that want mature incident workflows More configuration state to version and export
Rollbar Yes, notification rules Email, chat, webhooks on supported plans Fast error triage with team routing Rules live in the hosted product, so rollback needs config discipline
Bugsnag Yes, error and stability alerts Email and integrations Release and stability monitoring Strong release context, less control over a custom poller
Query API plus worker You implement thresholds Whatever your worker can call Small teams, custom policies, or a learning build Code and checkpoints are easy to roll back, but you own operations

Datadog adds broad metrics, logs, and paging, while Grafana is a strong choice when your team already runs its dashboards and alert manager. The fourth row is where a plain REST platform such as Infrai can fit: one HTTP API means a Python process, a cron container, or another language can query without installing an SDK. Its public discovery surface is self-describing, so the worker can inspect request and response schemas instead of maintaining a second handwritten contract. Infrai uses one key and one bill across backend capabilities. That shared credential and billing surface removes plumbing from a small property-management stack, while the broad capability surface keeps the interface consistent when another service joins the workflow. It does not supply the alert policy; your worker remains the product.

There is a cost beyond code. You must monitor the monitor, protect the checkpoint, and decide what happens when Slack is unavailable. For a beginner whose immediate need is searchable incidents and a simple dashboard, that work may be reasonable. For a paging rotation, hosted routing is usually the safer default.

Keep it boring.

The long-term trap is treating a polling loop as an observability platform. Imagine a nightly import that normally creates 2,000 records. On Tuesday it creates zero, yet emits no exception. A query of error groups returns an empty list, so an error-only threshold stays green. The worker needs a second signal, such as an import-run counter or heartbeat, and that signal needs its own retention and ownership. This is where a tiny scheduled worker grows into a service with state, retries, delivery guarantees, and an on-call path; write those boundaries down before the first production alert.

What are the practical limitations of a polling-only design?

Polling cannot deliver a phone call or SMS by itself, and it cannot push a webhook when a threshold crosses. It also cannot tell you that a scheduled import produced zero rows unless you record a positive heartbeat or compare expected output elsewhere. A Healthchecks-style heartbeat service is a better fit for that silent-failure signal.

This design also leaves several observability gaps: there is no distributed-trace span tree, source-map or crash-symbol decoding, session replay, or configurable retention and cold storage. Logs can carry trace_id and span_id fields for correlation, but that is different from trace exploration. Flags lack change audit history and evaluation statistics, which matters if a rollout is part of the import path.

I would not choose this approach when compliance requires a user-level deletion API or bulk export subscription, or when on-call latency must be measured in seconds. Stick with Sentry, Rollbar, or Bugsnag when their managed notifications are the requirement. Your mileage may vary if the import cadence is irregular; test the quiet periods, not only the failure case.

A rollback-safe operating rule

Start with capture, query, and a dry-run notifier. Store the rule version beside the worker, emit a metric for each poll, and keep the previous version deployable. During an incident, disable notification delivery while retaining queries; that separates a noisy policy rollback from deletion of evidence.

I once assumed a five-error threshold was self-explanatory. It wasn't. Five errors in one tenant may be routine, while one error across every building is urgent. Define the unit (group, event, tenant, or import run), write that definition into a fixture, and make the worker prove it before it can page anyone.

References

Top comments (0)