Authoring Log Alerts
PostHogOfficialPublished by PostHog
- Skill
- Workflow
- Data and AI
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured, data-driven approach to creating log alerts that minimize noise while ensuring critical issues are captured. It moves away from guessing thresholds by using historical baseline analysis, simulation, and validation. The process involves triaging services, establishing statistical baselines, drafting thresholds using recommended defaults, and back-testing configurations before deployment. It emphasizes using automated tools to calculate thresholds rather than manual estimation, ensuring alerts are grounded in real production patterns.
What it does
- Identifies candidate services for monitoring based on log volume and error rates.
- Calculates data-driven alert thresholds using historical log distributions.
- Simulates alert configurations against 7 days of historical data to validate performance.
- Configures alert notification destinations such as Slack, webhooks, or Microsoft Teams.
- Provides a standardized workflow to avoid common pitfalls like noisy alerts or brittle filters.
When to use it
- When a user requests to set up or suggest new log alerts for their project.
- When evaluating whether a specific service generates enough signal to warrant an alert.
- When a user has recently enabled log alerting and needs a reliable starter configuration.
Advantages
- Reduces alert fatigue by using N-of-M logic and statistical baselines.
- Ensures alert reliability through mandatory simulation against historical data.
- Standardizes the alerting configuration process across different services.
- Prevents common configuration errors like overly broad filters or inappropriate thresholds.
Usage examples
- “Can you suggest some useful log alerts for my api-gateway service?”
- The agent will triage the service, analyze its 7-day baseline, simulate a draft alert, and present a validated configuration for the user to approve.
- “Set up alerts for all my services that are currently throwing errors.”
- The agent will identify the top services with error signals, perform baseline analysis for each, and propose a set of alerts with simulated fire counts.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/posthog/authoring-log-alerts/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip authoring-log-alerts.zip -d .agents/skills
Documentation by PostHog
Authoring log alerts
Authoring an alert is a measurement problem, not a guessing problem. You are not trying to be exhaustive — you are trying to land thresholds that fire 0–3 times per week on real production patterns, on services that matter.
When to use this skill
The user asks to "set up alerts" / "suggest alerts" for their project.
The user wants to evaluate whether a service is producing alertable signal.
The user has just enabled log alerting and wants a starter set.
When not to use this skill
Tuning an alert that already exists — that's a different job (use
posthog:logs-alerts-events-listto inspect fire/resolve cadence andposthog:logs-alerts-partial-updateto adjust).Investigating an active incident — pull rows with
posthog:query-logs, don't author an alert mid-incident.
Tools
| Tool | Job | Where it fits |
|---|---|---|
posthog:logs-services | Top-25 services in window with log_count, error_count, error_rate, sparkline. | Step 1 — triage. |
posthog:logs-attributes-list / posthog:logs-attribute-values-list | Discover keys/values for narrower filters. | Step 2, optional. |
posthog:logs-count-ranges | Adaptive time-bucketed counts for a filter. | Step 3 — baseline. |
posthog:logs-alerts-simulate-create | Replay a draft config against -7d history with full state machine. | Step 4 — validate. |
posthog:logs-alerts-create | Persist the alert. | Step 5 — ship. |
posthog:logs-alerts-destinations-create | Wire the alert to Slack, webhook, or Microsoft Teams. | Step 5: ship. |
Do not call posthog:query-logs during authoring. You need distributions, not rows. Reserve posthog:query-logs for
the very end if the user asks "show me a sample of what would have fired" — limit: 10 is plenty.
Workflow
1. Triage — pick candidate services
Call posthog:logs-services for the last 24h with no filters. The response is capped at 25 services and includes a
sparkline, so it is small and bounded.
A service is a candidate when both are true:
log_countis non-trivial (≥ ~1k in 24h — quieter services produce too little signal to alert on).error_rateis non-zero, or the user has named the service explicitly.
Skip services with high volume but error_rate == 0 unless the user wants a volume-shape alert (e.g. "warn me
if api-gateway suddenly stops producing logs"). Volume-floor alerts use threshold_operator: below and need
different reasoning — see references/volume-floor-alerts.md.
If the user names a service, treat it as a candidate even without error signal.
2. (Optional) Narrow the filter
If a service has many error sub-types, an alert on "all errors" is usually too broad. Use
posthog:logs-attributes-list (try attribute_type: log) and posthog:logs-attribute-values-list to find a discriminator —
common ones are http.status_code, error.type, k8s.container.name. Add the narrowing filter to your draft.
Keep it simple: one severity filter + one or two attribute filters is plenty. Multi-clause filters are harder to reason about and rarely improve precision.
3. Baseline — characterise the candidate over 7 days
Call posthog:logs-count-ranges with the candidate's filters, dateRange: { date_from: "-7d" }, and
targetBuckets: 24 (one bucket ≈ 7h). The response gives you bucket counts.
Do not eyeball the percentiles or scale the threshold to the alert window manually. Pipe the count-ranges response into the helper script:
echo '<count-ranges JSON>' | python3 scripts/baseline_stats.py --window-minutes 5The script returns:
{
"n_buckets": 12,
"bucket_minutes": 420.0,
"alert_window_minutes": 5,
"stats": { "p50": 12.0, "p95": 71.25, "p99": 126.25, "max": 140 },
"suggested_threshold_count": 5,
"rationale": "max(p99=126.25, median*3=36.0, floor=5) scaled from 420m bucket to 5m window",
"health": []
}Use suggested_threshold_count as your starting threshold. Read health:
health flag | What it means | What to do |
|---|---|---|
sparse:N_of_M_buckets | Too few non-empty buckets for a 7d baseline. | Widen filter, extend to -30d, or skip. |
empty | All buckets are zero. | Skip — no signal. |
spiky | max is 10×+ p95. | Count-threshold alerts work well. Proceed. |
flat | p95 ≈ p50. | Be cautious — either no incidents in lookback, or the metric is too smooth. Try a longer lookback or skip. |
[] (empty) | Healthy distribution. | Proceed. |
4. Draft and simulate
Pick a starter draft from these defaults — see references/threshold-defaults.md for the reasoning:
| Setting | Default | Notes |
|---|---|---|
threshold_count | suggested_threshold_count from the script | Already scaled to the alert window. |
threshold_operator | above | Use below only for volume-floor alerts. |
window_minutes | 5 | Allowed: 5, 10, 15, 30, 60. Must match what you passed to the script. |
evaluation_periods | 3 | M in N-of-M. |
datapoints_to_alarm | 2 | N in N-of-M. 2-of-3 reduces flap from a single noisy bucket. |
cooldown_minutes | 30 | Minimum time between notifications (fire or resolve). |
Call posthog:logs-alerts-simulate-create with these settings and date_from: "-7d". The response gives you fire_count
and resolve_count.
5. Iterate — three rounds, then ship or skip
Target: fire_count between 0 and ~3 over -7d. If outside the band:
| Outcome | Adjustment |
|---|---|
fire_count = 0 over 7d and the baseline was spiky | Lower threshold_count toward stats.p95 from the script, or drop to 1-of-2. |
fire_count = 0 and the baseline was flat | The service has no alertable signal. Skip it; log why. |
fire_count > 5 | Raise threshold_count toward stats.max from the script, or move to 3-of-5 for a smoother window. |
fire_count is fine but resolve_count never matches fire_count | Cooldown is too long, or the underlying state is genuinely sticky. Acceptable for now. |
When adjusting the threshold, read values from the script's stats block — never recompute percentiles
by hand.
Cap iteration at 3 simulate calls per candidate. If you can't land in the band in 3 rounds, the metric is wrong — either the filter is too broad, the window is wrong, or the service genuinely doesn't have a threshold-shape signal. Note it and move on.
6. Ship — create + attach destination
Once a draft simulates cleanly:
Call
posthog:logs-alerts-createwith the validated config. Use a name like<service> error rate (auto)so the user can see at a glance which alerts came from this skill.Call
posthog:logs-alerts-destinations-createto wire it to a notification target. An alert with no destination is silent. Supported destination fields:Slack:
type: "slack",slack_workspace_id, andslack_channel_id.slack_channel_nameis optional.Webhook:
type: "webhook"andwebhook_url.Microsoft Teams:
type: "teams"andwebhook_url.
Always confirm the channel name or webhook URL with the user before attaching. Never wire an auto-generated alert to a production channel without explicit confirmation. If the user is unsure, suggest a low-traffic testing channel for the first few alerts.
If the user wants alerts created in enabled: false state for review-then-flip, pass enabled: false to
-create and tell them how many drafts you produced.
Filter shape — required
The filters field on posthog:logs-alerts-create takes a subset of LogsViewerFilters and must contain at
least one of:
severityLevels— list of["trace","debug","info","warn","error","fatal"]serviceNames— list of service name stringsfilterGroup— property filter group
The same shape goes into posthog:logs-alerts-simulate-create's filters field. Match the simulate filters to the alert filters
exactly — otherwise the simulation is testing a different alert than the one you ship.
Example minimum:
{
"severityLevels": ["error", "fatal"],
"serviceNames": ["api-gateway"]
}Token-economy rules
One
posthog:logs-servicescall at the start, not per-candidate.One
posthog:logs-count-rangescall per candidate attargetBuckets: 24. Don't go above 30 during authoring.≤ 3
posthog:logs-alerts-simulate-createcalls per candidate.Zero
posthog:query-logscalls during the authoring loop.Prefer reporting a small set of well-validated alerts over a long list of unvalidated drafts.
Output
Report what you did, in this shape:
For each shipped alert: name, filters, threshold, simulated fire_count over 7d, destination.
For each skipped candidate: service name + why (flat baseline, can't land threshold, low volume).
Total simulate calls made, total alerts created.
The user should be able to read this and decide whether to disable any drafts before they go live.
Related skills
investigating-logs— characterize a service's baseline before alerting on it, and investigate firings afterauthoring-error-tracking-alerts— alert on exceptions rather than log lines
Files
- ATTRIBUTION.mdAdded by AutoGineer859 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md11.9 KiB
- references/threshold-defaults.md3.8 KiB
- references/volume-floor-alerts.md2.0 KiB
- scripts/baseline_stats.pyScript5.0 KiB
references/threshold-defaults.md
Threshold defaults — reasoning
The math here is implemented in ../scripts/baseline_stats.py. The agent
should not reproduce these calculations inline — pipe the posthog:logs-count-ranges response into the script and
read suggested_threshold_count. This section explains what the script computes and why, so you can
sanity-check its output and reason about edge cases.
threshold_count = max(p99, median × 3, floor) × (window_minutes / bucket_minutes)
The bucket-level threshold takes the max of three terms:
p99 of bucket counts. The 99th percentile of recent buckets is a data-driven "above normal" line. Works when the baseline has enough non-empty buckets to compute a percentile.
median × 3. Catches services where p99 is misleadingly close to the median (flat baselines), or where the lookback didn't include any spikes.
floor (default 5). Alerting on counts of 1 or 2 produces too much noise on small services. Below 5 matches/window, prefer a different alert shape (e.g. existence-based) or skip.
The bucket-level threshold is then rate-scaled to the alert window — a 7h bucket with a threshold of 1000 errors equals ~12 errors per 5-minute window. The script does this scaling; the rationale field shows the math.
The scaling assumes errors arrive uniformly within a bucket, which is rarely true — a real spike can pack
the entire bucket's count into a single 5-minute window. That's exactly why posthog:logs-alerts-simulate-create is the
final arbiter: it replays the alert state machine against actual per-minute history, not the rate-scaled
average.
window_minutes = 5
The default minimum. Reasons to go higher:
The service is bursty in 5-minute chunks but the trend over 30 minutes is what matters → use 30.
Notifications at 5-minute resolution would be too noisy for the user (e.g. expected periodic spikes during cron) → smooth with a 30 or 60 minute window.
Allowed: 5, 10, 15, 30, 60. Don't pick a value not on this list — the API rejects it.
evaluation_periods = 3, datapoints_to_alarm = 2 (2-of-3)
N-of-M is the cheap, high-signal way to dampen flap. 2-of-3 means: out of the last 3 check intervals, at least 2 must breach to fire. A single noisy interval doesn't trip the alert. A sustained problem still does.
When to deviate:
1-of-1 — fire instantly on a single bucket breach. Use only for incidents you cannot afford to delay (e.g. payments service erroring at all).
3-of-5 — smoother, slower. Use when the service has known short bursts that are not real incidents.
Higher than 5 — diminishing returns; if 5 buckets aren't enough signal, the threshold is wrong.
cooldown_minutes = 30
A logs alert notifies once when it starts firing and once when it resolves. It does not notify again on each check while it stays firing, whatever the cooldown. Cooldown sets the minimum time between any two notifications, so an alert that flaps around the threshold notifies at most once per 30 minutes.
Use 0 when every fire and resolve must notify, even for an alert that flaps (rare).
Avoid these footguns
threshold_operator: belowwithout justification. Below-threshold alerts measure absence — useful for "service stopped logging" but easy to misuse. If the service has any quiet hours (overnight, weekends), a below-threshold alert will fire at 3am every night. See volume-floor-alerts.md.Filtering by message text alone (
searchTermormessage icontains). Brittle to log format changes. Prefer a structured attribute (http.status_code,error.type) when one exists.Filtering by
trace_id/span_id. Not useful in alerts — these are per-request and never repeat at a rate that crosses a meaningful threshold.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- PostHog
- Publisher
- PostHog (Official)
- Source
- PostHog/skills · skills/omnibus/authoring-log-alerts · commit a5d30e7
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by PostHog.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download authoring-log-alerts.zip