Skip to content

[vercel_otel] Add alert rule and SLO templates for server errors and Core Web Vitals - #20699

Open
giorgi-imerlishvili-elastic wants to merge 4 commits into
elastic:mainfrom
giorgi-imerlishvili-elastic:vercel_otel-alert-rule-and-slo-templates
Open

[vercel_otel] Add alert rule and SLO templates for server errors and Core Web Vitals#20699
giorgi-imerlishvili-elastic wants to merge 4 commits into
elastic:mainfrom
giorgi-imerlishvili-elastic:vercel_otel-alert-rule-and-slo-templates

Conversation

@giorgi-imerlishvili-elastic

@giorgi-imerlishvili-elastic giorgi-imerlishvili-elastic commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Proposed commit message

Add 10 alert rule templates and 5 SLO templates to the vercel_otel content package.

WHAT: The alert rule templates cover HTTP 5xx error rate, lambda/edge crash rate, elevated ERROR/WARNING log rate, regional error rate skew, firewall deny spikes, elevated TTFB p75, poor Core Web Vitals by route, request rate drop, page view rate drop, and sensitive audit activity spikes — all as .es-query rules using ES|QL. The SLO templates cover server-side error rate (occurrence budgeting) and LCP/INP/CLS/TTFB p75 (timeslice budgeting, rolling 30 days). Each template links to the relevant existing dashboard via artifacts.dashboards.

WHY: The package previously shipped dashboards only, so operators had no ready-to-enable detection (alerts) or reliability tracking (SLOs) for Vercel OTel data and had to hand-author ES|QL queries and thresholds from scratch. These templates encode SRE-informed golden-signal thresholds (errors, traffic, saturation, latency) so operators can enable them directly from Fleet. Closes elastic/observability-dev#5912.

Checklist

  • I have reviewed tips for building integrations and this pull request is aligned with them.
  • I have verified that all data streams collect metrics or logs.
  • I have added an entry to my package's changelog.yml file.
  • I have verified that Kibana version constraints are current according to guidelines.
  • I have verified that any added dashboard complies with Kibana's Dashboard good practices

Author's Checklist

  • Confirm the dashboard IDs referenced in each template's artifacts.dashboards (vercel_otel-logs, vercel_otel-audit-logs, vercel_otel-web-analytics) match this package's actual dashboard saved-object IDs.
  • Confirm the ES|QL thresholds in the alert rule templates (e.g. 2% 5xx rate, 100 firewall denies/10m) are reasonable tunable defaults rather than hard SLA commitments.
  • Confirm the SLO data_stream.dataset filter values (vercel.logs.otel, vercel.speedinsights.v1.otel) match the dataset names declared in manifest.yml's discovery.datasets.

How to test this PR locally

