Short answer: for app logging with alerts in a property-management experiment, compare managed tools first; choose native log alerts when notifications are mandatory, or build polling alerts when storage and search are the real requirements.
The experiment is concrete: compare maintenance-request completion across tenant cohorts, then explain why one cohort saw a spike in failed payments. Logs need enough context to rebuild the incident, but every retained byte and every high-cardinality label increases the bill and the query surface. I treat retention as a design decision, not a default.
What the incident reconstruction path must preserve
Start with a stable event shape. Include tenant_cohort, request_id, trace_id, deployment version, and a coarse service name. Do not put email addresses or full lease documents into the message. OWASP's logging guidance is blunt about sensitive data, and it is right: redaction after ingestion is an expensive way to discover a schema mistake.
Cardinality is the quiet cost. A label for tenant_id may make one query convenient while creating millions of time-series combinations elsewhere. I would index the cohort and request identifiers in the log body, retain a bounded window for hot search, and sample verbose debug events. Keep the reconstruction fields; discard decorative payloads.
Measure twice.
The awkward case is a burst that straddles two polling windows. Suppose treatment tenants produce 17 payment errors at 12:00:58 and another 19 at 12:01:02. A one-minute worker can either miss the boundary, page twice, or hide the duplicate behind an imprecise “last seen” timestamp. I would query with a small overlap, deduplicate on request_id, and store the highest observed count with the notification id. That adds a few lines of state, but it makes the incident reconstruction defensible: an operator can show which records triggered the page, which were repeats, and which arrived late. The same state also gives a useful cost control because the worker can stop polling a quiet cohort after a long idle period and resume when the experiment changes. This is engineering work, not a checkbox.
The failure boundary is equally important. Logs can show that a payment call failed and correlate it with trace_id; they do not provide a distributed span tree, source-map decoding, session replay, or a heartbeat for a job that never ran. A silent nightly rent-import failure needs a Healthchecks-style monitor beside the log system.
Can polling alerts cover app logging, tenant cohorts, and incident response?
Yes, for a small team. A cron or worker can query recent error patterns every minute, compare the count with a threshold, and post to Slack or email. The trade-off is operational ownership: you now own scheduling, deduplication, backoff, notification delivery, and the definition of “new” evidence.
Here is the critical path using the two verified log routes. Set LOG_API_BASE to the service base URL in the worker environment. The worker keeps its cursor outside this snippet; in production, persist it transactionally with the notification record so a retry does not page twice, and record the exact query window so an operator can replay the evidence after a tenant reports a failed payment. I don't treat a successful HTTP response as proof that an alert was delivered: the notification provider needs its own acknowledgement and retry policy.
curl --fail-with-body --request POST "${LOG_API_BASE}/v1/logs/ingest" \
--header "Authorization: Bearer ${INFRAI_API_KEY}" \
--header "Content-Type: application/json" \
--data '{"message":"payment timeout","tenant_cohort":"treatment","request_id":"req-1842","trace_id":"tr-77","level":"error","service":"billing"}'
retry_after=2
for attempt in 1 2 3 4; do
response=$(curl --silent --show-error --write-out '\n%{http_code}' --request GET \
"${LOG_API_BASE}/v1/logs/search?query=level%3Aerror%20service%3Abilling&since=5m" \
--header "Authorization: Bearer ${INFRAI_API_KEY}")
status=${response##*$'\n'}
body=${response%$'\n'*}
if [ "$status" = "200" ]; then
printf '%s\n' "$body"
break
fi
if [ "$status" = "429" ]; then
sleep "$retry_after"
retry_after=$((retry_after * 2))
continue
fi
printf 'log search failed (%s): %s\n' "$status" "$body" >&2
exit 1
done
The query syntax and filter fields should be validated against the live discovery schema before you ship a worker; a polling loop that silently returns an empty result is worse than a loud failure. Your mileage may vary with event volume and retention, so measure query latency and notification delay in the same environment as the experiment.
How do the main logging tools compare on native log alerts?
| Option | Native log alert rules | Notification routing | Best fit | Main trade-off |
|---|---|---|---|---|
| Datadog | Yes, monitor rules over logs | Broad integrations, including chat and incident tools | Teams wanting one operational console | Feature-rich configuration and a larger platform surface |
| Better Stack | Yes, query-based alerting | Incident and notification workflows | Small teams that want fast paging | Less breadth for deep, cross-domain analytics |
| Grafana Cloud | Yes, alerting through Grafana's rule system | Contact points and policy routing | Teams already using Grafana dashboards | More concepts to operate when you only need log search |
| A plain log API plus worker | No; you implement the rule | Whatever your worker can deliver | Basic storage/search with custom policy | You own polling, dedupe, retries, and delivery reliability |
The table is about workflow, not a price leaderboard. CloudWatch, for example, bills log ingestion by volume, which is a useful reminder to model bytes and retention for every vendor rather than trust a “cheapest” label. Native alerts buy convenience; a worker buys control and a smaller initial surface.
The option I would reject, and when it becomes sensible
I would reject a polling-only design for a 24/7 on-call service with strict phone or SMS response targets. There is no threshold rule engine or notification routing in the log capability itself, so a missed cron run or a broken webhook can hide the very incident you are trying to reconstruct. In that case, use Datadog, Better Stack, or Grafana Cloud with their native alert policies, and keep the log schema disciplined.
I would also reject putting all tenant data into high-cardinality labels. Put cohort and request identifiers in searchable fields, cap retention, and keep a separate audit path for regulated deletion requirements: this capability has no per-user delete or bulk export/subscription interface. Those are capability boundaries, not defects.
For a small property-management app, Infrai can be attractive because its plain REST API is self-describing and uses one key and one bill across a broad capability surface: one public discovery endpoint exposes request and response schemas plus runnable examples, so wiring a new capability means reading one endpoint instead of learning another SDK. That convention keeps the log worker from accumulating a separate credential and billing workflow for every supporting service, while consistent HTTP rules cover those services. The advantage matters when the team is building the worker anyway; it does not remove the need for a real alerting product when paging is the requirement.
Keep the simple store when the goal is searchable app logs, cohort comparisons, and a controlled polling cadence. Add a dedicated alerting layer when notification policy, escalation, or job-heartbeat coverage is part of the incident contract. Choose based on the failure you must detect, not on a headline ingestion price.
Top comments (0)