Executed by agent (this session):

  • elastic-package lint — passed cleanly.
  • elastic-package build — succeeded, produced build/packages/vercel_otel-0.3.0.zip.
  • Seeded ~1,300 synthetic documents across all 4 vercel_otel data streams on a local Elastic stack; created live instances of all 10 alert rule templates via the Kibana Alerting API and all 5 SLO templates via the SLO API. Confirmed all 10 rules transitioned to active with correctly-targeted alert instances (and correctly recovered as the data aged out of each rule's window), and all 5 SLOs computed real (non-NO_DATA) sliValues reflecting the seeded conditions.

Reviewer instructions:

  • elastic-package stack up, then elastic-package install --zip build/packages/vercel_otel-0.3.0.zip.
  • In Kibana, open the Vercel OpenTelemetry Assets integration → Assets tab, confirm 10 alerting rule templates and 5 SLO templates are listed alongside the 4 existing dashboards.

Related issues

Screenshots

screencapture-2026-08-13T10_29_52 355Z screencapture-2026-08-13T08_25_49 547Z

…Core Web Vitals

Add 10 alert rule templates (errors, traffic, saturation, latency, and
audit governance) and 5 SLO templates (server-side error rate and Core
Web Vitals p75) with dashboard links, so operators have ready-to-enable
detection and reliability tracking for Vercel OTel data on top of the
existing dashboards.
@giorgi-imerlishvili-elastic
giorgi-imerlishvili-elastic requested a review from a team as a code owner August 13, 2026 10:34
@giorgi-imerlishvili-elastic giorgi-imerlishvili-elastic added the enhancement New feature or request label Aug 13, 2026
@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Elastic Docs Style Checker (Vale)

Summary: 1 suggestion found

💡 Suggestions (1): Optional style improvements. Apply when helpful.
File Line Rule Message
packages/vercel_otel/_dev/build/docs/README.md 69 Elastic.Wordiness Consider using 'all' instead of 'All of '.

The Vale linter checks documentation changes against the Elastic Docs style guide. To use Vale locally or report issues, refer to Elastic style guide for Vale.

Point the 0.3.0 changelog entry at elastic#20699 now
that the PR has been opened, replacing the placeholder link.
Comment thread packages/vercel_otel/docs/README.md Outdated
| **[Vercel OTel] Web Analytics** | Traffic and engagement overview covering page views, geography, devices, top pages, and custom events. |
| **[Vercel OTel] Speed Insights** | Web Vitals performance overview tracking LCP, INP, CLS, and TTFB at the p75 benchmark over time and per page. |

## Alert rules

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be generated dynamically from template

## Alerting Rule Templates
{{alertRuleTemplates}}

Comment thread packages/vercel_otel/docs/README.md Outdated

All rules use ES|QL queries and are filtered to `deployment.environment.name: production`. Thresholds are conservative defaults — adjust them to match your traffic volume and SLA.

## SLO templates

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be generated dynamically from template

## SLO Templates
{{sloTemplates}}

"params": {
"searchType": "esqlQuery",
"esqlQuery": {
"esql": "FROM metrics-vercel.speedinsights.v1.otel-default\n| WHERE deployment.environment.name == \"production\"\n AND metrics.TTFB IS NOT NULL\n| STATS\n ttfb_p75 = PERCENTILE(metrics.TTFB, 75),\n samples = COUNT(*)\n// Minimum sample size, since Speed Insights data is sampled\n| WHERE samples >= 20\n// \"Poor\" threshold per the Web Vitals spec table (milliseconds); lower toward 800-1800 for a stricter,\n// earlier-warning threshold aligned with the \"needs improvement\" boundary\n| WHERE ttfb_p75 > 1800.0"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"params": {
"searchType": "esqlQuery",
"esqlQuery": {
"esql": "FROM metrics-vercel.speedinsights.v1.otel-default\n| WHERE deployment.environment.name == \"production\"\n// Evaluate LCP, INP, and CLS independently per route, then union the results (FORK branches must share a schema)\n| FORK\n (WHERE metrics.LCP IS NOT NULL\n | STATS p75 = PERCENTILE(metrics.LCP, 75), samples = COUNT(*) BY route = COALESCE(http.route, url.path)\n // \"Poor\" threshold per the Web Vitals spec table (milliseconds); adjust to your own SLA if stricter\n | EVAL metric = \"LCP\", poor_threshold = 4000.0)\n (WHERE metrics.INP IS NOT NULL\n | STATS p75 = PERCENTILE(metrics.INP, 75), samples = COUNT(*) BY route = COALESCE(http.route, url.path)\n | EVAL metric = \"INP\", poor_threshold = 500.0)\n (WHERE metrics.CLS IS NOT NULL\n | STATS p75 = PERCENTILE(metrics.CLS, 75), samples = COUNT(*) BY route = COALESCE(http.route, url.path)\n // CLS is unitless; \"poor\" threshold per the spec table\n | EVAL metric = \"CLS\", poor_threshold = 0.25)\n// Minimum sample size per route/metric, since Speed Insights data is sampled\n| WHERE samples >= 5\n| WHERE p75 > poor_threshold\n// Combine route + metric into a single grouping key since the rule can only group alerts by one field\n| EVAL group_key = CONCAT(route, \" - \", metric)\n| SORT p75 DESC\n| LIMIT 20"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"params": {
"searchType": "esqlQuery",
"esqlQuery": {
"esql": "FROM logs-vercel.logs.otel-default\n| WHERE vercel.log.source == \"firewall\"\n// vercel.proxy is a flattened field; sub-fields aren't queryable via dotted notation, use FIELD_EXTRACT instead\n| EVAL waf_action = FIELD_EXTRACT(vercel.proxy, \"waf_action\")\n// \"firewall\" source includes log-only entries too; isolate actual blocks\n| WHERE waf_action == \"deny\"\n| STATS denies = COUNT(*)\n// Alert threshold: tune based on your application's normal WAF deny baseline.\n// Consider grouping by client.address instead of a flat total if you want per-attacker alerting.\n| WHERE denies > 100"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing WHERE deployment.environment.name == \"production\"\n which is present in other alerts

"params": {
"searchType": "esqlQuery",
"esqlQuery": {
"esql": "FROM logs-vercel.auditlog.v1.otel-default\n// Fall back through identifying fields since non-user actors (app/system/integration) lack user.* fields\n| EVAL actor = COALESCE(user.email, user.name, user.id, vercel.actor.type)\n// Sensitive action categories: env var access, drain/webhook config (log exfiltration risk), token issuance,\n// team/domain configuration, and project ownership transfers. Extend this list as needed.\n| WHERE event_name LIKE \"*env-variable*\"\n OR event_name LIKE \"*drain*\"\n OR event_name LIKE \"*webhook*\"\n OR event_name LIKE \"*token*\"\n OR event_name LIKE \"*team-*\"\n OR event_name LIKE \"*domain*\"\n OR event_name LIKE \"*project-transfer*\"\n| STATS sensitive_events = COUNT(*) BY actor\n// Alert threshold: tune based on your team's normal operational volume of sensitive actions\n| WHERE sensitive_events > 5\n| SORT sensitive_events DESC\n| LIMIT 20"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing WHERE deployment.environment.name == \"production\"\n which is present in other alerts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed for vercel_otel-firewall-deny-spike.json (logs-vercel.logs.otel-default), which does have this field.

For this one specifically I held off: logs-vercel.auditlog.v1.otel-default doesn't carry deployment.environment.name — I checked _field_caps against a live cluster and it's not part of that data stream's mapping (audit events are team/account-level governance actions — env var reads, token/webhook/domain changes, etc. — not tied to a specific deployment or environment). Adding the filter throws Unknown column [deployment.environment.name] (verification_exception) rather than just returning no rows, so it would break the rule outright. The pre-existing Audit Logs dashboard doesn't reference this field either, for the same reason.

Happy to add it if I'm missing something about how this field gets populated for audit events — let me know.

…docs

- Generate Alerting Rule Templates / SLO Templates sections in docs/README.md
  dynamically via {{alertRuleTemplates}}/{{sloTemplates}} instead of hand-
  maintained tables, matching the convention used elsewhere.
- Use TS instead of FROM for Speed Insights queries (TSDB data stream),
  matching the existing dashboard; adjust COUNT(*) to COUNT(<field>) since
  the TS command doesn't support COUNT(*).
- Add the missing deployment.environment.name == "production" filter to the
  firewall deny spike alert, consistent with its sibling alerts on the same
  data stream.
@elastic-vault-github-plugin-prod

Copy link
Copy Markdown
Contributor

✅ All changelog entries have the correct PR link.

@infra-vault-gh-plugin-prod

Copy link
Copy Markdown

💚 Build Succeeded

History

cc @giorgi-imerlishvili-elastic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants