<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title><![CDATA[Incident Management - Elastic Observability Labs]]></title>
    <description><![CDATA[Trusted security news & research from the team at Elastic.]]></description>
    <copyright><![CDATA[© 2026. Elasticsearch B.V. All Rights Reserved]]></copyright>
    <image>
      <title><![CDATA[Incident Management - Elastic Observability Labs]]></title>
      <url>https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltad972c1c27dbefc6/6a88d9782904ea5e8511d473/observability-labs-thumbnail.png</url>
      <link>https://www.elastic.co/observability-labs/blog/category/incident-management</link>
    </image>
    <link>https://www.elastic.co/observability-labs/blog/category/incident-management</link>
    <atom:link href="https://www.elastic.co/observability-labs/rss/category/incident-management.xml" rel="self" type="application/rss+xml"/>
    <language><![CDATA[en]]></language>
    <lastBuildDate>Sat, 24 Oct 2026 09:05:11 GMT</lastBuildDate>
  <item>
    <title><![CDATA[Control how alerts activate, recover and notify: Kibana Universal Alerting in Elastic Observability]]></title>
    <description><![CDATA[Turn ES|QL queries into alert data using Kibana Universal Alerting, and triage Classic and external alerts alongside Universal alerts in a single view.]]></description>
    <content:encoded><![CDATA[<p>Kibana Universal Alerting is a new option for building and maintaining your alerts and notifications. Whether you start with a guided builder or dive straight into ES|QL, you choose exactly what to query, which data each alert stores, and who gets notified across all of your rules. Every alert is stored as time-series data, allowing you to search, analyze, and triage them all in one place. Six levers, covered below, help you tune each step.</p>
<p>Kibana Universal Alerting will be generally available and on by default in Elastic Cloud Serverless on October 20, 2026, and in Elastic Stack 9.6 for Elastic Cloud Hosted and self-managed deployments. Already have alerts in Kibana? The new system runs alongside your existing Classic Alerting system, so you can adopt what you like at your own pace.</p>
<p>Kibana Classic Alerting continues to handle common scenarios well. Pick a rule type, fill in its parameters, and attach actions as you always have. Classic Alerting is as simple and reliable as ever. Universal Alerting goes further, enabling you to express any ES|QL query as an alerting rule, control flapping with alert and recovery delay, use custom recovery queries, shape alert data to fit your needs, and schedule actions that respond and group alerts across your rules.</p>
<h2 id="whenuniversalalertingfits">When Universal Alerting fits</h2>
<p>It's a good fit when one or more of these is true:</p>
<ol>
<li>Your alert scenarios go beyond what Classic Alerting can offer, e.g., custom severities, workflow-powered actions, alert data control, full control over recovery, and more.</li>
<li>You want the flexibility of building rules using simplified query builders and later "dropping down" to edit and expand the generated ES|QL.</li>
<li>Your alerts come from more than one system, and you want to triage and analyze them in one view.</li>
<li>Fine-grained notification control matters, e.g., who is notified and how often, cross-rule grouping and processing, and more.</li>
<li>Your team wants all the flexibility offered by <a href="https://www.elastic.co/docs/explore-analyze/discover">Discover</a>, <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a>, and <a href="https://www.elastic.co/docs/explore-analyze/workflows">Workflows</a>. Universal Alerting integrates perfectly with and depends on all of these foundational Elastic features.</li>
</ol>
<p>If a Classic rule type already covers your scenario, keep using it. We compare the two systems <a href="https://www.elastic.co/observability-labs/blog/kibana-universal-alerting#one-place-for-all-your-alerts-classic-universal-alerting-and-external">side by side</a> later in this post.</p>
<h2 id="howuniversalalertingworksfromesqlqueryrulestoalertdata">How Universal Alerting works: From ES|QL query rules to alert data</h2>
<p>A Universal Alerting rule runs an ES|QL query on a schedule. Each result is written as a rule event to a dedicated data stream, <code>.rule-events</code>. Four core ideas define the system:</p>
<ul>
<li>Rules are ES|QL queries, so there are no rule types to pick from. Each row the query returns, identified by its group, becomes a rule event, and the columns it returns are stored with the event. The query defines the shape of the alert.</li>
<li>Alerts are data. Rule events live in a regular data stream with a documented schema, whether they came from Kibana alerting rules or an external tool. They can be searched in Discover, charted on dashboards, and used as the input for other rules.</li>
<li>An alert follows one ongoing problem through its lifecycle (pending, active, recovering, inactive) and groups all of its underlying rule events as the alert's history. A rule doesn't trigger a new alert on every run, nor does it update an existing alert document. Rule events can be captured without triggering alerts. These are simply collected as data.</li>
<li>Action policies live outside rules. They match on alert data, group and throttle them, and send them to workflows. Changing who gets notified doesn't mean editing an action per rule.</li>
</ul>
<p>Each of these is a separate component, and each one offers flexibility and controls on its own.</p>
<h2 id="thesreworkflowauthorandshapeonerule">The SRE workflow: Author and shape one rule</h2>
<p>Say you run production hosts across several availability zones. The goal is to alert when a host stays hot, with the severity level and availability zone the on-call engineer needs. A brief spike shouldn't count. The host examples use metrics from the Elastic Agent System integration (<code>system.*</code> fields), and the pod example later uses the Kubernetes integration (<code>kubernetes.*</code> fields). Swap in your own fields as needed.</p>
<h3 id="authoroneesqlrulewrittenthewayyoualreadyquery">Author: One ES|QL rule, written the way you already query</h3>
<p>This rule computes average CPU per host, assigns a severity and returns only the hosts worth a look:</p>
<pre><code>/*
  Average CPU per host, with a severity and a minimum-sample guard.
*/
FROM metrics-*
| WHERE system.cpu.total.norm.pct IS NOT NULL
| STATS
    avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
    samples = COUNT(*)
  BY host.name, cloud.availability_zone
    /* A column named severity sets the alert's built-in severity field
       (info, low, medium, high or critical). Hosts below every threshold get
       no severity, and the WHERE below drops them. The other returned
       columns, such as avg_cpu_pct and the zone, are stored on the alert and
       available to action policies and dashboards later. */
| EVAL severity = CASE(avg_cpu_pct &gt; 95, "critical", avg_cpu_pct &gt; 90, "high", avg_cpu_pct &gt; 85, "medium")
| WHERE samples &gt;= 10 AND severity IS NOT NULL
    /* A minimum-sample guard skips hosts that just came up and have only
       reported a few data points. */
| KEEP host.name, cloud.availability_zone, avg_cpu_pct, severity, samples
</code></pre>
<h3 id="shapedecidehowthealertbehavesovertime">Shape: Decide how the alert behaves over time</h3>
<p>The query decides what matches. The rule definition decides what happens as matches come and go. Here's the same rule in YAML, split into a base query, a breach condition, and a recovery condition:</p>
<pre><code>kind: alert

metadata:
  name: host-cpu-saturation
  routing_tags: [infra]                 # links this rule to action policies (lever 5)

schedule:
  every: 1m
  lookback: 5m

query:
  base: |
    FROM metrics-*
    | WHERE system.cpu.total.norm.pct IS NOT NULL
    | STATS
        avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
        samples = COUNT(*)
      BY host.name, cloud.availability_zone
    | EVAL severity = CASE(avg_cpu_pct &gt; 95, "critical", avg_cpu_pct &gt; 90, "high", avg_cpu_pct &gt; 85, "medium")
  breach:
    segment: WHERE samples &gt;= 10 AND severity IS NOT NULL

recovery:
  strategy: condition
  segment: WHERE avg_cpu_pct &lt; 70       # recover well below the breach threshold

no_data:
  strategy: keep_last                   # a host that is rebooting keeps its last known status

state_transition:
  pending:
    count: 2                            # alert delay: the alert goes active on the third consecutive breach
  recovering:
    count: 2                            # recovery delay: the alert goes inactive on the third consecutive recovery

grouping:
  fields: [host.name, cloud.availability_zone]   # one alert per host
</code></pre>
<p>Recovery, state transitions, grouping and no-data handling are each tunable, and levers 1 to 4 below cover them. Levers 5 and 6 pick up once the alert is active: who gets notified, and how responders triage it.</p>
<h2 id="sixleversforfinetuningyouralerts">Six levers for fine-tuning your alerts</h2>
<p>Universal Alerting has a control at each stage of the pipeline, listed here from earliest to latest. Use only the ones a scenario calls for.</p>
<h3 id="1thequerydecideexactlywhatcounts">1. The query: Decide exactly what counts</h3>
<p>ES|QL has everything needed to define a condition precisely. <code>WHERE</code> sets the scope, a minimum-sample guard ignores hosts that just came up, <code>CASE</code> assigns severities, and <code>SORT</code> with <code>LIMIT</code> restricts alerts to the worst offenders.</p>
<pre><code>/*
  Alert on only the three hottest pods, not every pod above the line.
*/
FROM metrics-*
| WHERE kubernetes.pod.cpu.usage.node.pct IS NOT NULL
| STATS avg_cpu_pct = ROUND(AVG(kubernetes.pod.cpu.usage.node.pct) * 100, 1) BY kubernetes.pod.name
| SORT avg_cpu_pct DESC
| KEEP kubernetes.pod.name, avg_cpu_pct
| LIMIT 3
    /* LIMIT caps the number of alerts a single run can produce.
       Use it when "the worst few" is the question you care about. */
</code></pre>
<p><code>LIMIT</code> has one side effect to plan for. A pod that drops out of the top three disappears from the results. With <code>no_breach</code> recovery, that counts as recovered, and the alert can activate again if the pod climbs back. If that matters for your rule, pair <code>LIMIT</code> with a recovery delay (lever 3).</p>
<h3 id="2groupingchoosetheunitofanalert">2. Grouping: Choose the unit of an alert</h3>
<p><code>STATS ... BY</code> defines what a single alert is. Group by <code>host.name</code> for one alert per host, or by <code>service.name</code> for one per service. Pick the unit someone can act on: a grouping that's too wide hides problems, and one that's too narrow multiplies them. If a grouping field is missing from some documents, those rows fall into a single null group and share one alert, while the other groups keep their own alerts. Handle missing values explicitly in the query if that matters.</p>
<p>Alerting rules return at most 1,000 rows unless you set a <code>LIMIT</code>. If a single run could produce more groups than that, add an explicit <code>LIMIT</code>.</p>
<h3 id="3alertdelayandrecoverydelaydefinehowanalertstartsandends">3. Alert delay and recovery delay: Define how an alert starts and ends</h3>
<p>Thirty seconds over a threshold isn't always an incident. An instance of recovery isn't always indicative of a real recovery. This kind of flapping is one of the biggest sources of alert noise, and two delay configurations handle it without touching the query.</p>
<p>The alert delay (<code>state_transition.pending</code>) is the number of evaluations an alert spends in the pending state before it goes active. With <code>count: 2</code>, the alert goes active on the third consecutive breach, so a short spike stays pending and never becomes an alert.</p>
<p>The recovery delay (<code>state_transition.recovering</code>) does the same for the recovering state before the alert goes inactive. A signal that bounces around the threshold stays one alert. Each phase accepts a <code>count</code>, a <code>timeframe</code> or both, plus an <code>operator</code> (<code>and</code> or <code>or</code>) that says whether both must be met or just one.</p>
<p>Recovery is a separate setting, and every alert rule must set one (<code>no_breach</code> by default):</p>
<ul>
<li><code>no_breach</code> recovers an alert when its group stops appearing in the breach results.</li>
<li><code>condition</code> recovers it when a different recovery condition (as opposed to alert condition) on the same base query is met, as in the rule above.</li>
<li><code>query</code> recovers it when a separate ES|QL query returns the alert's group. Recovery is per group, so each alert recovers only when the query returns its group.</li>
<li><code>manual</code> never recovers on its own. The alert stays active until someone manually resolves it.</li>
</ul>
<p>A common pattern is to recover well below the breach threshold; for example, breach at 90% CPU and recover under 67%. That way, an alert doesn't close the moment the metric dips under the line. And because <code>query</code> recovery can run its own ES|QL query, it can check different values than the breach does. The example below requires both CPU and load average to be back to normal before the alert closes:</p>
<pre><code># A recovery query that looks at more than the breach query does.
recovery:
  strategy: query
  query: |
    FROM metrics-*
    | STATS
        avg_cpu_pct = ROUND(AVG(system.cpu.total.norm.pct) * 100, 0),
        load_avg_5m = AVG(system.load.5)
      BY host.name, cloud.availability_zone
    | WHERE avg_cpu_pct &lt; 67 AND load_avg_5m &lt; 4
    | KEEP host.name, cloud.availability_zone
</code></pre>
<h3 id="4nodatahandlingdecidewhatsilencemeans">4. No-data handling: Decide what silence means</h3>
<p>When a group with an active alert stops showing up in the breach results, there are two very different explanations. Either the problem cleared up or the data source stopped reporting, and the alert condition alone can't tell them apart. No-data handling lets you decide, per rule, what happens in that moment, so a broken metrics agent doesn't quietly recover a real alert.</p>
<p>Set it with <code>no_data.strategy</code>, or with the no-data setting in the rule builder:</p>
<p>| Option | <code>no_data.strategy</code> | What happens |
| --- | --- | --- |
| Keep last status | <code>keep_last</code> | Holds the last known state. An active alert stays active, and a recovering alert stays recovering. A <code>status:no_data</code> rule event is written to the data stream.|
| Recover | <code>resolve</code> | Closes the alert the first time no data is found, moving it straight to inactive without waiting for the recovery delay. A <code>status:no_data</code> rule event is written to the data stream.|
| Do nothing | <code>ignore</code> | Skips the no-data check. With <code>no_breach</code> recovery, a gap in data looks like a recovery. With <code>condition</code> or <code>query</code> recovery, no event is written and the alert stays active. |</p>
<p>The right choice depends on what silence means for the rule. For infrastructure CPU, use Keep last status: if the agent stops sending data, an active CPU breach stays active until data comes back. For a rule that triggers an alert when a job backlog grows too large, Recover makes sense. Once the queue is empty, the query has nothing to return and the alert closes.</p>
<h3 id="5actionpoliciesroutegroupandthrottle">5. Action policies: Route, group and throttle</h3>
<p>Rules don't carry their own actions. Instead, an action policy selects alerts, bundles them into a payload, controls how often they repeat and dispatches them to a workflow. One action policy can cover alerts from many rules, so changing who gets paged for a hundred rules takes a single edit.</p>
<p>Policy scope selects the alerts an action policy handles, in two parts that can be combined. Routing tags limit the action policy to alerts from rules that carry at least one of those tags. Under Advanced matching, a <a href="https://www.elastic.co/docs/explore-analyze/query-filter/languages/kql">KQL</a> expression filters on alert fields such as <code>severity</code>, <code>group_hash</code> or <code>data.*</code>. When both are set, an alert has to pass both.</p>
<p>Routing tags connect an action policy to a rule. Add them in the Routing tags field on the Actions step of the rule form, and select the same tags in the action policy form. From then on, the action policy applies to every rule that carries one of those tags, including rules created later. The rule form's Applied policies panel lists every action policy that matches the rule, so the connection is easy to verify. To target a single rule, give it a routing tag that no other rule uses. Attaching or detaching a rule is then a matter of editing its routing tags, and the action policy itself doesn't change.</p>
<p>Routing tags aren't the same as a rule's regular tags. Regular tags group and filter rules in the rule list. They have no effect on notifications, so adding a regular tag to a rule never attaches an action policy to it.</p>
<p>Two more settings shape the notifications. Notify per controls bundling: one notification per alert, one per group of alerts across rules that share a field value such as the availability zone, or a single digest of everything the action policy matches. Frequency limits repeats, for example to at most once every 30 minutes.</p>
<p>Here are two action policies for the CPU rule, written as the settings chosen in the UI. Both are tied to the rule through its <code>infra</code> routing tag:</p>
<pre><code># Page the on-call engineer only for critical hosts, one page per availability zone.
Name:           infra-oncall-pager
Policy scope:   Routing tags: infra
                Advanced matching (KQL): severity : "critical"
Notify per:     Group, by data.cloud.availability_zone
Frequency:      At most once every 30 minutes
Destination:    pagerduty-infra workflow

# Send the lower severities to the infrastructure channel, batched per zone, at most hourly.
Name:           infra-team-digest
Policy scope:   Routing tags: infra
                Advanced matching (KQL): severity : "high" or severity : "medium"
Notify per:     Group, by data.cloud.availability_zone
Frequency:      At most once every 1 hour
Destination:    slack-infra-team workflow
</code></pre>
<p>An alert can match several action policies, and each one applies its own grouping and frequency. If a whole zone runs hot, the pager gets one grouped page rather than one per host, and the team channel gets an hourly summary of the high and medium hosts.</p>
<p>Action policies also work across rules. Give your CPU, memory and disk rules the same routing tag, select that tag in an action policy and set Notify per to Group by <code>data.cloud.availability_zone</code>. A zone where all three rules fire then sends one page instead of three. Switch Notify per to Digest, and the action policy sends a single notification for everything it matches. To alert on the pattern itself, see <a href="https://www.elastic.co/observability-labs/blog/kibana-universal-alerting#rules-on-top-of-your-alerts">rules on top of your alerts</a>.
Expand commentComment on line R230Resolved
Each destination in these examples is a workflow that calls your connector, such as Slack or PagerDuty. To learn how to build and enable one, see the <a href="https://www.elastic.co/docs/explore-analyze/workflows">Workflows documentation</a>.</p>
<h3 id="6triagecontrolsandmaintenancewindowsquietthingsonpurpose">6. Triage controls and maintenance windows: Quiet things on purpose</h3>
<p>Once an alert is active, a responder can acknowledge it, snooze the series for a set period or resolve it. Suppressed alerts stay out of notification channels but remain in the data. Nothing is deleted, and everything stays auditable. During a planned rollout, for example, snoozing the series keeps it out of notification channels until the snooze expires. Maintenance windows do the same for scheduled work and apply to Universal Alerting and Classic rules alike.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltef955ad52b501fd7/6acf7a53bcf6e058b0e5640c/alerts-actions.png" alt="The Alerts page with an alert details flyout and the Take action menu showing acknowledge, snooze and resolve" /></p>
<h2 id="oneplaceforallyouralertsclassicuniversalandevenexternal">One place for all your alerts: Classic, Universal, and even external</h2>
<p>Few teams have a single source of alerts. Some come from Classic Alerting rules, some from Universal Alerting rules, and some from other monitoring tools. Our new alerts views are built so responders can work from a single queue.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltec51c43df4d2b9ba/6acf7a6a6d1916350f15a8fb/alerts-multisource.png" alt="The Alerts page with Classic, Universal and external alerts" /></p>
<h3 id="alertsfromothertoolsasdata">Alerts from other tools, as data</h3>
<p>Alerts from external monitoring tools land in the same data stream as the alerts Elastic generates, and are searched, routed and triaged the same way. A workflow writes each one with the <code>alerting.create_alert</code> step, and the <code>source</code> field records where it came from. For the on-call engineer, that's one place to check instead of a tab per tool. External alert ingestion is an experimental feature, so expect it to change and improve as we learn how it's used.</p>
<h3 id="universalvsclassicfeaturecomparison">Universal vs. Classic feature comparison</h3>
<p>The two systems share a goal but make different tradeoffs. Classic Alerting favors guided setup for known scenarios, while Universal Alerting favors flexibility and control.</p>
<p>| Feature | Classic | Universal |
| --- | :---: | :---: |
| Prebuilt rule types with guided parameters | ✓ | Partial, more to come |
| Rules written as ES|QL queries | ✓ | ✓ |
| Basic threshold rules | ✓ | ✓ |
| Anomaly detection and APM anomaly rules | ✓ | Coming soon |
| Machine learning job health and transform health rules | ✓ | Coming soon |
| SLO burn rate rules | ✓ | Coming soon |
| Synthetics monitor status rules | ✓ | Coming soon |
| Alert fields defined by the rule's query | | ✓ |
| Alert event historical data, per run |  | ✓ |
| Alert delay (consecutive breaches before an alert goes active) | ✓ | ✓ |
| Recovery delay (consecutive recoveries before an alert auto-recovers) | | ✓ |
| Custom recovery condition or recovery query | | ✓ |
| Actions attached to each rule, sent directly to connectors | ✓ | |
| Reusable action policies shared across many rules, with grouping and throttling | | ✓ |
| Import alerts from external tools (experimental) | | ✓ |
| Triage and analysis in the combined Alerts page | ✓ | ✓ |
| Maintenance windows | ✓ | ✓ |</p>
<p>Universal and Classic alerts appear together on the combined Alerts page, so responders triage everything in one view. Classic Alerting keeps running exactly as it does today. Maintenance windows, connectors, and workflows are shared across both systems.</p>
<p>New alerting capabilities are actively being built in Universal Alerting. Classic rules remain a good fit for the standard scenarios they were designed for, and you can adopt Universal Alerting at your own pace, one rule at a time.</p>
<h2 id="whatuniversalalertingdoesntdoyet">What Universal Alerting doesn't do (yet)</h2>
<p>Universal Alerting doesn't match Classic Alerting feature for feature yet. We'd rather you hear about the gaps now than discover them mid-incident, so here's what to know before moving a rule over.</p>
<ul>
<li>Not every Classic rule type has a Universal equivalent yet. Universal rules are ES|QL queries, written by hand or generated by the rule builder. Threshold-style rules, such as custom, metric, log and index threshold rules, map well to the threshold rule builder, and many other conditions can be written directly in ES|QL. Rule types that rely on results computed elsewhere in the stack, such as anomaly detection, don't have a Universal counterpart yet, so those rules stay on Classic for now. The comparison table above lists them, and the rule library on our roadmap will add ready-made starting points for common patterns.</li>
<li>SLO and synthetics rules need extra work. Their conditions can be written in ES|QL against the SLO and synthetics data, but the multi-window burn rate logic and per-monitor settings of the Classic rules have to be rebuilt by hand. Keep those rules on Classic unless you need the extra control.</li>
<li>Classic rules are not automatically migrated to the Universal system. They keep running unchanged, and moving one to Universal Alerting means rebuilding its condition as an ES|QL rule.</li>
<li>Universal Alerting actions depend on workflows. A Classic rule sends its actions straight to connectors. A Universal rule has no actions of its own: action policies hand alerts to workflows, which are the only destination type, and the workflow then calls Slack, PagerDuty or any other connector. Before a Universal rule can notify anyone, at least one workflow must exist and be enabled.</li>
<li>Workflows require an Enterprise license on Elastic Cloud Hosted and self-managed deployments. Without this license, Universal rules still run and alerts are still written, but action policies cannot deliver to any destination, so no notifications go out. Existing action policies can still be viewed, disabled, snoozed and deleted.</li>
<li>Universal alerts don't provide flapping state configuration. Alert and recovery delays and custom recovery (lever 3) are the way to keep a noisy alert from firing too often.</li>
<li>Field names may differ between the two alert types. A query on a Universal alert field such as <code>group_hash</code> won't match any Classic alerts. Our unified alerts table mostly hides this distinction, but use shared filters (status, severity, rule, tags) in your own combined queries, or query each system separately.</li>
</ul>
<p>Two delivery details apply to every action policy. Notifications are delivered at least once, so in rare cases, such as a restart between dispatch and recording, the same notification can arrive twice. Make workflows idempotent. Notifications are also dispatched on a short polling interval rather than instantly, so don't depend on sub-second paging latency.</p>
<h2 id="understandyouralertingwithesql">Understand your alerting with ES|QL</h2>
<p>An alert and its history are documents in a data stream, so the language that defines a rule can also answer questions about your alerting. From an alert, jump into Discover and pivot to that host's metrics and logs without a separate tool. Both examples below query the <code>.rule-events</code> data stream.</p>
<pre><code>/*
  The ten rules that activated the most alerts in the last 7 days.
  rule.id is the rule's saved object ID, not its name.
*/
FROM .rule-events
| STATS alerts = COUNT_DISTINCT(alert.id) BY rule.id
| SORT alerts DESC
| LIMIT 10
    /* Rules at the top are good candidates for tuning. Adjust a threshold,
       raise the alert delay or change the grouping, then re-run this. */
</code></pre>
<pre><code>/*
  Alert volume per day for one rule, to confirm a change had the effect you wanted.
  rule.id is the rule's saved object ID, not its name.
*/
FROM .rule-events
| WHERE rule.id == "&lt;your-rule-id&gt;" AND type == "alert"
| STATS alerts = COUNT_DISTINCT(alert.id) BY day = BUCKET(@timestamp, 1 day)
| SORT day ASC
</code></pre>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt41c659d5f8a763e7/6acf7a7f0179ead3d02e4711/discover-alerts.png" alt="Discover running an ES|QL query on .rule-events that counts distinct alerts per day" /></p>
<h2 id="rulesontopofyouralerts">Rules on top of your alerts</h2>
<p>Since alerts are data, rules can run over them too. A rule on alert data is an ordinary ES|QL rule that reads <code>.rule-events</code> instead of logs and metrics. That lets it catch a pattern no single alert shows, such as three or more rules firing in the same availability zone. Alerts from Universal rules and from external tools work the same way here, because both land in the same data stream with the same fields. The alerts such a rule produces get the same lifecycle, action policies and triage controls as any other alert.</p>
<p>Signal rules are the building blocks for this. A rule with <code>kind: signal</code> (labeled Events in the UI) records breach and recovery events as data, without triggering alerts or notifying anyone. Use signal rules for conditions that matter mostly in combination, like high CPU, low free disk and elevated I/O wait on the same host. Then write an alert rule that fires when several of those signals line up.</p>
<h2 id="whatwereworkingonnext">What we're working on next</h2>
<h3 id="tryupcomingfeaturesperspace">Try upcoming features per space</h3>
<p>New and upcoming Kibana Universal Alerting features will ship behind an experimental feature flag, so teams can try them before they're enabled for everyone. The flag is the <strong>Universal Alerting: Experimental Features</strong> setting (<code>alerting:v2:experimentalFeatures</code>). It's off by default and set per Kibana space, so you can turn it on in a test or staging space and leave production spaces as they are. Changing it requires a page reload.</p>
<p>The flag gates every upcoming Universal Alerting feature. Everything that ships at GA works without it.</p>
<h3 id="whatscoming">What's coming</h3>
<p>While we aren't committing to any specific dates, this is where we're focused after GA:</p>
<ul>
<li>Sequence rule builder GA: A guided way to build rules that detect events happening in a particular order, without writing the whole query by hand (already available via "experimental features").</li>
<li>More rule builders: Provide more guided ways to build rule queries. (SLO, synthetics, anomaly detection, etc.)</li>
<li>A rule library of ready-made rules to start from and adapt, so a new rule doesn't begin with a blank query.</li>
<li>Alert deletion, for alerts that are no longer needed.</li>
<li>External alert import, generally available, with simpler setup and management.</li>
<li>Bi-directional alert syncing with external systems, so an action taken in one system is reflected in the other.</li>
<li>Further "no data" features.</li>
</ul>
<p>If one of these matters more to you than the rest, tell us. It helps us decide what to build first.</p>
<h2 id="getstarted">Get started</h2>
<p>Universal Alerting is generally available on Elastic Cloud Serverless, and on Elastic Cloud Hosted and self-managed deployments starting with Elastic Stack 9.6. On Elastic Cloud Hosted and self-managed deployments, workflow-based action policies require an Enterprise license, which includes 10,000 workflow executions per month (see <a href="https://www.elastic.co/subscriptions">subscriptions</a>). On Serverless, Elastic manages the alerting system.</p>
<h3 id="giveyourteamaccess">Give your team access</h3>
<p>Access is controlled with Kibana feature privileges, set per role in <strong>Stack Management &gt; Roles</strong>. Universal Alerting adds four, listed under Universal Alerting in the role editor, each with its own <strong>All</strong> or <strong>Read</strong> level:</p>
<p>| Privilege | Read | All |
| --- | --- | --- |
| Rules | View rules | Create, edit and delete rules |
| Alerts | View alerts | Triage alerts |
| Action Policies | View action policies | Create and edit action policies |
| Execution history | View execution history | Same as Read |</p>
<p>Most responders need Alerts: All and Rules: Read. Rule authors need Rules: All, plus Action Policies: All if they also manage notifications. Notifications run through workflows, so anyone who builds or edits them also needs <strong>All</strong> for <strong>Analytics &gt; Workflows</strong>. See <a href="https://www.elastic.co/docs/explore-analyze/workflows/get-started/setup">Set up Workflows</a> for the full list of Workflows privileges.</p>
<p>Privileges control what a user can manage, not what data a rule can read. Each rule runs with the API key of the user who last saved it, and that key determines what data the rule can query. Save your rules as a user who can read the indices they query. For how rules, action policies and workflows are authorized, see the <a href="https://www.elastic.co/docs/explore-analyze/alerting/experimental-alerting-system/authorization">authorization documentation</a>.</p>
<h3 id="createyourfirstrule">Create your first rule</h3>
<p>Open the Universal rules tab, create your first rule and tell us what you find. If something comes up that we didn't cover here, we want to hear about it.</p>
<p>Don't have a deployment yet? Start an <a href="https://cloud.elastic.co/registration">Elastic Cloud free trial</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/kibana-universal-alerting</link>
    <guid isPermaLink="false">kibana-universal-alerting</guid>
    <category><![CDATA[What's New]]></category>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Tia Milosevic,Jason Rhodes]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt51b3aa37674303d4/6acf7a1ff87eb7bd53b443e6/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 20 Oct 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Meet Elastic nightshift AI SRE, your always-on SRE coworker]]></title>
    <description><![CDATA[Elastic's SRE team has been running Elastic nightshift against our own production environment. It picks up incidents that never triggered an alert and shows its working for every conclusion it reaches.]]></description>
    <content:encoded><![CDATA[<p>Today we're announcing the Private Preview of the Elastic nightshift AI SRE, built into Elastic Observability to find, investigate, and fix production problems alongside your team. The Private Preview on Serverless is free during the promotional period.  <a href="https://www.elastic.co/observability/ai-sre/signup">Sign up for the waitlist here →</a></p><p>Production is outrunning the tools that watch it. Teams ship faster than ever, and more of that code is written by AI. Yet keeping production healthy is still manual work. Someone has to write the alert rules that catch problems and work out why each alert fired, and then they have to apply the fix. Much of what they need to know lives only in the minds of a few engineers. Every release changes what needs watching, and this work has become the bottleneck.</p><p>Elastic nightshift AI SRE works alongside your team, with the full context about your systems. It watches your telemetry around the clock and detects problems, including the unknown ones that no alert rule covers. It investigates incidents the way that an experienced site reliability engineer (SRE) would, testing hypotheses against your telemetry data, such as logs, metrics, traces, and profiles, ruling out dead ends and showing you evidence for the root cause. Then it recommends a fix to remediate the problem and applies it automatically, if you want it to.</p><p>At every step, it uses the most relevant contextabout your data and systems, built from your telemetry, code, runbooks, and more. It learns proactively from your data and prior incidents, along with enterprise knowledge. And, like you and your team, it investigates more accurately with every incident it detects or any alert it investigates. Plus, it can work with you in the Kibana UI, a terminal, an agent of your choice, or anywhere else you work. </p><p>When we were building this system, it was incredibly important that we earn your trust. If you've been using large language models (LLMs) for a while, you know how overly confident some tend to be. With Elastic nightshift AI SRE, instead of just getting the conclusion, you can follow all the steps that the agent took to get there and even drill down into the raw data, if you need to, as the following animation shows. 

You can check out the interactive version of this article <a href="https://elastic.co/blog/introducing-elastic-nightshift-ai-sre-interactive-post">here</a>.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta15e1887802c8a69/6ac738c5d056022ac8294ebc/image3.gif" alt="Animation of the Elastic nightshift AI SRE investigating an incident and showing each step of its reasoning" /><h2>How Elastic nightshift AI SRE works</h2><p>Elastic nightshift consists of four engines that work together (or you can use them on their own): Context, Detection, Investigation, and Remediation. For example, you can have it investigate alerts from the monitoring tools that you already use without turning on detection.</p><p><strong>Engine</strong></p><p><strong>What it does</strong></p><p><strong>What triggers it</strong></p><p><strong>What you get</strong></p><p>Context</p><p>Builds knowledge of your environment from telemetry, code, runbooks, and wikis</p><p>Runs continuously from the moment it has access to your data and systems</p><p>Entities, relationships, and a long-term memory of past incidents that the other three engines draw on</p><p>Detection</p><p>Evaluates telemetry continuously and flags what needs attention, with no alert rules to write or maintain</p><p>Always on when configured for data in Serverless</p><p>A significant event grouping related signals across services, with the parts of your system that it affects</p><p>Investigation</p><p>Tests hypotheses in parallel against logs, metrics, traces, and profiles, separating symptoms from causes</p><p>Any alert, fired in Elastic or a connected tool; optionally a significant event</p><p>A root cause in plain language, affected services, proposed actions, and the blind spots that it couldn’t verify</p><p>Remediation</p><p>Picks a workflow or connector action and fills in the parameters for this incident, or gives you commands to run</p><p>A completed investigation</p><p>A fix that you review before it runs (or apply automatically, if you choose)</p><p></p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt797d70fcacc7319b/6ac738e2ea4fe9e7e60f3f08/image8.png" alt="Architecture of the Elastic nightshift AI SRE: context, detection, investigation and remediation engines" /><p>Most importantly, you don't have to move your data to use it. The agents run on Elastic Observability Serverless and reach out to your telemetry and alerts in the systems where they already live. By technical preview later this year, it will work with:</p><ul><li><p>Data in Elastic Observability Serverless (sending data there is optional).</p></li><li><p>Elastic Cloud Hosted (ECH) deployments.</p></li><li><p>Self managed clusters (provided that the Elasticsearch endpoint is exposed appropriately).</p></li><li><p>Popular third-party observability tools.</p></li><li><p>Popular telemetry stores and databases.</p></li></ul><p>When you sign up for the waitlist, tell us which third-party tools, telemetry stores, and databases you want most so we can prioritize accordingly. In addition, we’re working toward a plan to deploy all this functionality in self-managed environments. We’ll have more to share later this year.</p><h3>Context Engine: Giving an AI SRE knowledge of your systems</h3><p>When an experienced SRE joins a new team, their skills come with them, but their knowledge of the system doesn't. They spend weeks learning the architecture, the runbooks, and the history of past incidents, along with which services are always noisy. What makes an SRE fast is the combination: years of experience plus deep knowledge of the system they run.</p><p>The same is true for AI agents. LLMs bring some of the experience but none of the knowledge of your system. Without context, an agent spends time and tokens working out how things fit together before it gets to the actual problem.</p><p>The <a href="https://www.elastic.co/search-labs/blog/context-engineering-for-agents">Context Engine</a> gives Elastic nightshift that knowledge before an incident starts. It's the layer that ties the whole system together.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f88910656b47929/6ac7390182545ed48b57ca0a/image10.png" alt="Context Engine layers: raw telemetry, knowledge indicators, entities and relations, enterprise knowledge, learnings" /><p>The Context Engine builds its knowledge in three ways:</p><ul><li><p><strong>Ahead of time.</strong> As soon as it has access to your data and systems, it extracts knowledge from your telemetry; that is, the entities in your environment, such as services and hosts, and the relationships between them. It can also read your wikis, runbooks, and code from systems like GitHub.</p></li><li><p><strong>From its own work.</strong> Each investigation and detection adds to a long-term memory of what happened and what fixed it.</p></li><li><p><strong>From your interactions and input. </strong>Give it feedback about detections, or feed it a postmortem doc. You can also manually upload important information.</p></li></ul><p>Remembering facts like these is different from the investigation learning described earlier. The Context Engine remembers what's true about your environment, while the Investigation Engine gets better at how it investigates.</p><p>Elastic stores telemetry efficiently enough that you can keep it unsampled, and the agent can search everything within your retention period. Because that telemetry and the knowledge built from it live on Elastic alongside the agent, it can go back to the raw data behind its conclusions.</p><h3>Investigation Engine: AI root cause analysis from any alert</h3><p>Alerts pile up, and each one that matters still lands on an engineer who has to piece together logs, metrics, traces, and recent changes by hand before they can even consider a fix.</p><p>Your new AI SRE coworker takes on that work, using the Investigation Engine. An investigation can start automatically from any alert, whether it’s fired in Elastic or in another tool that you've connected. From there, it does what a good on-call engineer would:</p><ol><li><p>Checks what changed.</p></li><li><p>Forms hypotheses about the cause.</p></li><li><p>Tests them in parallel against your logs, metrics, traces, and profiles.</p></li><li><p>Follows the leads that the evidence supports and drops the ones it doesn't.</p></li><li><p>Separates symptoms from causes.</p></li><li><p>Converges on the most likely root cause.</p></li><li><p>Proposes a fix.</p></li></ol><p>The core logic in our Investigation Engine comes from <a href="https://ir.elastic.co/News--Events/news/news-details/2026/Elastic-Completes-Acquisition-of-Deductive-AI/default.aspx">Deductive AI, which Elastic acquired</a>, and builds on Deductive's three years of work on automated incident investigation.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd2121e2cc3de321e/6ac73933c6656641987d5b9c/image1.png" alt="AI root cause analysis: six hypotheses tested and rejected before the Elastic nightshift AI SRE reaches a conclusion" /><p>The nightshift agent plans a step and runs the tools to carry it out. It observes what comes back and checks its conclusions against the evidence before moving on. Across investigations, it learns which paths lead to useful evidence and successful outcomes, so it gets better at investigating with every incident. </p><p>We talked earlier about building trust when using agentic systems, and this is one example of earning it from you. You can expand any step of an investigation to see the reasoning behind it and the queries that the agent ran. You can also see the raw data that it ran them against.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bdbb1f7e6a4e86c/6ac73956d1edc68b41573932/image4.png" alt="Expanded hypothesis in an AI SRE investigation showing confidence, the tools it ran and the charts behind the result" /><p>When an investigation finishes, you get:</p><ul><li><p><strong>A conclusion.</strong> The root cause, in plain language, and the immediate fix that we’d recommend.</p></li><li><p><strong>Impact.</strong> Which services are affected and how many problems each one has.</p></li><li><p><strong>Proposed actions.</strong> Next steps, each waiting for your review.</p></li><li><p><strong>Blind spots.</strong> What the agent couldn't verify, so you know where its confidence ends.</p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt55636698924d5624/6ac7397e512a6d2599e35d81/image5.png" alt="Completed AI SRE investigation showing what happened, impact across three services and the confirmed root cause" /><h3>Detection Engine: How do you catch incidents with no alert rule?</h3><p>The hardest incidents are often the ones that never triggered an alert to begin with. As a result, the service degrades and users notice. Monitoring stays quiet. No one can write an alert rule for a failure that they haven't imagined, and when something does fire, the related signals are scattered across services, each raising its own alert.</p><p>Your new coworker also takes on that work, using the Detection Engine, which continuously evaluates your telemetry and flags what needs attention. You don’t have to write or maintain any alert rules. When related signals show up across several services, it groups them into a single significant event that shows the signals behind it and the parts of your system that it affects. Significant Events can start an investigation automatically, if you choose, so that it’s already underway by the time someone looks.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7b6e111558a36739/6ac7399ec66566737c7d5ba0/image2.png" alt="Detection Engine grouping error spikes into a Significant Event, with the ES|QL query and sample logs behind it" /><h3>Remediation Engine: Automated remediation from the diagnosis</h3><p>Historically, Observability often stopped at the diagnosis. It could tell you that something was wrong, and eventually why, but the fix was up to you. Runbook automation could only replay the fixes that someone had scripted in advance, and real incidents rarely follow the script.</p><p>Your same new coworker also takes on this work, using the Remediation Engine, which recommends a fix. It can pick one of your existing workflows or connector actions in Elastic Workflows and fill in the parameters for this incident, or it can give you commands or instructions that you can copy into a terminal or a coding agent. Unlike a runbook script, the action and its parameters come from the diagnosis of the incident in front of it. You decide how much it does on its own; you review each action and the parameters it chose before it runs, or let it apply fixes automatically.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt517456f01cbbc68c/6ac739bdd056023fad294ec4/image7.png" alt="Automated remediation: proposed workflow with confidence scores waiting for review before the AI SRE runs it" /><h2>Using the AI SRE from Kibana, your terminal, or Slack</h2><p>Kibana will always be a first-class home for Elastic nightshift, and we'll keep making it a great experience. The Elastic nightshift home page lists your team’s open and recent investigations, sorted by severity, so whoever is on call can see at a glance what needs attention and what's already been handled.</p><p>However, more and more engineers want to use our products from the tools they already live in, like a terminal, a coding agent, or a chat channel, and we're embracing that:</p><ul><li><p><strong>Elastic CLI.</strong> You'll be able to pick up an investigation from your terminal or from a coding agent, like Claude, Cursor, or Codex, and carry on from there. Where Model Context Protocol (MCP) is the easier way to connect an agent, you can use that instead.</p></li><li><p><strong>Slack.</strong> We're building a Slack integration that makes Elastic nightshift feel like a coworker in your channels. It will post finished investigations (and answer questions about them) where your team already talks. More importantly, it will learn from any interaction in Slack. Nudge it along with additional context, or post a root cause analysis or postmortem document in the Slack thread to automatically trigger a learning loop that will use all of this information to get better for the next incident. </p></li></ul><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte113463856bfd07c/6ac739df4ad07f826b385d92/image6.png" alt="Elastic nightshift AI SRE posting a correlated investigation and proposed fix in a Slack incident channel" /><h2>Join the waitlist for the AI SRE private preview</h2><p>Elastic's own SRE team already runs Elastic nightshift against our production environment, and what team members learn goes straight back into the product. With the private preview, we're opening it to more teams.</p><ul><li><p><strong>Who can join:</strong> Existing Elastic users, at no cost during the early access period.</p></li><li><p><strong>What's included:</strong> A preview of the four engines described above, with more capabilities arriving every week.</p></li><li><p><strong>What we ask:</strong> Honest feedback. We'll work directly with preview teams, and what you tell us will shape what we build next.</p></li><li><p><strong>What's next:</strong> Technical preview in Elastic Cloud Serverless later this year.</p></li></ul><p><a href="https://www.elastic.co/observability/ai-sre/signup">Sign up for the private preview</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/ai-sre-elastic-nightshift</link>
    <guid isPermaLink="false">ai-sre-elastic-nightshift</guid>
    <category><![CDATA[Agentic Observability]]></category>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[AI]]></category>
    <dc:creator><![CDATA[Luca Wintergerst]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd195024f55f200a2/6ac737f43022640353bb5887/image9.png" length="0" type="image/png"/>
    <pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[14 alerts, 1 incident: Measuring alerting rule noise with ES|QL in Elasticsearch]]></title>
    <description><![CDATA[Grouping keys decide how many alerts one incident produces, and because Kibana stamps every alert document with a rule revision, you can measure your alert noise reduction against the same incident that caused the alerts in the first place.]]></description>
    <content:encoded><![CDATA[<p>One bad deploy on a payments platform opened 18 alert episodes in seven minutes, all from one incident. A single miscalibrated rule produced 14 of those episodes across only 7 alert instances. The median episode stayed active for one minute, so most of them had recovered before anyone could open the page.</p>
<p>This is an alert noise reduction exercise done with queries. Kibana writes every alert into a hidden index and keeps it there after recovery, so we put three questions to that <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts">alert history</a> with ES|QL. Was this one incident or many, where did it start, and which rule produced more noise than signal? Then we change the grouping key on the noisy rule and replay the same failure, and the same query comes back with one episode that lasts seven minutes.</p>
<p>Kibana stamps every alert document with a rule revision, so the before and after of that tuning change sit in one index. At the end, an <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Agent Builder</a> agent answers the same questions over the same alert history.</p>
<p>Everything below was tested on Elasticsearch and Kibana 9.5.2 on Elastic Cloud, with OpenTelemetry Collector Contrib 0.157.0.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8859a2c225fa1e53/6ab8cbec4d574224fabc2701/01-architecture.png" alt="Three payments services emit logs through the OTel Collector into one Elasticsearch data stream, where three rules turn them into alert episodes stored in .alerts-*: fourteen short episodes at revision 0 against one long episode at revision 1, all queried back with ES|QL and joined against the service catalog" /></p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>Elasticsearch and Kibana 9.5 or later, on <a href="https://www.elastic.co/cloud">Elastic Cloud</a> or <a href="https://www.elastic.co/docs/deploy-manage/deploy/self-managed">self-managed</a>.</li>
<li>Kibana privileges to create and edit <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rules</a> and <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-custom-threshold-rule">Observability custom threshold rules</a>, plus read access to the <code>.alerts-*</code> indices.</li>
<li><a href="https://github.com/open-telemetry/opentelemetry-collector-releases">OpenTelemetry Collector Contrib</a> 0.157.0 or later, and an Elasticsearch API key with ingest privileges for the OTLP endpoint.</li>
<li><a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Agent Builder</a> enabled, for the final section only. Everything before it is plain ES|QL and works without it.</li>
</ul>
<h2 id="whyalerthistorybeatsalertnotifications">Why alert history beats alert notifications</h2>
<p>A notification answers one short-lived question: who should look at this right now?</p>
<p>Most of the questions that make alerting better arrive later, and they are questions about data:</p>
<ul>
<li>Did 18 alert episodes represent 18 problems, or one incident evaluated from 18 angles?  </li>
<li>Which service showed the first symptom, and did the failure follow the dependency chain?  </li>
<li>Which rules produce short, repetitive episodes that nobody can act on?  </li>
<li>Did last month's threshold change actually reduce noise?</li>
</ul>
<p>Inside Elastic, this framing has a name: alerts as data.</p>
<p>The idea is that every alert and every state transition is a fact in time, stored with explicit semantics, so correlation becomes a query across streams rather than a bespoke feature.</p>
<p>It also matches what shows up in real operations channels: a single infrastructure failure can open dozens of alerts within seconds, and flapping alerts with miscalibrated thresholds are a familiar source of page fatigue.</p>
<p>Both problems are hard to solve from notifications and straightforward to study from preserved alert history.</p>
<h2 id="whatkibanaalertingstoresineveryalertdocument">What Kibana alerting stores in every alert document</h2>
<p>Kibana <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts">alerting rules</a> write alerts-as-data documents into hidden indices that you can query like any other index.</p>
<p>The two aliases used in this article are <code>.alerts-stack.alerts-default</code> for Elasticsearch query rules and <code>.alerts-observability.threshold.alerts-default</code> for the Observability custom threshold rule.</p>
<p>The unit of storage is what we will call an episode: one document that covers one alert instance from the moment it becomes active until it recovers.</p>
<p>The fields that matter for investigation:</p>
<p>| Field | What it tells you |
| :---- | :---- |
| <code>kibana.alert.instance.id</code> | The grouping-key value this episode belongs to |
| <code>kibana.alert.start</code>, <code>kibana.alert.end</code> | When the episode opened and recovered |
| <code>kibana.alert.duration.us</code> | How long it stayed active |
| <code>kibana.alert.status</code> | <code>active</code> or <code>recovered</code> |
| <code>kibana.alert.flapping</code>, <code>kibana.alert.flapping_history</code> | Whether Kibana marked it as rapidly changing state |
| <code>kibana.alert.rule.name</code>, <code>kibana.alert.rule.revision</code> | Which rule, and which version of it |
| <code>kibana.alert.grouping</code> | The structured grouping values, for example <code>service.name</code> |
| <code>kibana.alert.reason</code> | The human-readable condition at trigger time |
| <code>kibana.alert.url</code> | A deep link back to the rule's data view |</p>
<p>Two properties make the rest of the article possible.</p>
<p>First, a new episode for the same instance creates a new document, so counting documents counts episodes.</p>
<p>Alert documents and notifications are decoupled: whether an episode also pages anyone depends on the actions attached to the rule and on their frequency, which can be every check, only on status changes, or a summary on a custom interval.</p>
<p>Our three rules have no actions attached, so nobody was paged during the lab; everything we count from here on is an alert document.</p>
<p>Second, grouping fields with ECS names such as <code>service.name</code> are copied to the top level of the alert document, which means alert history can join against other indices.</p>
<p>One caveat before you query: alert indices are wide.</p>
<p>The alerts index on this deployment maps roughly 1,400 fields, so every ES|QL query in this article projects a narrow set of columns with <code>KEEP</code>, and you should do the same, especially in tools that feed an LLM.</p>
<p>Because these are hidden system indices, the Discover editor cannot always introspect their fields, so it may flag the query with a warning or even an error badge while still running it and returning rows. Trust the result count in the toolbar, not the badge.</p>
<p>One more orientation note: Kibana screenshots below render timestamps in the browser's local time (UTC-5 in this lab), while the query result tables in the text use UTC.</p>
<h2 id="thelabhowonebaddeployproducesanalertstorm">The lab: how one bad deploy produces an alert storm</h2>
<p>To generate an honest storm, we need services that fail in a chain, not a single threshold breach.</p>
<p>The scenario: <code>fraud-scorer</code> 2.3.0 ships with slow model loading, the payments queue backs up, <code>payment-gateway</code> requests start timing out, and customers see failed checkouts on <code>payments-web</code>.</p>
<p>Every service writes structured JSON logs, and an <a href="https://github.com/open-telemetry/opentelemetry-collector-releases">OpenTelemetry Collector</a> tails them with a <code>filelog</code> receiver and ships them over OTLP/HTTP to the deployment's <a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic OpenTelemetry endpoint</a> using an API key.</p>
<pre><code>receivers:
  filelog/payment_gateway:
    include:
      - ${env:LAB_DIR}/events/payment-gateway.jsonl
    start_at: end
    resource:
      service.name: payment-gateway
      service.namespace: payments
      service.version: 5.1.2
      cloud.region: us-central1
    operators:
      - id: parse_json
        type: json_parser
        parse_from: body
      - id: parse_severity
        type: severity_parser
        parse_from: attributes.severity_text
      - id: use_message_as_body
        type: move
        from: attributes.message
        to: body

exporters:
  otlphttp/elasticsearch:
    endpoint: ${env:OTEL_EXPORTER_OTLP_ENDPOINT}
    headers:
      Authorization: ApiKey ${env:ES_API_KEY}
</code></pre>
<p>A transform processor routes everything into one data stream, <code>logs-payments.stormlab-default</code>, and the records land with <code>service.name</code>, <code>log.level</code>, custom string attributes under <code>labels.*</code>, and numeric attributes under <code>numeric_labels.*</code>.</p>
<p>The full collector configuration for all three services, and a notebook that reproduces the whole lab, are in the <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/observability-labs/alerts-as-data-log-derived-signals-v2">companion repository</a>.</p>
<p>We also load one small index that will do a lot of work later: a service catalog.</p>
<pre><code>PUT payments_service_catalog
{
  "settings": { "index.mode": "lookup" },
  "mappings": {
    "properties": {
      "service.name": { "type": "keyword" },
      "team":         { "type": "keyword" },
      "tier":         { "type": "keyword" },
      "depends_on":   { "type": "keyword" }
    }
  }
}
</code></pre>
<p>Three documents map each service to its owning team, tier, and upstream dependency: <code>fraud-scorer</code> depends on <code>payments-queue</code>, <code>payment-gateway</code> on <code>fraud-scorer</code>, and <code>payments-web</code> on <code>payment-gateway</code>.</p>
<pre><code>POST payments_service_catalog/_bulk
{ "index": {} }
{ "service.name": "fraud-scorer",    "team": "risk-ml",       "tier": "backend",  "depends_on": "payments-queue" }
{ "index": {} }
{ "service.name": "payment-gateway", "team": "payments-core", "tier": "edge",     "depends_on": "fraud-scorer" }
{ "index": {} }
{ "service.name": "payments-web",    "team": "storefront",    "tier": "frontend", "depends_on": "payment-gateway" }
</code></pre>
<p><code>index.mode: lookup</code> is what later allows <a href="https://www.elastic.co/docs/reference/query-languages/esql/esql-lookup-join"><code>LOOKUP JOIN</code></a> from ES|QL.</p>
<h3 id="howgroupingkeysdecidehowmanyalertsyouget">How grouping keys decide how many alerts you get</h3>
<p>The storm is observed by three alerting rules, and their grouping keys are the real subject of this article.</p>
<p>| Rule | Rule type | Grouping key | Window | Threshold |
| :---- | :---- | :---- | :---- | :---- |
| <code>gateway-5xx-per-endpoint-status</code> | Elasticsearch query rule (ES|QL mode) | <code>labels.endpoint</code>, <code>labels.status_code</code>, <code>cloud.region</code> | 1 minute | 3 or more errors |
| <code>fraud-scorer-queue-lag</code> | Elasticsearch query rule (ES|QL mode) | <code>service.name</code>, <code>cloud.region</code> | 1 minute | Max lag of 30 seconds or more |
| <code>payments-error-rate-per-service</code> | Observability custom threshold rule | <code>service.name</code> | 2 minutes | More than 5 <code>ERROR</code> documents |</p>
<p>The first rule is deliberately miscalibrated in a way we have all shipped at some point: it groups gateway errors by endpoint and status code with a one-minute window and a low threshold.</p>
<pre><code>FROM logs-payments.stormlab*
| WHERE service.name == "payment-gateway" AND log.level == "ERROR"
| STATS error_count = COUNT(*) BY labels.endpoint, labels.status_code, cloud.region
| WHERE error_count &gt;= 3
</code></pre>
<p>It runs every minute as an Elasticsearch query rule in ES|QL mode, and each returned row becomes one alert instance, so <code>/api/payments,503,us-central1</code> and <code>/api/payments/confirm,504,us-central1</code> alert separately.</p>
<p>The second rule watches the first domino directly: consumer lag on the fraud queue, reported by the service in <code>numeric_labels.queue_lag_seconds</code>.</p>
<pre><code>FROM logs-payments.stormlab*
| WHERE service.name == "fraud-scorer" AND numeric_labels.queue_lag_seconds IS NOT NULL
| STATS max_lag_seconds = MAX(numeric_labels.queue_lag_seconds) BY service.name, cloud.region
| WHERE max_lag_seconds &gt;= 30
</code></pre>
<p>The third rule is an Observability <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-custom-threshold-rule">custom threshold rule</a>: more than five <code>ERROR</code> documents in two minutes, grouped by <code>service.name</code>.</p>
<p>It represents the sane default: one stable alert instance per affected service. It also demonstrates that different rule types feed the same investigation because all of them write alerts-as-data documents.</p>
<p>All three rules carry the tag <code>alerts-as-data-v2</code>, which is how every query below isolates them.</p>
<p>Tags on rules are not cosmetic: <code>kibana.alert.rule.tags</code> is stored on every alert document, so a tag is effectively a queryable dataset label for your alert history.</p>
<h3 id="replayingtheincident18alertepisodesinsevenminutes">Replaying the incident: 18 alert episodes in seven minutes</h3>
<p>We replay the incident: three minutes of healthy baseline, the 2.3.0 deploy, queue lag climbing past the threshold, six minutes of rotating gateway 5xx bursts, customer-visible errors, then a rollback and recovery.</p>
<p>The Alerts page during the storm looks like the pager felt.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltef758fb7ee9a57f4/6ab8cbff25e2f26bda5b00c3/02-alerts-storm.png" alt="Kibana Alerts page during the storm: 12 episodes already open or recovered, six active at once, and the same endpoint and status instances flipping between Active and Recovered" /></p>
<p>Notice the table, not just the count.</p>
<p>The same instance IDs appear both as <code>Active</code> and as <code>Recovered</code> within minutes of each other, which is churn you can see but not yet measure.</p>
<p>When the dust settles, the preserved history can be queried in Discover's ES|QL mode.</p>
<p>The raw material of the storm, the error logs themselves, confirms the cascade shape:</p>
<pre><code>FROM logs-payments.stormlab*
| WHERE labels.incident_id == "payments-brownout-20260722" AND log.level == "ERROR"
| STATS errors = COUNT(*) BY minute = DATE_TRUNC(1 minutes, @timestamp), service.name
| SORT minute
</code></pre>
<p>![Discover ES|QL view of error counts per minute and service: fraud-scorer errors first, then payment-gateway at seven per minute, then payments-web]]<a href="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1ced7aef9d407283/6ab8cc24688f6518c589ec0b/04-first-symptoms.png">3</a></p>
<p>Now we leave the logs behind and interrogate only the alert history.</p>
<h2 id="howtotellifanalertstormwasoneincidentormany">How to tell if an alert storm was one incident or many</h2>
<pre><code>FROM .alerts-*
| WHERE kibana.alert.rule.tags == "alerts-as-data-v2"
| STATS episodes = COUNT(*),
        alert_instances = COUNT_DISTINCT(kibana.alert.instance.id),
        first_episode = MIN(kibana.alert.start),
        last_episode = MAX(kibana.alert.start)
  BY rule = kibana.alert.rule.name
| SORT episodes DESC
</code></pre>
<p>| rule | episodes | alert_instances | first_episode | last_episode |
| :---- | ----: | ----: | :---- | :---- |
| gateway-5xx-per-endpoint-status | 14 | 7 | 16:36:38 | 16:42:38 |
| payments-error-rate-per-service | 3 | 3 | 16:36:41 | 16:37:41 |
| fraud-scorer-queue-lag | 1 | 1 | 16:35:35 | 16:35:35 |</p>
<p>Eighteen episodes, one incident, and every one of them opened inside a seven-minute window.</p>
<p>The gateway rule alone produced 14 of the 18 episodes while tracking only 7 distinct instances, which means half of its episodes were re-opens of something that had just recovered.</p>
<p>That single row is the quantified version of a complaint that usually stays anecdotal: this rule fires more than it helps.</p>
<h2 id="howtofindwhichservicefailedfirstwithesql">How to find which service failed first with ES|QL</h2>
<p>Because the grouping fields were ECS names, each episode carries <code>service.name</code> at the top level, and we can join the alert history against the service catalog.</p>
<pre><code>FROM .alerts-*
| WHERE kibana.alert.rule.tags == "alerts-as-data-v2" AND service.name IS NOT NULL
| STATS first_symptom = MIN(kibana.alert.start) BY service.name
| LOOKUP JOIN payments_service_catalog ON service.name
| KEEP first_symptom, service.name, team, tier, depends_on
| SORT first_symptom
</code></pre>
<p>| first_symptom | service.name | team | tier | depends_on |
| :---- | :---- | :---- | :---- | :---- |
| 16:35:35 | fraud-scorer | risk-ml | backend | payments-queue |
| 16:36:41 | payment-gateway | payments-core | edge | fraud-scorer |
| 16:37:41 | payments-web | storefront | frontend | payment-gateway |</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1ced7aef9d407283/6ab8cc24688f6518c589ec0b/04-first-symptoms.png" alt="Discover ES|QL view of the first-symptom query joining alert history with the payments service catalog" /></p>
<p>The alert order matches the dependency chain exactly: the first symptom belongs to team risk-ml, the edge degraded 66 seconds later, and customers noticed a minute after that.</p>
<p>There is a subtler deduction available in this table.</p>
<p>The earliest alerting service depends on <code>payments-queue</code>, which never alerted at all, so the most likely true origin sits one hop upstream of the first visible symptom, and that is where a post-incident review should start reading logs.</p>
<p>Also note which rule is absent from this analysis: the noisy gateway rule grouped by endpoint and status code, so its episodes carry no <code>service.name</code> and cannot participate in the topology join.</p>
<p>A grouping key is the data contract for your future investigations.</p>
<h2 id="howtoscorewhichalertingruleisnoisiest">How to score which alerting rule is noisiest</h2>
<p>A noise scorecard query condenses rule quality into one table.</p>
<pre><code>FROM .alerts-*
| WHERE kibana.alert.rule.tags == "alerts-as-data-v2"
| EVAL minutes_active = COALESCE(kibana.alert.duration.us, 0) / 60000000.0
| STATS episodes = COUNT(*),
        alert_instances = COUNT_DISTINCT(kibana.alert.instance.id),
        flapping_episodes = SUM(CASE(kibana.alert.flapping == true, 1, 0)),
        median_minutes_active = MEDIAN(minutes_active)
  BY rule = kibana.alert.rule.name, revision = kibana.alert.rule.revision
| SORT episodes DESC
</code></pre>
<p>| rule | revision | episodes | instances | flapping | median minutes active |
| :---- | ----: | ----: | ----: | ----: | ----: |
| gateway-5xx-per-endpoint-status | 0 | 14 | 7 | 0 | 1.0 |
| payments-error-rate-per-service | 0 | 3 | 3 | 0 | 7.0 |
| fraud-scorer-queue-lag | 0 | 1 | 1 | 0 | 9.0 |</p>
<p>Read the last column first.</p>
<p>A median active time of one minute means the typical episode from the gateway rule was already resolving itself before the on-call could even sign in, while the two well-grouped rules produced episodes long enough to represent the actual incident.</p>
<p>The <code>flapping</code> column is Kibana's own attempt at the same problem, and it is damage control.</p>
<p>The real fix is upstream, in the rule definition, and the alert history has just told us exactly what to change: the grouping key manufactures instances, and the one-minute window manufactures re-opens.</p>
<h2 id="alertnoisereductionfixingtheruleandprovingitworked">Alert noise reduction: fixing the rule and proving it worked</h2>
<p>The rule's own detail page already summarizes the problem: 14 alerts in 24 hours, every one of them from a single incident, produced by the definition on the right.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte457ea7bdb8c61f6/6ab8cc35f9d7ed34d45e8cc3/05-rule-detail-rev0.png" alt="Rule detail page of the noisy gateway rule: 14 alerts, zero active, with the endpoint and status grouping visible in the ES|QL definition" /></p>
<p>We update the rule rather than replace it, changing the grouping to <code>service.name</code>, widening the window to five minutes, and raising the threshold.</p>
<pre><code>FROM logs-payments.stormlab*
| WHERE service.name == "payment-gateway" AND log.level == "ERROR"
| STATS error_count = COUNT(*) BY service.name, cloud.region
| WHERE error_count &gt;= 10
</code></pre>
<p>Updating a rule in place matters for one specific reason: Kibana stamps every alert document with <code>kibana.alert.rule.revision</code>, so the before and after of a tuning change are separable in the same index with no bookkeeping on our side.</p>
<p>Then we replay a compressed version of the same failure: same deploy, same lag ramp, same rotating gateway bursts, same customer impact, rollback after seven minutes.</p>
<p>The scorecard query runs unchanged, and now the revision column earns its place:</p>
<p>| rule | revision | episodes | instances | flapping | median minutes active |
| :---- | ----: | ----: | ----: | ----: | ----: |
| gateway-5xx-per-endpoint-status | 0 | 14 | 7 | 0 | 1.0 |
| payments-error-rate-per-service | 0 | 6 | 3 | 0 | 6.0 |
| fraud-scorer-queue-lag | 0 | 2 | 1 | 0 | 8.0 |
| gateway-5xx-per-endpoint-status | 1 | 1 | 1 | 0 | 7.0 |</p>
<p>The unchanged rules now show both incidents in their rows, six and two episodes, which is exactly what stable rules should do.</p>
<p>The gateway rule splits into two rows because the revision changed, and the contrast is the entire argument of this article in four numbers.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb8f816570cfca616/6ab8cc45fb64e075972dd74e/06-noise-scorecard.png" alt="Discover ES|QL view of the noise scorecard grouped by rule and revision, showing revision 1 of the gateway rule producing a single stable episode" /></p>
<p>Same failure shape, one episode instead of fourteen, and the median episode now lasts seven minutes instead of one, so it spans the incident instead of a fraction of it.</p>
<p>This is the part of alerting work that usually relies on faith, and here it is a query result: the tuning change is verified against the same incident pattern, in the same index, by the same ES|QL.</p>
<p>Nothing about this loop is specific to our lab rule.</p>
<p>Any rule that tags its alerts can be scored, tuned, and re-scored this way, which turns alert tuning from an opinion into a measurement.</p>
<h2 id="queryingalerthistorywithanagentbuilderagent">Querying alert history with an Agent Builder agent</h2>
<p>Every query above is deterministic, which makes them ideal tools for an AI agent.</p>
<p>We register three ES|QL tools in <a href="https://www.elastic.co/docs/explore-analyze/ai-features/elastic-agent-builder">Agent Builder</a>, each one a parameterized version of a query from this article with an explicit <code>KEEP</code> projection, and wire them into an agent called Alert Historian.</p>
<pre><code>POST kbn:/api/agent_builder/tools
{
  "id": "alert_noise_scorecard",
  "type": "esql",
  "description": "Noise scorecard per alerting rule and revision: episodes, distinct instances, flapping episodes, median minutes active.",
  "configuration": {
    "query": "FROM .alerts-* | WHERE kibana.alert.rule.tags == \"alerts-as-data-v2\" AND DATE_DIFF(\"hours\", kibana.alert.start, NOW()) &lt;= ?lookback_hours | EVAL minutes_active = COALESCE(kibana.alert.duration.us, 0) / 60000000.0 | STATS episodes = COUNT(*), alert_instances = COUNT_DISTINCT(kibana.alert.instance.id), flapping_episodes = SUM(CASE(kibana.alert.flapping == true, 1, 0)), median_minutes_active = MEDIAN(minutes_active) BY rule = kibana.alert.rule.name, revision = kibana.alert.rule.revision | KEEP rule, revision, episodes, alert_instances, flapping_episodes, median_minutes_active | SORT episodes DESC",
    "params": {
      "lookback_hours": {
        "type": "integer",
        "description": "How many hours of alert history to analyze, for example 6"
      }
    }
  }
}
</code></pre>
<p>The agent's instructions define the vocabulary (an episode is one alert document), the method (timeline first, then propagation, then scorecard), and the honesty rules (report numbers and timestamps from tool output, never invent alerts).</p>
<p>We ask it one question: what happened in the last three hours, was it one incident or many, which team owns the first symptom, and did the gateway rule tuning reduce noise?</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ef21427e82f02b3/6ab8cc572f8bb23e405e8070/07-agent-two-storms.png" alt="Agent Builder conversation where Alert Historian identifies two storms separated by a quiet gap, with 23 episodes across 3 rules" /></p>
<p>The agent called all three tools and got the structure right: it separated the history into two storms with a quiet gap, one per failure wave, rather than blending them into one event.</p>
<p>It attributed the first symptom to team risk-ml, reproduced the upstream deduction about the queue that never alerted, and then answered the tuning question with the revision table.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8ebe9a3561cff889/6ab8cc696ad09533cb4259df/08-agent-summary.png" alt="End of the Alert Historian conversation: team risk-ml named as the owner of the first symptom, the deduction about the upstream queue that never alerted, and revision 0 compared against revision 1" /></p>
<p>Its verdict on the fix reads like a review comment backed by data: fourteen episodes at revision 0 against one stable episode at revision 1, with the median episode going from one minute to seven.</p>
<p>The agent is not doing anything we could not do by hand.</p>
<p>That is precisely the point: because alerts are durable, structured data with explicit semantics, a language model can reason over patterns and histories instead of reacting to a single notification, and every number it states is traceable to a tool call you can rerun.</p>
<h2 id="runningalerthistoryqueriesinproduction">Running alert history queries in production</h2>
<p>A few things to decide before you rely on alert history in production.</p>
<h3 id="retentionisalearningwindowdecision">Retention is a learning-window decision</h3>
<p>Alert indices follow an ILM policy, and the default keeps far more than the few hours this lab needed, but if you want quarter-over-quarter rule quality trends, align the policy with that ambition deliberately.</p>
<h3 id="scopealertindexreadaccessnarrowly">Scope alert index read access narrowly</h3>
<p>Alert documents embed rule parameters, reasons, and deep links, so treat <code>.alerts-*</code> read privileges like any other operational dataset and grant the narrowest index patterns that work, for example only the observability alert aliases.</p>
<h3 id="groupingkeysareadatacontract">Grouping keys are a data contract</h3>
<p>Use ECS field names in <code>BY</code> clauses when they exist, keep cardinality bounded, and never put secrets or personal data into grouping values, because they become <code>kibana.alert.instance.id</code> and live for the retention window.</p>
<h3 id="projectcolumnsaggressivelywithkeep">Project columns aggressively with <code>KEEP</code></h3>
<p>With around 1,400 mapped fields in the alert indices, an unprojected query is unpleasant for humans and actively harmful inside LLM tools, so end every reusable query with <code>KEEP</code>.</p>
<h3 id="alertstormsalsostressyourautomation">Alert storms also stress your automation</h3>
<p>If a rule triggers a workflow per alert, fourteen episodes mean fourteen executions doing the same work, which is another argument for pointing automation at the aggregated alert dataset rather than at each notification.</p>
<h2 id="wheretostartwithyourownnoisiestrule">Where to start with your own noisiest rule</h2>
<p>The lab in this article is intentionally small, and every piece of it generalizes.</p>
<p>Score your noisiest real rule with the scorecard query, fix its grouping or window, and let <code>kibana.alert.rule.revision</code> tell you whether the fix held.</p>
<p>Join your alert history against whatever catalog you already have; a CMDB export in a lookup index is enough, and the propagation order becomes a query.</p>
<p>If you use Agent Builder, wrap your two or three most-used investigation queries as tools, and the next storm review starts with a conversation instead of a wall of notifications.</p>
<p>The <a href="https://github.com/elastic/elasticsearch-labs/tree/main/supporting-blog-content/observability-labs/alerts-as-data-log-derived-signals-v2">companion notebook</a> builds everything used here: the catalog index, the log data stream, the three rules, both incident replays, the queries, and the Agent Builder tools and agent. It runs for about 25 minutes because the rules evaluate once a minute and the episode durations are the data.</p>
<p>A notification tells you something broke.</p>
<p>The alert history tells you how your detection is performing, and it is already sitting in an index waiting to be queried.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/esql-alert-noise-reduction</link>
    <guid isPermaLink="false">esql-alert-noise-reduction</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Data Management]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2306ef56ba70a634/6ab8cbb44d5742d665bc26fd/header.png" length="0" type="image/png"/>
    <pubDate>Tue, 29 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Cross-project search for Elastic Observability: one query across every linked project]]></title>
    <description><![CDATA[Keep your observability data where it lives, and still search, alert, and monitor across every linked Serverless project, easily!]]></description>
    <content:encoded><![CDATA[<p>A single query can now reach every Serverless project you own. Link your projects once and every search, dashboard and alerting rule after that can span all of them. Discover, dashboards and ES|QL all operate with no duplication of data between projects and no remote index prefixes in your queries. Cross-project search (CPS) is <a href="https://www.elastic.co/blog/cross-project-search-elastic-serverless-ga">generally available</a> on <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a>.</p><p>If you run Observability on Elastic Cloud Hosted (ECH), this will feel familiar. <a href="https://www.elastic.co/docs/explore-analyze/cross-cluster-search">Cross-cluster search</a> (CCS) has long enabled teams to query across remote clusters, and it's a mainstay in large scale ECH environments. CPS takes the same idea to Serverless, and makes linking even simpler: no topology mapping, certificate configuration or per connection credentials. You link projects in the Cloud console and Elastic manages the rest.</p><h2>What is cross-project search?</h2><p>Serverless organizes your data into <strong>projects</strong>: Elasticsearch, Observability, or Security, each with its own scaling, permissions, and lifecycle. That isolation is a feature: it preserves data locality where needed, simplifies cost attribution, and lets teams own their own data. The tradeoff, historically, has been visibility. If your logs live in one project and your APM data lives in another, or if every business unit runs its own Observability project, getting a unified view has typically meant duplicating data into a shared store.</p><p>CPS removes that tradeoff. You designate an <strong>origin project</strong> (the project you search from) and link it to other projects you want included in that search. Once linked, an unqualified query like <code>FROM logs*</code> or a Discover session automatically spans the origin plus every linked project; no special syntax, no remote index prefixes. Kibana's project picker lets anyone narrow a query to just the current project or expand it to everything in scope, and administrators can set a default scope per Kibana space so a local incident-response space stays local by default while a global operations space starts broad. Access is enforced per project using your existing Elastic Cloud roles, so linking a project for search doesn't quietly grant broader permissions than a user already has.</p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2cee1f522a706b1c/6ab29c62a8762835045d68c9/Screenshot_2026-09-22_at_6.18.44_p.m..png" alt="Diagram showing an origin Observability project linking to multiple linked projects across regions and project types, with queries fanning out and results merging back automatically" /><p></p><h3>What's the difference between cross-project search and cross-cluster search?</h3><h3></h3><p></p><p>Cross-cluster search (ECH, self-managed)</p><p>Cross-project search (Serverless)</p><p>Where you link</p><p>Remote cluster connections, configured per cluster</p><p>Link projects in the Elastic Cloud console</p><p>Credentials</p><p>Certificates or API keys you issue and rotate</p><p>Managed by Elastic</p><p>Network setup</p><p>Network topology you map and maintain</p><p>None to configure</p><p>Query syntax</p><p>Remote cluster name referenced in the query</p><p>Unqualified query, no remote index prefix</p><p>Scope control</p><p>Set per query</p><p>Project picker, per-space defaults, and tag-based routing</p><p>Where CCS asks administrators to configure remote cluster connections, exchange certificates or API keys, and reference remote cluster names in every query, CPS asks you to link projects in the UI and query the way you already do. It's the same underlying idea: remote search without moving data, adapted to a control plane where infrastructure is meant to disappear.</p><h2>Why cross-project search matters for Observability teams</h2><p>Cross-project search matters most the moment your observability data stops living in one place. Observability organizations typically split their data by team, environment, region, or customer, usually for good reasons: isolating noisy neighbors, controlling blast radius, meeting data residency requirements, or giving each team ownership over its own ingest and retention. The problem shows up during an incident or a cross-cutting investigation, when someone needs to see across those boundaries without waiting for a data migration or standing up a consolidated cluster.</p><p>A few examples of where CPS changes the equation:</p><h3>Keep observability data in its region and still search globally</h3><p>Teams operating in multiple regions or clouds often need to keep observability data in the region it was generated, whether for regulatory reasons or to limit cross-region egress. CPS lets each region keep its own Observability project while a central project links to all of them, so an SRE team can run one query to check whether an anomaly in one region is also showing up elsewhere, without pulling all that data into one place first.</p><h3>Isolate environments without losing the incident view</h3><p>Splitting production, staging, and per-service or per-team projects makes scaling and cost predictable, and it limits how far a bad deploy or a runaway job can spread. It also means an on-call engineer used to have to check several places during an incident. With CPS, on-call can work from a single origin project and pull in whichever linked environments matter to the investigation, then narrow back down once things are resolved.</p><h3>Central teams monitoring many business units or customers</h3><p>Platform or Observability Center of Excellence (CoE) teams that operate on behalf of many business units, or Managed Service Providers (MSPs) monitoring several customer environments, can give each unit or customer its own project, with its own retention, access, and cost, while the central team links across all of them for cross-cutting reporting and anomaly detection. Each project owner keeps control of their own data; the center gets the aggregate view it needs.</p><h3>Avoid ingesting the same data twice</h3><p>When two teams need the same data for different purposes, the old options were duplicate ingestion pipelines or a shared, less isolated deployment. With linked projects, data is ingested once and queried from wherever it's needed, which keeps both storage costs and pipeline maintenance down.</p><p>This applies across solution types too: CPS links Elasticsearch, Observability, and Security projects interchangeably, so a Security team and an Observability team can each keep their own project, with their own retention and access controls, while querying the same underlying logs. A security analyst investigating an alert can query infrastructure or service context from a linked Observability project in Discover, dashboards, or ES|QL, and an SRE debugging an incident can query related signals from a linked Security project the same way, all without either team re-ingesting the other's data.</p><h2>What does cross-project search support in Elastic Observability?</h2><p>At GA, CPS is available across Elasticsearch, Observability, and Security project types (Observability and Security projects require the Complete tier). For Elastic Observability specifically, application and infrastructure monitoring, alerting rules, SLOs, machine learning anomaly detection (including APM anomaly detection), and Synthetics work across linked projects, alongside the platform-level capabilities every project gets: Discover, dashboards, and ES|QL. Project routing lets you target a query to a subset of linked projects using predefined tags (alias, cloud provider, region) or your own custom tags, combined with boolean logic, so "all EU projects" or "every environment except staging" can be expressed once and reused.</p><p>This release also raises the default number of projects you can link to a single origin, well beyond the tech preview limit, with room to go higher for larger deployments. Any existing project can now act as an origin rather than only newly created ones. For programmatic access, you only need a Cloud API key to call CPS from your own tooling. A new dedicated cloud role can now grant those keys for programmatic access.</p><p>A few nuances are worth knowing before you design around CPS:</p><ul><li><strong>Alerting</strong>: Alerting rules defined in the origin project can evaluate data across linked projects. The alerts they generate stay local to the project where the alerting rule lives.</li><li><strong>SLOs</strong>: SLOs don't follow the project picker in the Kibana header. Instead, you set scope in the SLO create and edit form, choosing this project, all linked projects, or a specific subset, since changing scope rebuilds the SLO's underlying computation. You can't view SLOs created in linked projects from the current project, but you can create an SLO here based on data that lives in those projects.</li><li><strong>Synthetics</strong>: Overview, monitor details, certificates, errors, and journeys honor the Kibana header project picker and show linked-project monitors as read-only. Creating, editing, and managing monitors stay in the origin project, and Synthetics status and TLS rules still evaluate origin monitors only.</li><li><strong>Streams</strong>: Not CPS-aware yet; Streams stay scoped to the project where they were created.</li></ul><h2>How do you set up cross-project search?</h2><p>Setting up cross-project search takes a few minutes:</p><ol><li>From your origin project's settings in the Elastic Cloud console, open <strong>Link projects</strong>.</li><li>Select the projects you want to link, individually or in bulk.</li><li>Save.</li><li>Open Discover, run an ES|QL query, or build a dashboard as you normally would: linked project data is included automatically, and the project picker lets you narrow or widen the scope at any time.</li></ol><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd604c155e0ef0f17/6aad2c627e66d4c520015488/cps-link-projects-screenshot-embedded.svg" alt="The Link projects screen in the Elastic Cloud console, showing project search and filters (project type, cloud provider, region, tags) and a list of candidate projects to select for linking" /><p>For the full configuration walkthrough, including linking topologies (hub-and-spoke, shared data project, or full mesh) and how to scope project routing with tags and expressions, see the <a href="https://www.elastic.co/docs/deploy-manage/cross-project-search-config">cross-project search documentation</a> and the <a href="https://www.elastic.co/docs/solutions/observability/cross-project-search">Observability-specific guide</a>.</p><h2>Cross-project search pricing</h2><p>CPS pricing has two components: a charge for data retained in each linked project you query, and a charge for data egress generated by cross-project query traffic, tracked as its own line item so you can see what CPS is adding to your transfer costs. There's no separate per-link fee. Because actual cost depends on your data volumes, retention settings, and how many projects you link, use the <a href="https://cloud.elastic.co/pricing/serverless">Elastic Cloud Serverless pricing estimator</a> to model costs for your own environment against your own ingest and retention numbers.</p><h2>Start using cross-project search</h2><p>If you're already running Elastic Observability on Serverless, cross-project search is available in your project settings today: link your projects and run your first cross-project query in Discover. If you haven't tried Elastic Cloud Serverless yet, <a href="https://cloud.elastic.co/serverless-registration">start a free trial</a> and see how far a single query can reach.</p><p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p><p><em>Elastic, Elasticsearch, and associated marks are trademarks, logos, or registered trademarks of Elasticsearch B.V. in the United States and other countries. All other company and product names are trademarks, logos, or registered trademarks of their respective owners.</em></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/cross-project-search-serverless-observability</link>
    <guid isPermaLink="false">cross-project-search-serverless-observability</guid>
    <category><![CDATA[Data Management]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Vinay Chandrasekhar]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt871cded13b7f8cb8/6aad22506ea6417ea141fe46/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 21 Sep 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Skip writing alert rules: 6 ready-made ES|QL templates ship inside the NGINX OTel integration]]></title>
    <description><![CDATA[Elastic integrations come with alerting rule templates, each one an ES|QL query with a threshold already set. Create Elasticsearch alert rules in minutes, tune them to your traffic, and catch silent data streams early.]]></description>
    <content:encoded><![CDATA[<p>The NGINX OpenTelemetry Assets integration ships six <a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">alerting rule templates</a>. Each one is an <a href="https://www.elastic.co/docs/reference/query-languages/esql">ES|QL</a> query with a threshold already tuned. Install the integration, create a rule from one of the templates, and adjust the threshold to match your traffic. You get working alerts in minutes instead of writing them from scratch. This walkthrough covers the full setup, threshold tuning, and how to use an idle data streams rule to catch a service that stops sending data.</p>
<h2 id="prerequisitesforelasticintegrationalertingruletemplates">Prerequisites for Elastic integration alerting rule templates</h2>
<p>Elastic Stack 9.4.0 or later.</p>
<p><em>Alerting rule templates have been available since 9.2.1, under the integration <strong>Assets</strong> tab. This article covers three things that need 9.4.0: the dedicated <strong>Alerting</strong> tab, idle data streams rules, and the NGINX OpenTelemetry Assets package, which is in technical preview.</em></p>
<h2 id="step1sendnginxlogsandmetricstoelasticsearchwithopentelemetry">Step 1: Send NGINX logs and metrics to Elasticsearch with OpenTelemetry</h2>
<p>First, get NGINX metrics and logs into Elasticsearch.</p>
<p>Enable the NGINX <code>stub_status</code> module and make the access and error logs readable by the collector. Then, configure an <a href="https://www.elastic.co/docs/reference/opentelemetry">EDOT</a> or upstream OpenTelemetry Collector with the <code>nginx</code> and <code>filelog</code> receivers to export metrics and logs to Elasticsearch.</p>
<p>The <a href="https://www.elastic.co/docs/reference/integrations/nginx_otel">integration setup</a> has the full receiver and pipeline configuration.</p>
<p>If you want to reproduce this example, you can use the <a href="https://github.com/Delacrobix/Creating-alerts-from-OOTB-alerting-template">companion repository</a>.</p>
<p>Both signals matter for alerting, and each group of templates reads a different data stream:</p>
<ul>
<li>The <strong>log-based</strong> templates (4xx and 5xx error rates, error log spike) query <code>logs-nginx.access.otel-*</code> and <code>logs-nginx.error.otel-*</code>, which come from the <code>filelog</code> receiver.</li>
<li>The <strong>metric-based</strong> templates (active connections, dropped connections) query <code>metrics-nginxreceiver.otel-*</code>, which comes from the <code>nginx</code> receiver.</li>
</ul>
<p>This is easy to get wrong: the Fleet <a href="https://www.elastic.co/docs/solutions/observability/infra-and-hosts/collect-nginx-data-otel-integration-fleet-managed"><strong>Nginx (OpenTelemetry)</strong> input package</a> collects <code>stub_status</code> metrics only. Its companion for logs is the classic Nginx integration, which writes ECS-based <code>nginx.access</code> and <code>nginx.error</code> datasets, not the <code>*.otel-*</code> data streams the log-based templates query. If you rely on that pairing alone, the log-based rules have nothing to evaluate and silently never fire. Run the <code>filelog</code> receiver too, not just the <code>nginx</code> receiver.</p>
<h2 id="step2installthenginxopentelemetryassetsintegration">Step 2: Install the NGINX OpenTelemetry Assets integration</h2>
<p>NGINX OpenTelemetry Assets is a content-only package. It ships the dashboards, alerting rule templates, and SLO templates, but it does not collect data itself. The data comes from the collector you set up in Step 1.</p>
<p>You don't need to install it by hand. Once the NGINX OTel data from Step 1 starts arriving, Elastic detects it and installs the Assets package for you, which takes a minute or two. Confirm it under <strong>Management</strong> &gt; <strong>Integrations</strong> &gt; <strong>Installed integrations</strong>, where <code>NGINX OpenTelemetry Assets</code> should appear.</p>
<h2 id="step3createelasticsearchalertrulesfromaruletemplate">Step 3: Create Elasticsearch alert rules from a rule template</h2>
<p>Open the integration and select the <strong>Alerting</strong> tab.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt06c9c544b653b81c/6a85c98d43c0b7f8172f05f0/02-alerting-tab.png" alt="The Alerting tab of the NGINX OpenTelemetry Assets integration listing its rule templates" /></p>
<p>This package ships six templates: high 4xx and 5xx error rates, high active connections, an error log spike, dropped connections, and a generic <code>High error rate by service</code> template that points at a placeholder <code>logs-myservicereceiver.otel-*</code> index for you to repoint and rename. The five NGINX rules run ES|QL every minute and group results by <code>host.name</code>, so an alert points at the host with the problem. The generic template groups by <code>service.name</code> instead, since it is meant to be repointed at whichever service you choose.</p>
<p>Select a template, for example <code>[Nginx OTel] High 5xx error rate</code>. Kibana opens a prefilled <strong>Create rule</strong> form built on an <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a>. It runs the template's ES|QL on a schedule and alerts when the query returns rows.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d9547363761de4b/6a85c991bc5bb36529f81acd/03-create-rule-form.png" alt="The prefilled Create rule form for the High 5xx error rate template" /></p>
<p>The query looks like this:</p>
<pre><code>FROM logs-nginx.access.otel-*
// Flag each access log entry as a server error (5xx) or not
| EVAL is_5xx = CASE(http.response.status_code &gt;= 500, 1, 0)
// Aggregate total requests and 5xx count per NGINX host
| STATS total = COUNT(*), errors_5xx = SUM(is_5xx) BY host.name
// Minimum sample size to avoid noisy low-traffic hosts
| WHERE total &gt; 50
// Calculate 5xx error rate as a percentage
| EVAL error_rate_pct = ROUND(TO_DOUBLE(errors_5xx) / TO_DOUBLE(total) * 100.0, 2)
// Alert threshold: adjust to tune sensitivity
| WHERE error_rate_pct &gt; 5.0
| SORT error_rate_pct DESC
| LIMIT 10
</code></pre>
<p>It counts requests and 5xx responses per host, keeps hosts with enough traffic to matter, and returns those above five percent.</p>
<p>Three things to get right while the form is open:</p>
<ul>
<li><strong>Send data first.</strong> ES|QL validates column names against the indices that exist when the query runs. Open a template before any NGINX data has been ingested and the editor reports <code>Unknown column "http.response.status_code"</code> and the form shows errors. Once data is flowing (Step 1), the same query validates and the error clears, so collect data before you create the rule.</li>
<li><strong>Set the time field to <code>@timestamp</code>.</strong></li>
<li><strong>Leave "Create an alert for each row" selected.</strong> Because the query groups by <code>host.name</code>, this makes every affected host raise its own alert.</li>
</ul>
<p>Add a <a href="https://www.elastic.co/docs/deploy-manage/manage-connectors">connector</a> and an action so the alert reaches Slack, email, or PagerDuty, then save and enable the rule.</p>
<h2 id="step4tunealertingruletemplatethresholdsinesql">Step 4: Tune alerting rule template thresholds in ES|QL</h2>
<p>The thresholds are starting points, so confirm them against your own traffic. The threshold lives in the ES|QL <code>WHERE</code> clause.</p>
<p>To make the rule stricter, change <code>error_rate_pct &gt; 5.0</code> to <code>error_rate_pct &gt; 2.0</code>. To require more traffic before it fires, raise <code>total &gt; 50</code>. Use <strong>Test query</strong> in the rule form to confirm the edited query parses and returns rows before you save.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8b66296ca6cd70c8/6a85c994bc5bb3f376f81ad1/04-test-query.png" alt="Test query results after editing the threshold in the ES|QL query" /></p>
<p>Three more settings are worth a look:</p>
<ul>
<li><strong>Time window</strong>: the look-back period the query runs over. A shorter window reacts faster but is noisier on bursty traffic.</li>
<li><strong>Rule schedule</strong>: how often the query runs, every minute by default.</li>
<li><strong>Alert delay</strong>: the number of consecutive runs the condition must hold before an alert is created, which filters out single-run blips.</li>
</ul>
<h2 id="howdoyoudetectidledatastreamsinelasticsearch">How do you detect idle data streams in Elasticsearch?</h2>
<p>Threshold rules only fire while data keeps arriving. When an agent goes offline or an output breaks, the data stops, and a threshold rule has nothing to evaluate.</p>
<p>Many Elastic integrations include a dynamically generated <a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">idle data streams template</a> for exactly this case. It is named <code>[{Integration name}] Idle data streams</code> and appears in the same Alerting tab, though it is generated automatically rather than bundled with the integration. It alerts when no data is written to any of the integration's data stream patterns within a set period.</p>
<p>The NGINX OpenTelemetry packages do not include an idle data streams template, which is why no such template appears in the Alerting tab from Step 3. The end of this section covers what to do instead.</p>
<p>The default period is 24 hours, which is usually too long. A production service can go quiet for most of a day before you hear about it.</p>
<p>When you create the rule, drop the period to match how fast you need to know. Fifteen minutes to one hour works for a critical service. For a batch job, set a period comfortably longer than its run interval, so the quiet gaps between runs do not trigger it.</p>
<p>So why is this example left out? The template is generated from the data stream patterns an integration defines, and it is not generated for input-only packages. A content-only package like NGINX OpenTelemetry Assets defines no data streams of its own either. To catch silence in a collector-based setup like this, recreate the rule by hand with an <a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a>. Use the query DSL or KQL variant rather than ES|QL, because an ES|QL rule fires on returned rows and so cannot alert on the <em>absence</em> of data. Point it at the OTel data streams (<code>logs-nginx.access.otel-*</code>, or <code>metrics-nginxreceiver.otel-*</code>) and set the condition to fire when the number of matching documents <strong>is below 1</strong> over a window of, say, the last 15 minutes. That reproduces what an idle data streams template does, scoped to the data streams your collector writes.</p>
<h2 id="getstartedwithelasticintegrationalertingruletemplates">Get started with Elastic integration alerting rule templates</h2>
<p>Alerting rule templates turn alert setup into a few steps: send data, install the integration, create a rule from a template, and adjust the threshold. Treat the bundled thresholds as defaults to confirm, not numbers to trust blindly. And where an idle data streams template is available, reduce its 24-hour default so you find out quickly when a service goes silent.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://github.com/Delacrobix/Creating-alerts-from-OOTB-alerting-template">Companion repository</a>, to generate the NGINX demo data used here</li>
<li><a href="https://www.elastic.co/docs/reference/fleet/alerting-rule-templates">Alerting rule templates</a></li>
<li><a href="https://www.elastic.co/docs/reference/integrations/nginx_otel">NGINX OpenTelemetry Assets integration</a></li>
<li><a href="https://www.elastic.co/observability-labs/blog/nginx-opentelemetry-end-to-end-tracing">End-to-end tracing for NGINX with OpenTelemetry</a></li>
<li><a href="https://www.elastic.co/docs/explore-analyze/alerts-cases/alerts/rule-type-es-query">Elasticsearch query rule</a></li>
<li><a href="https://www.elastic.co/docs/reference/fleet/alert-templates">Elastic Agent built-in alerts</a>, for monitoring the agents themselves</li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/alerting-rule-templates-elastic-integrations</link>
    <guid isPermaLink="false">alerting-rule-templates-elastic-integrations</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Infrastructure Monitoring]]></category>
    <dc:creator><![CDATA[Jeffrey Rengifo]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt20e53c65a0321a28/6a85c997078290b06c321742/01-header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 31 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Your SLO is on fire; here's how to find the arsonist in Elastic Observability]]></title>
    <description><![CDATA[When SLO alerting flags a burn rate spike, follow the SLI from the alert detail page through bad event spans and trace waterfalls to find the exact dependency burning your SLO error budget, all without leaving the investigation.]]></description>
    <content:encoded><![CDATA[<p>The redesigned <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-an-slo-burn-rate-rule">SLO burn rate alert</a> detail page in Elastic Observability connects the alert to the SLI, the events behind it, and the traces that show which dependency is burning your <a href="https://www.elastic.co/docs/solutions/observability/incident-management/service-level-objectives-slos">service-level objective (SLO)</a> error budget.
You see when burn rate rose, compare good and bad event spans side by side, then follow the bad ones into the trace waterfall to find the failing hop, all without switching tools.</p>
<p>This walkthrough shows the full path using the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry Demo</a> (Astronomy Shop), where a failing shipping dependency drives every checkout SLI miss.
You can reproduce it by running the demo against Elastic Observability, defining an APM availability SLO on checkout, and introducing a failing dependency.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bc4f3056185ce56/6a7f1ae0448e4e7e625c0b70/slo-burn-rate-3.gif" alt="Comparing good and bad event spans for an SLO SLI" /></p>
<p><strong>Availability</strong></p>
<p>SLO burn rate analysis is available in Elastic Observability serverless today and is coming to Elastic Cloud Hosted and self-managed deployments in 9.5.</p>
<h2 id="prerequisitesforsloburnratealertinginelasticobservability">Prerequisites for SLO burn rate alerting in Elastic Observability</h2>
<p>You need an instrumented service and an SLO defined on its APM data in Elastic Observability.</p>
<ul>
<li><strong>Application instrumentation:</strong> one of the following:</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/apm-agents">Elastic APM agents</a></strong> for Java, .NET, Node.js, Python, PHP, Ruby, Go, and other supported languages</li>
<li><strong><a href="https://www.elastic.co/docs/reference/opentelemetry">Elastic Distributions of OpenTelemetry (EDOT)</a></strong> language SDKs</li>
<li><strong><a href="https://www.elastic.co/docs/solutions/observability/apm/opentelemetry">OpenTelemetry SDKs</a></strong> sending OTLP via the EDOT Collector, Elastic Agent, APM Server, or the Managed OTLP endpoint.
If you run a custom upstream Collector pipeline, include both the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmconnector"><code>elasticapm</code> connector</a> and the <a href="https://www.elastic.co/docs/reference/edot-collector/components/elasticapmprocessor"><code>elasticapm</code> processor</a>.
Those components ship with the EDOT Collector (or a custom EDOT-like build); they are not part of the standard OpenTelemetry Collector Contrib distribution.
For wiring details, see the <a href="https://www.elastic.co/docs/solutions/observability/get-started/opentelemetry/use-cases/upstream-collector">upstream collector setup</a>.</li>
<li><strong>SLO definition:</strong> an SLO based on <a href="https://www.elastic.co/docs/solutions/observability/incident-management/create-an-slo">APM latency or APM availability</a> for the service you want to protect.
Elastic creates a default burn rate alert rule when you save the SLO.</li>
<li><strong>Backend:</strong> Elastic Observability serverless today, or Elastic Stack 9.5 on Elastic Cloud Hosted and self-managed when 9.5 releases.</li>
</ul>
<h2 id="sloburnrateanalysiswalkthroughalerttofailingdependency">SLO burn rate analysis walkthrough: alert to failing dependency</h2>
<h3 id="step1reviewsloburnrateonthealertdetailpage">Step 1: Review SLO burn rate on the alert detail page</h3>
<p>Open the SLO burn rate alert detail page from the notification.</p>
<p>The chart shows when burn rate rose and how much error budget remains for the rolling period.
From there, open the SLI linked to the alert and decide whether you are looking at a short spike or sustained degradation before you dig into events.</p>
<p>In this example, the availability SLO for checkout is burning budget quickly, and the rise is recent, so the change likely landed in the last few hours rather than as long-running drift:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt81cb47bc3eea7e6f/6a7f1ae273d9bd661329df65/slo-burn-rate-1.gif" alt="SLO burn rate alert detail showing when burn rate rose" /></p>
<h3 id="step2checktheslierrorratedrivingsloerrorbudgetconsumption">Step 2: Check the SLI error rate driving SLO error budget consumption</h3>
<p>On the SLI view, check the error rate that feeds the SLO.</p>
<p>You can look at the SLI as <a href="https://www.elastic.co/docs/solutions/observability/apm/metrics">RED metrics</a> in the APM UI for a quick service-level picture, or open <strong>Traces in Discover</strong> when you want to filter, compare, or break down the spans that count toward the SLO.</p>
<p>Here, error rate on checkout has climbed, which matches the jump in availability SLO burn rate:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltab8b81391074958a/6a7f1ae63ce8e234c8cf57b6/slo-burn-rate-2.gif" alt="SLI error rate with RED metrics and Traces in Discover" /></p>
<h3 id="step3comparegoodandbadeventspansforthesli">Step 3: Compare good and bad event spans for the SLI</h3>
<p>Open the events for the SLI and switch between spans for good events and bad events.</p>
<p>Differences between those sets usually show up before you open individual traces.
In this case, bad events share a pattern that good events do not, which narrows where to look next:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4bc4f3056185ce56/6a7f1ae0448e4e7e625c0b70/slo-burn-rate-3.gif" alt="Good and bad event spans for the SLO SLI" /></p>
<h3 id="step4findthefailingspaninthetracewaterfall">Step 4: Find the failing span in the trace waterfall</h3>
<p>Open a few sample spans for bad events and view each trace in the waterfall.</p>
<p>That puts the failing span in the request path so you can see which hop is causing the SLI miss.
In this example, the failing span is a downstream call, not application logic inside checkout itself:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt1344742cca793033/6a7f1aebb43770167c4d7112/slo-burn-rate-4.gif" alt="Trace waterfall showing where bad events occur in the request path" /></p>
<h3 id="step5verifywhichdependencyisburningyoursloerrorbudget">Step 5: Verify which dependency is burning your SLO error budget</h3>
<p>Open the service that owns the bad span and check its dependencies.</p>
<p>Here the waterfall points to <strong>shipping</strong> and a failing call to <strong>quote-old</strong>.
Requests to that dependency fail on every attempt, so the SLO burn is driven by an external dependency rather than checkout code:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7900bf577690e2ea/6a7f1aee3ce8e24fa8cf57ba/slo-burn-rate-5.gif" alt="Trace timeline and dependency view showing 100% failed requests to quote-old" /></p>
<h2 id="sloburnrateinvestigationfromalerttorootcause">SLO burn rate investigation: from alert to root cause</h2>
<p>From an SLO burn rate alert in Elastic Observability, you can see when burn rate rose, open the linked SLI, compare good and bad event spans, inspect bad spans in the trace waterfall, and verify failing dependencies to find what is burning error budget.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/slo-burn-rate-analysis-trace-investigation</link>
    <guid isPermaLink="false">slo-burn-rate-analysis-trace-investigation</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[APM]]></category>
    <dc:creator><![CDATA[Roshan Gonsalkorale]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2c266fed417e9cd1/6a7f1af1b6b7347d70e491ca/header.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 28 Jul 2026 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Automating User Journeys for Synthetic Monitoring with MCP in Elastic]]></title>
    <description><![CDATA[This post explores how you can automatically create user journeys with Synthetic Monitoring in Elastic Observability, TypeScript, and FastMCP, and walks through the app and its workflow.]]></description>
    <content:encoded><![CDATA[<p><a href="https://www.elastic.co/docs/solutions/observability/synthetics">Synthetic Monitoring in Elastic Observability</a> enables you to track user pathways using a global testing infrastructure, emulating the full user path to measure the impact of web applications. It also provides comprehensive insight into your website's performance, functionality, and availability from development to production, allowing you to identify and resolve issues before they affect your customers. </p>
<p>One of the main components of Elastic's Synthetic Monitoring is the ability to create user journeys, which can be done with or without code. There is a <a href="https://github.com/elastic/synthetics">Synthetics agent,</a>, a CLI tool that guides you through the process of creating both heartbeat monitors and user journeys and deploying your code to Elastic Observability. If you are using code to create user journeys, you are using <a href="https://playwright.dev/">Playwright</a> under the hood with some additional configuration to make it easier to work with Elastic Observability. </p>
<p>To automatically create user journeys using TypeScript, you can create Playwright tests based on a prompt using <a href="https://www.warp.dev">Warp</a>, an AI-assisted terminal, <a href="https://deepmind.google/models/gemini/pro/">Gemini 2.5 Pro</a>, and <a href="https://modelcontextprotocol.io/docs/getting-started/intro">MCP</a>. This application was built using Python and <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, which wraps the synthetic agent to deploy browser tests to Elastic automatically. This blog post will guide you through how the application works, how to use it, and its development process. You can find the complete code on <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics">GitHub</a>. </p>
<h2 id="solutionoverview">Solution overview</h2>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt52957c4621a51a56/6a7f0d329090b04d0584ea0f/01-diagram.jpg" alt="diagram" /></p>
<p>Currently, this solution is set up to run inside Warp as an <a href="https://docs.warp.dev/knowledge-and-collaboration/mcp">MCP server</a>; however, you can also use another client, such as <a href="https://claude.ai/download">Claude Desktop</a> or <a href="https://cursormcp.com/en">Cursor</a>. From there, you create a Python script using <a href="https://gofastmcp.com/getting-started/welcome">FastMCP</a>, which allows you to create functions that are callable by an LLM. Within Warp, you can make a configuration file in JSON that enables you to point to your Python script and pass in all the environment variables you are working with. From there, you'll want to toggle agent mode and ask a question about creating synthetic testing or call the MCP function directly. There are many options for which LLM you can select, be sure to check out <a href="https://docs.warp.dev/agents/using-agents">Warp's documentation</a> to learn more about the options available.</p>
<p>After that, you should ask a question about creating synthetic testing or call the MCP function you are looking for. The following three functions can be used: </p>
<ul>
<li><p><code>diagnose_warp_mcp_config</code> 
Used for debugging environment variable issues that may arise. This function likely won't be needed unless there is an issue with your configuration.  </p></li>
<li><p><code>create_and_deploy_browser_test</code>
Will automatically create Playwright tests if given the test name, the URL you want to test, and a schedule. This approach uses a template-based method, rather than a machine learning-based method, and all the tests it outputs will appear similar.   </p></li>
<li><p><code>llm_create_and_deploy_test_from_prompt</code>
Similar to <code>create_and_deploy_browser_test</code>, but the main difference is that it uses an LLM to create tests based on a prompt you give it. The tests should reflect the prompt you provided. To run this function you'll provide a test name, URL, prompt, and schedule.</p></li>
</ul>
<h2 id="whycreatethissolutionasanmcpserver">Why create this solution as an MCP server?</h2>
<p>The reason this was developed as an MCP server, as opposed to just a standalone script or a standard CLI, is that it can be structured and interacted with in a more conversational manner. It enables an LLM to generate dynamic Playwright testing while maintaining consistent arguments, environment variables, and responses to ensure accuracy and reliability. Thus, it becomes a reliable workflow that other agents or developers can compose with additional tools. In other words, the MCP layer turns your LLM-based test authoring into a standardized, reusable capability instead of a one-off script. To learn more about the direction of MCP, be sure to check out our article on the <a href="https://www.elastic.co/search-labs/blog/mcp-current-state">topic.</a></p>
<h2 id="implementationconsiderations">Implementation considerations</h2>
<p>When creating a solution like this one, one thing to be mindful of is your use of tokens. An early version of this solution took approximately twenty minutes to create synthetic tests and ultimately led to severe rate-limiting. </p>
<p>Another issue faced during the building process was striking a balance between creating a template that facilitates the creation of a Playwright script and having an LLM create Playwright scripts based on prompts that didn't feel cookie-cutter. While using a more LLM approach an issue faced was that the scripts often didn't work or were based on parameters that didn't exist and a more templated approach was more reliable but felt repetitive. The final version of this solution attempted to balance this by using elements of the template while adjusting the LLM parameter of temperature, which controls the randomness or creativity of a large language model's output. </p>
<p>While testing this solution, a failing test also emerged that required navigating past a pop-up. In more complex cases, this may serve as a building block that requires additional domain knowledge to create a complete passing Playwright test.</p>
<h2 id="howtogetstarted">How to get started</h2>
<h3 id="prerequisites">Prerequisites</h3>
<ul>
<li>The version of Python that is used is Python 3.12.1 but you can use any version of Python higher than 3.10.   </li>
<li>This application uses Elastic Observability version 9.1.2, but you can use any version of Elastics Observability that is higher than 8.10. You can also use <a href="https://www.elastic.co/cloud/serverless">Elastic Cloud Serverless</a> as well.  </li>
<li>You will also need an OpenAI API key to use the LLM capabilities of this application. You will want to configure an environment variable for your OpenAI API Key, which you can find on the API keys page in <a href="https://platform.openai.com/api-keys">OpenAI's developer portal</a>.</li>
</ul>
<h3 id="step1installthepackagesandclonetherepository">Step 1: Install the packages and clone the repository</h3>
<p>In order for this MCP server to run locally you will need to install the the following packages: </p>
<pre><code>pip install fastmcp openai
npm install -g playwright @elastic/synthetics
</code></pre>
<p>You will use <a href="https://gofastmcp.com/getting-started/welcome">FastMCP 2.0</a> to create the MCP server, and <a href="https://github.com/openai/openai-python">OpenAI</a> to generate tests based on prompts that you provide. Additionally, you will want to clone the repository to obtain a local copy of the server.</p>
<h3 id="step2setupaconfigurationfileinwarp">Step 2: Set up a configuration file in Warp</h3>
<p>Inside of Warp, you will want to go to the side panel, where it says MCP servers and where it says “add”. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc67bb0095d239f6f/6a7f0d359090b0390b84ea15/02-add-mcp.jpg" alt="Add MCP Server" /></p>
<p>After that, you will be prompted to add a JSON configuration file that should resemble the following. Be sure to add your own Kibana URL, update the correct path, and include your own keys and tokens.</p>
<pre><code>{
 "elastic-synthetics": {
   "command": "python",
   "args": ["elastic_synthetics_server.py"],
   "env": {
     "PYTHONPATH": ".",
     "ELASTIC_KIBANA_URL": "https://your-kibana-url.elastic-cloud.com",
     "ELASTIC_API_KEY": "your-api-key-here",
     "ELASTIC_PROJECT_ID": "mcp-synthetics-demo",
     "ELASTIC_SPACE": "default",
     "ELASTIC_AUTO_PUSH": "true",
     "ELASTIC_USE_JAVASCRIPT": "false",
     "ELASTIC_INSTALL_DEPENDENCIES": "true",
     "OPENAI_API_KEY": "sk-your-openai-key",
     "LLM_MODEL": "gpt-4o"
   },
   "working_directory": "/path/to/your/file",
   "start_on_launch": true 
   }
}
</code></pre>
<h3 id="step3askaquestionorcallthetoolsdirectly">Step 3: Ask a question or call the tools directly</h3>
<p>Now that you've set up locally, you will want to toggle agent mode and select the LLM you wish to use. The reason why Gemini-Pro-2.5 was chosen for this blog post is that it provides a straightforward answer, while other LLMs selected returned a very lengthy response. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt645bcec459cec042/6a7f0d3833fa8a3c4220272a/03-agent-mode.jpg" alt="Agent mode" /></p>
<p>To start using the MCP tools, from your MCP server, you can ask a question that contains the test name, URL, prompt, and schedule. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt69de803d89d49ee9/6a7f0d3b1967ea6ee8330787/04-full-question-answer.jpg" alt="Full question and answer" /></p>
<p>You can also call the directly by typing <code>llm_create_and_deploy_test_from_prompt()</code> and the program will prompt you for the relevant details:<br />
<img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt400063dcb9e2ed05/6a7f0d3d33fa8a2ba1202730/05-call-mcp-tool.jpg" alt="Call MCP Tool" /></p>
<p>Inside Kibana, you should see your monitor listed if you click under Applications and select Monitors listed under Synthetics. You can also find a link to your monitor in the response of your MCP tool. </p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt0c8e1e9e79a7ca38/6a7f0d4073d9bd5cca29db4d/06-kibana-monitors.jpg" alt="Monitors in Kibana" /></p>
<h2 id="whatsgoingoninside">What's Going On Inside</h2>
<p>This code sample consists of three primary functions, which are MCP tools that you can call from your MCP client, including <code>diagnose_warp_mcp_config</code>, <code>create_and_deploy_browser_test</code> and <code>llm_create_and_deploy_test_from_prompt</code>.</p>
<h3 id="debuggingenvironmentissues">Debugging environment issues</h3>
<p>There were various issues that came up while creating this application around environment variable loading, so there was a need to create an MCP that could be called depending on errors that may be present. </p>
<p>The tool <code>diagnose_warp_mcp_config</code> kicks off with a decorator <code>@mcp.tool()</code> which allows it to be called and listed in the list of available tools. This tool is designed to help debug issues with Elastic-specific environment variables for troubleshooting purposes. First, it loads in the environment variables and looks for the Elastic specific variables, after it does some security masking so it doesn't show any variables and hides sensitive information like API keys in the output, showing only the first eight characters followed by "…". This tool determines if the minimum required credentials (Kibana URL and API Key) are present to proceed with deployment and provides a report letting you know to address any issues that may exist. </p>
<pre><code>@mcp.tool()
def diagnose_warp_mcp_config() -&gt; Dict[str, Any]:
   """Diagnose Warp MCP environment configuration for Elastic Synthetics"""
   try:
       env_vars = load_env_from_warp_mcp()

       # Check for required variables
       kibana_url = env_vars.get('ELASTIC_KIBANA_URL') or env_vars.get('KIBANA_URL')
       api_key = env_vars.get('ELASTIC_API_KEY') or env_vars.get('API_KEY')
       project_id = env_vars.get('ELASTIC_PROJECT_ID') or env_vars.get('PROJECT_ID')
       space = env_vars.get('ELASTIC_SPACE') or env_vars.get('SPACE', 'default')

       # Mask sensitive values for display
       masked_vars = {}
       for key, value in env_vars.items():
           if 'API_KEY' in key or 'TOKEN' in key:
               masked_vars[key] = f"{value[:8]}..." if value and len(value) &gt; 8 else "***"
           else:
               masked_vars[key] = value

       deployment_ready = bool(kibana_url and api_key)

       return safe_json_response({
           "status": "success",
           "environment_variables": masked_vars,
           "required_check": {
               "kibana_url": bool(kibana_url),
               "api_key": bool(api_key),
               "project_id": bool(project_id),
               "space": bool(space)
           },
           "deployment_ready": deployment_ready,
           "recommendations": [
               "Environment variables detected" if env_vars else "No environment variables found",
               "Kibana URL configured" if kibana_url else "Missing ELASTIC_KIBANA_URL or KIBANA_URL",
               "API Key configured" if api_key else "Missing ELASTIC_API_KEY or API_KEY",
               "Ready for deployment" if deployment_ready else "Missing required credentials"
           ]
       })

   except Exception as e:
       return safe_json_response({
           "status": "error",
           "error": str(e),
           "error_type": type(e).__name__
       })
</code></pre>
<h3 id="creatingsynthetictestsbasedonatemplate">Creating synthetic tests based on a template</h3>
<p>While developing this solution to generate tests based on a prompt, the process wasn't always smooth. Early versions encountered issues with accuracy, hallucinations, and the creation of loops. To make progress, a version that relied on creating a test template to verify the mechanics of the solution, such as whether the test could pass and be deployed to Elastic correctly, was a logical next step. </p>
<p>This solution automates the entire process of creating a synthetic browser test that will regularly check if a website is working correctly, then deploys it to Elastic Observability Synthetics. Similar to <code>diagnose_warp_mcp_config</code>, the MCP tool <code>create_and_deploy_browser_test</code> starts with the decorator <code>@mcp.tool()</code> and checks to make sure that the proper environment variables are loaded. </p>
<p>From there, it creates a TypeScript test file that is based on templates and generates dynamic test steps based on the target website's characteristics, including navigating to the website, verifying the page title exists, checking page load performance, taking a screenshot, verifying page content is visible, and finally saves the test file in a <code>synthetic_tests</code> directory.</p>
<p>Finally, it wraps Elastic's CLI tool <code>@elastic/synthetics</code> to push the test to Kibana, allowing you to set which geographic locations to run tests from, how often to run the test, and the project and workspace settings.</p>
<p>You check out the full code for this MCP tool <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics/blob/main/elastic_synthetics_server.py#L943">here.</a></p>
<h3 id="creatingsynthetictestsbasedonaprompt">Creating synthetic tests based on a prompt</h3>
<p>While creating browser tests based on a templated approach is a good starting point, it felt generic and cookie-cutter. But it made a helpful structure to build an LLM-based function on top of.</p>
<p>The MCP tool <code>llm_create_and_deploy_test_from_prompt</code> begins by ensuring that basic parameters, including locations, schedule, and directories, are listed. Additionally, it aims to learn more about the target website to inform the AI and initialize the OpenAI client and model, which is GPT-4o. </p>
<p>After setting up the LLM, it converts natural language requests into actual Playwright test code, then cleans and validates the AI-generated code to prevent issues like injection attacks or malformed syntax. It draws inspiration from the templated approach, wrapping AI-generated steps within a proven, reliable test framework template. Finally, it deploys the test to Elastic in a similar manner to the previous tool. </p>
<p>You can find the code for this tool <a href="https://github.com/JessicaGarson/MCP-Elastic-Synthetics/blob/main/elastic_synthetics_server.py#L1559">here</a>.</p>
<h2 id="conclusionandnextsteps">Conclusion and next steps</h2>
<p>Synthetic monitoring in Elastic Observability makes it easy to test complete user journeys and keep your site reliable, with simple setup and a Playwright integration. A tool like this can provide a starting point for tests that you can iterate on after.</p>
<p>A solution like this is just the start of an MCP implementation that automatically generates Playwright tests for you and can be expanded in the future to include heartbeat monitors, utilize the <a href="https://github.com/microsoft/playwright-mcp">Playwright MCP server</a>, or consider experimenting with <a href="https://www.anthropic.com/news/claude-for-chrome">Claude for Chrome</a> to create synthetic testing.  </p>
<p>Check out more articles on <a href="https://www.elastic.co/observability-labs/blog/category/infrastructure-monitoring">Observability Labs on Infrastructure Monitoring</a></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/mcp-elastic-synthetics</link>
    <guid isPermaLink="false">mcp-elastic-synthetics</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[AI]]></category>
    <category><![CDATA[LLM Observability]]></category>
    <dc:creator><![CDATA[Jessica Garson]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt7f7fbdc61045d078/6a7f0d43fc63ab4f3364cc71/retro.jpg" length="0" type="image/jpeg"/>
    <pubDate>Wed, 17 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[From Uptime to Synthetics in Elastic: Your migration Playbook]]></title>
    <description><![CDATA[Effortlessly migrate your existing Uptime TCP, ICMP, and HTTP monitors to Elastic Synthetics with this comprehensive guide, leveraging Private Locations and Synthetics Projects for efficient, future-proof monitoring.]]></description>
    <content:encoded><![CDATA[<p>Have you seen the warning that Uptime is deprecated and want to know how to easily migrate to Synthetics? Then you are in the right place. 
Starting with version 8.15.0, uptime checks have been deprecated in favor of synthetic monitoring.</p>
<p>Many users may have a large number of TCP, ICMP, and HTTP monitors and need to migrate them to Synthetics. In this guide, we will explain how to perform this migration easily while ensuring that it will be future-proof or able to develop more advanced checks such as <a href="https://www.elastic.co/docs/solutions/observability/synthetics/#monitoring-synthetics">Browser monitors</a>.</p>
<p>First, we must consider the number of monitors to migrate; if the number is small, the easiest way would be to do it manually through the <a href="https://www.elastic.co/docs/solutions/observability/synthetics/create-monitors-ui">Synthetics UI</a>. However, in this guide we will assume that we have dozens or hundreds of monitors to migrate, and doing it manually in the Synthetics UI is not an option.</p>
<h2 id="privatelocation">Private Location</h2>
<p>Traditionally, uptime monitors required a <a href="https://www.elastic.co/docs/reference/beats/heartbeat/">Heartbeat</a> to be deployed in your infrastructure, which indirectly allowed you to monitor endpoints or hosts on your private network. If this is still a requirement, you will need to either configure <a href="https://www.elastic.co/docs/solutions/observability/synthetics/monitor-resources-on-private-networks">Private Location</a> or allow Elastic’s global managed infrastructure to <a href="https://www.elastic.co/docs/solutions/observability/synthetics/monitor-resources-on-private-networks#monitor-via-access-control">access your private endpoints</a> (only on <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/cloud-hosted">ECH</a> &amp; <a href="https://www.elastic.co/docs/deploy-manage/deploy/elastic-cloud/serverless">Serverless</a>).</p>
<p>In this guide, we will use Private Locations, which will allow you to monitor both internal and external resources. More details can be found here: <a href="https://www.elastic.co/docs/solutions/observability/synthetics/monitor-resources-on-private-networks#monitor-via-private-agent">Monitor resources on private networks</a></p>
<h3 id="step1setupfleetserverandelasticagent">Step 1: Set up Fleet Server and Elastic Agent</h3>
<p>Private Locations are simply Elastic Agents enrolled in Fleet and managed through an agent policy. </p>
<p>If you don't have a Fleet Server yet, start <a href="https://www.elastic.co/docs/reference/fleet/fleet-server">setting up a Fleet Server</a>. This step is not necessary if you use ECH, as it comes by default.</p>
<p>Next, you will need to create an Agent Policy. Go to <strong>Observability → Monitors (Synthetics) → Settings (top right) → Private Location → + Create Location</strong></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt64b587cc01e23833/6a7f1bf7fc63ab348a64d0a6/create-private-location.png" alt="Create Private Location" /></p>
<p>Fill in the fields and create a new policy for this Private Location. It is important to know that Private Location should be set up against an agent policy that runs on a <strong>single</strong> Elastic Agent. </p>
<h3 id="step2deploytheelasticagent">Step 2: Deploy the Elastic Agent</h3>
<p>Now we need to deploy the Elastic Agent that will be responsible for running all the monitors. We can use the same host we were using for Heartbeat. There is only one requirement: we must be able to run Docker containers, since to take advantage of all the features of Synthetics, we must use the <code>elastic-agent-complete</code> Docker Image.</p>
<ol>
<li><p>Go to <strong>Fleet –&gt; Enrollment tokens</strong> and note the enrollment token relevant to the policy you just created for the Private Location. Now go to <strong>Settings</strong> and note the default Fleet server host URL.</p></li>
<li><p>On the host, run the following commands. For more information on running Elastic Agent with Docker, refer to Run Elastic Agent in a container.</p></li>
</ol>
<pre><code>docker run \
&amp;nbsp;&amp;nbsp;--env FLEET_ENROLL=1 \
&amp;nbsp;&amp;nbsp;--env FLEET_URL={fleet_server_host_url} \
&amp;nbsp;&amp;nbsp;--env FLEET_ENROLLMENT_TOKEN={enrollment_token} \
&amp;nbsp;&amp;nbsp;--cap-add=NET_RAW \
&amp;nbsp;&amp;nbsp;--cap-add=SETUID \
&amp;nbsp;&amp;nbsp;--rm docker.elastic.co/elastic-agent/elastic-agent-complete:9.3.2
</code></pre>
<h2 id="syntheticproject">Synthetic Project</h2>
<p>At this point, we already have the location from which our Synthetic monitors will run. Now we need to load our Uptime monitors as Synthetics.</p>
<p>As we mentioned earlier, there are two ways to do this: either manually through the Synthetics UI or through a Synthetics Project.
In our case, since we have so many monitors to migrate and don't want to do it manually, we will use <a href="https://www.elastic.co/docs/solutions/observability/synthetics/create-monitors-with-projects">Synthetics Projects</a>. </p>
<p>The great thing about Synthetics Project is that it has some backward compatibility with the definition of monitors in <code>heartbeat.yml</code> and we will be leveraging it.</p>
<h3 id="whatssyntheticsproject">What's Synthetics project?</h3>
<p>Synthetics project is the most powerful and flexible way to manage synthetic monitors in Elastic, based on the Infrastructure as Code principle and compatible with Git-Ops flows. Instead of configuring monitors from the interface, you define them as code: .yml files for lightweight monitors and JavaScript or TypeScript scripts for browser-type monitors (journeys).</p>
<p>This approach allows you to structure your monitors in a repository, version them with Git, validate them, and deploy them automatically using CI/CD flows, providing traceability, reviews, and consistent deployments.</p>
<h3 id="step3initializeyoursyntheticsproject">Step 3: Initialize your Synthetics project</h3>
<p>You will no longer need to connect to the hosts where you deployed the Elastic Agent, as the remaining steps can be performed locally as long as you have connectivity to Kibana!</p>
<p>Since Synthetics Projects is based on Node.js, make sure you have it <a href="https://nodejs.org/en/download">installed</a>. </p>
<ol>
<li>Install the package:</li>
</ol>
<pre><code>npm install -g @elastic/synthetics
</code></pre>
<ol>
<li>Confirm your system is setup correctly:</li>
</ol>
<pre><code>npx @elastic/synthetics -h
</code></pre>
<ol>
<li>Start by creating your first Synthetics project. Run the command below to create a new Synthetics project named <code>synthetic-project-test</code> in the current directory.</li>
</ol>
<pre><code>npx @elastic/synthetics init synthetic-project-test
</code></pre>
<ol>
<li><p>Follow the prompt instructions to configure the default variables for your Synthetics project. Make sure to at least <strong>select your Private Location.</strong> Once that’s done, set the <code>SYNTHETICS_API_KEY</code> environment variable in your terminal, which allows the project to authenticate with Kibana.</p></li>
<li><p>To generate an API key go to Synthetics Kibana.</p></li>
<li><p>Click <strong>Settings</strong>.</p></li>
<li><p>Switch to the <strong>Project API Keys</strong> tab.</p></li>
<li><p>Click <strong>Generate Project API key</strong>.</p></li>
</ol>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbf4abd9defe75bad/6a7f1bfa73d9bd03a929df94/generate-api-key.png" alt="Generate API Key" /></p>
<p>More details for all the steps can be found here: <a href="https://www.elastic.co/docs/solutions/observability/synthetics/create-monitors-with-projects#synthetics-get-started-project-create-a-synthetics-project">Create monitors with a Synthetics project</a></p>
<h3 id="step4addyourheartbeatymlfiles">Step 4: Add your <code>heartbeat.yml</code> files</h3>
<p>Once the project is initialized, access the folder it has created and take a look at the project structure:</p>
<ul>
<li><p><code>journeys</code> is where you’ll add .ts and .js files defining your browser monitors. It currently contains files defining sample monitors.</p></li>
<li><p><code>lightweight</code> is where we’ll add our heartbeat.yml files defining our lightweight monitors. It currently contains a file defining sample monitors.</p></li>
</ul>
<p>Therefore, all we have to do is copy our <code>heartbeat.yml</code> files to this lightweight folder. Before copying <code>heartbeat.yml</code>, keep in mind that we don't need all the content, we are only interested in the <code>heartbeat.monitors</code> part. \
We recommend considering splitting the file into logical groups. Instead of maintaining a single large YAML file, you could create multiple smaller YAML files, with each file representing either a single check or a group of related checks. This approach may simplify management and improve compatibility with GitOps workflows.\
Each YAML file should look like this:</p>
<pre><code>carles@synthetics-migration:synthetic-project-test/lightweight# cat heartbeat.yml

heartbeat.monitors:
- type: icmp
&amp;nbsp;&amp;nbsp;schedule: '@every 10s'
&amp;nbsp;&amp;nbsp;hosts: ["localhost"]
&amp;nbsp;&amp;nbsp;id: my-icmp-service-synth
&amp;nbsp;&amp;nbsp;name: My ICMP Service - Synthetic
- type: tcp
&amp;nbsp;&amp;nbsp;schedule: '@every 10s'
&amp;nbsp;&amp;nbsp;hosts: ["myremotehost:8123"]
&amp;nbsp;&amp;nbsp;mode: any
&amp;nbsp;&amp;nbsp;id: my-tcp-service-synth
&amp;nbsp;&amp;nbsp;name: My TCP Service Synthetic
- type: http
&amp;nbsp;&amp;nbsp;schedule: '@every 10s'
&amp;nbsp;&amp;nbsp;urls: ["http://elastic.co"]
&amp;nbsp;&amp;nbsp;id: my-http-service-synth
&amp;nbsp;&amp;nbsp;name: My HTTP Service Synthetic
</code></pre>
<p>What we just did is define different ICMP, TCP, and HTTP checks as code.</p>
<p>Now we need to ask Synthetics project to create the monitors in Kibana based on what we have defined in our YAML files:</p>
<pre><code>npx @elastic/synthetics push --auth $SYNTHETICS_API_KEY --url &lt;kibana-url&gt;
</code></pre>
<p>Unfortunately, we do not support a 1-to-1 mapping of the heartbeat schema to the lightweight schema, so you may encounter some errors during the execution of this command. One example is the definition of <code>schedule</code>. Heartbeat supports the use of crontab expressions, but Project requires the use of <code>@every</code> syntax.</p>
<p>If no syntax errors were found, the command output will show that the monitors have been successfully created in Kibana!</p>
<p>Then, go to <strong>Synthetics</strong> in Kibana. You should see your newly pushed monitors running. You can also go to the Management tab to see the monitors' configuration settings.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/uptime-to-synthetics-guide</link>
    <guid isPermaLink="false">uptime-to-synthetics-guide</guid>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Carles Salvador]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt77a5b002e5de61f1/6a7f1bfde02fac67655d69d9/blog-header.png" length="0" type="image/png"/>
    <pubDate>Thu, 11 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Smarter Alerting Arrives with Faster Triage, Clearer Groupings, and Actionable Guidance]]></title>
    <description><![CDATA[Exploring the latest enhancements in Elastic Stack alerting, including improved related alert grouping, linking dashboards to alert rules, and embedding investigation guides into alerts.]]></description>
    <content:encoded><![CDATA[<p>In the 9.1 release, we've made significant upgrades to alerting to help SREs and operators cut through the noise, understand what's happening faster, and take meaningful action with less guesswork.</p>
<p>Here's what's new:</p>
<h2 id="improvedrelatedalertgroupingwithrelevancescoringreasoning">Improved Related Alert Grouping with Relevance Scoring &amp; Reasoning</h2>
<p>We've enhanced our related alert detection to go beyond surface-level correlations. Alerts are now grouped based on a relevance score that reflects the strength of their relationship across dimensions like:</p>
<ul>
<li><strong>Shared entities or resources</strong> (e.g. same host, pod, or service)</li>
<li><strong>Temporal proximity</strong> (alerts firing within a suspiciously short window)</li>
<li><strong>Signal similarity</strong> (e.g. spikes in logs, metrics, and traces that point to the same failure mode)</li>
</ul>
<p>More importantly, we now <strong>show the why</strong>. You'll see why an alert is grouped, whether it's sharing the same Kubernetes pod, has similar log patterns, or was triggered by the same upstream anomaly. This gives users confidence in the grouping logic and accelerates root cause analysis.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd6b9a9db1dd737ce/6a7f093f6c6eac5f4ff13f99/alerting-1.jpg" alt="Related Alerts" /></p>
<h2 id="linkdashboardstoalertrulesandgetsmartsuggestions">Link Dashboards to Alert Rules and Get Smart Suggestions</h2>
<p>You can now <strong>link dashboards directly to your alert rules</strong>, giving responders an instant visual lens into the metrics or logs that matter most for that alert. No more scrambling to remember which dashboard to check — just click and go.</p>
<p>And we've made this smarter too: Elastic will now <strong>suggest relevant dashboards</strong> based on the alert's source, rule logic, or monitored entities, helping users land on the right view without needing to configure anything upfront.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4ae716d60bfc0c17/6a7f0942ea068d40daf09d0d/alerting-2.jpg" alt="Related Alerting Dashboards" /></p>
<h2 id="investigationguidesembeddedintoalerts">Investigation Guides Embedded Into Alerts</h2>
<p>Every alert can now be configured with an <strong>investigation guide</strong>, a set of pre-configured, context-aware instructions or next steps tailored to the alert. Think of it as a playbook that's embedded right where and when you need it.</p>
<p>Use it to:</p>
<ul>
<li>Document your team's runbooks and standard triage steps or link to existing runbooks</li>
<li>Guide junior engineers or on-call responders through unfamiliar territory</li>
<li>Automate the first few steps of root cause analysis</li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt777147fbc5bc822d/6a7f0945b4377011ea4d6b39/alerting-3.jpg" alt="Investigation Guide" /></p>
<h2 id="whythismatters">Why This Matters</h2>
<p>These changes are all about reducing time to detect (MTTD) and time to resolve (MTTR). By:</p>
<ul>
<li>Grouping alerts more intelligently (and transparently)</li>
<li>Giving you the dashboards you need, when you need them</li>
<li>Embedding action-oriented guides in every alert</li>
</ul>
<p>We're bringing you closer to a truly streamlined incident response workflow; No swivel-chairing, no guesswork, just clarity.</p>
<p>Additionally, look at some of our other articles on Elastic Observability Labs related to analysis:</p>
<ul>
<li><p><a href="https://www.elastic.co/observability-labs/blog/ai-assistant">Using the AI Assistant in Elastic Observability to Accelerate Root Cause Analysis</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/category/logs-analytics">All of the log analytics features in Elastic Observability</a></p></li>
<li><p><a href="https://www.elastic.co/observability-labs/blog/opentelemetry">Our latest on OpenTelemetry support in Elastic Observability</a></p></li>
</ul>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/elastic-stack-observability-alerting-upgrade</link>
    <guid isPermaLink="false">elastic-stack-observability-alerting-upgrade</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <dc:creator><![CDATA[Drew Post]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f615dc5059d80e2/6a7f0948e88c652ff600b528/cover-alerting.jpg" length="0" type="image/jpeg"/>
    <pubDate>Thu, 04 Sep 2025 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Build better Service Level Objectives (SLOs) from logs and metrics]]></title>
    <description><![CDATA[To help manage operations and business metrics, Elastic Observability's SLO (Service Level Objectives) feature was introduced in 8.12. This blog reviews this feature and how you can use it with Elastic's AI Assistant to meet SLOs.]]></description>
    <content:encoded><![CDATA[<p>In today's digital landscape, applications are at the heart of both our personal and professional lives. We've grown accustomed to these applications being perpetually available and responsive. This expectation places a significant burden on the shoulders of developers and operations teams.</p>
<p>Site reliability engineers (SREs) face the challenging task of sifting through vast quantities of data, not just from the applications themselves but also from the underlying infrastructure. In addition to data analysis, they are responsible for ensuring the effective use and development of operational tools. The growing volume of data, the daily resolution of issues, and the continuous evolution of tools and processes can detract from the focus on business performance.</p>
<p>Elastic Observability offers a solution to this challenge. It enables SREs to integrate and examine all telemetry data (logs, metrics, traces, and profiling) in conjunction with business metrics. This comprehensive approach to data analysis fosters operational excellence, boosts productivity, and yields critical insights, all of which are integral to maintaining high-performing applications in a demanding digital environment.</p>
<p>To help manage operations and business metrics, Elastic Observability's SLO (Service Level Objectives) feature was introduced in <a href="https://www.elastic.co/guide/en/observability/8.12/slo.html">8.12</a>. This feature enables setting measurable performance targets for services, such as <a href="https://sre.google/sre-book/monitoring-distributed-systems/">availability, latency, traffic, errors, and saturation or define your own</a>. Key components include:</p>
<ul>
<li><p>Defining and monitoring SLIs (Service Level Indicators)</p></li>
<li><p>Monitoring error budgets indicating permissible performance shortfalls</p></li>
<li><p>Alerting on burn rates showing error budget consumption</p></li>
</ul>
<p>Users can monitor SLOs in real-time with dashboards, track historical performance, and receive alerts for potential issues. Additionally, SLO dashboard panels offer customized visualizations.</p>
<p>Service Level Objectives (SLOs) are generally available for our Platinum and Enterprise subscription customers.</p>
<div>
    
</div>
<p>In this blog, we will outline the following:</p>
<ul>
<li><p>What are SLOs? A Google SRE perspective</p></li>
<li><p>Several scenarios of defining and managing SLOs</p></li>
</ul>
<h2 id="servicelevelobjectiveoverview">Service Level Objective overview</h2>
<p>Service Level Objectives (SLOs) are a crucial component for Site Reliability Engineering (SRE), as detailed in <a href="https://sre.google/sre-book/table-of-contents/">Google's SRE Handbook</a>. They provide a framework for quantifying and managing the reliability of a service. The key elements of SLOs include:</p>
<ul>
<li><p><strong>Service Level Indicators (SLIs):</strong> These are carefully selected metrics, such as uptime, latency, throughput, error rates, or other important metrics, that represent the aspects of the service and are important from an operations or business perspective. Hence, an SLI is a measure of the service level provided (latency, uptime, etc.), and it is defined as a ratio of good over total events, with a range between 0% and 100%.</p></li>
<li><p><strong>Service Level Objective (SLO):</strong> An SLO is the target value for a service level measured as a percentage by an SLI. Above the threshold, the service is compliant. As an example, if we want to use service availability as an SLI, with the number of successful responses at 99.9%, then any time the number of failed responses is &gt; .1%, the SLO will be out of compliance.</p></li>
<li><p><strong>Error budget:</strong> This represents the threshold of acceptable errors, balancing the need for reliability with practical limits. It is defined as 100% minus the SLO quantity of errors that is tolerated.</p></li>
<li><p><strong>Burn rate:</strong> This concept relates to how quickly the service is consuming its error budget, which is the acceptable threshold for unreliability agreed upon by the service providers and its users.</p></li>
</ul>
<p>Understanding these concepts and effectively implementing them is essential for maintaining a balance between innovation and reliability in service delivery. For more detailed information, you can refer to <a href="https://sre.google/workbook/slo-document/">Google's SRE Handbook</a>.</p>
<p>One main thing to remember is that SLO monitoring is <em>not</em> incident monitoring. SLO monitoring is a proactive, strategic approach designed to ensure that services meet established performance standards and user expectations. It involves tracking Service Level Objectives, error budgets, and the overall reliability of a service over time. This predictive method helps in preventing issues that could impact users and aligns service performance with business objectives.</p>
<p>In contrast, incident monitoring is a reactive process focused on detecting, responding to, and mitigating service incidents as they occur. It aims to address unexpected disruptions or failures in real time, minimizing downtime and impact on service. This includes monitoring system health, errors, and response times during incidents, with a focus on rapid response to minimize disruption and preserve the service's reputation.</p>
<p>Elastic®’s SLO capability is based directly off the Google SRE Handbook. All the definitions and semantics are utilized as described in Google’s SRE handbook. Hence users can perform the following on SLOs in Elastic:</p>
<ul>
<li><p>Define an SLO on an SLI such as KQL (log based query), service availability, service latency, custom metric, histogram metric, or a timeslice metric. Additionally, set the appropriate threshold.</p></li>
<li><p>Utilize occurrence versus time slice based budgeting. Occurrences is the number of good events over the number of total events to compute the SLO. Timeslices break the overall time window into slammer slices of a defined duration and compute the number of good slices over the total slices to compute the SLO. Timeslice targets are more accurate and useful when calculating things like a service’s SLO when trying to meet agreed upon customer targets.</p></li>
<li><p>Manage all the SLOs in a singular location.</p></li>
<li><p>Trigger alerts from the defined SLO, whether the SLI is off, burn rate is used up, or the error rate is X.</p></li>
<li><p>Create unique service level dashboards with SLO information for a more comprehensive view of the service.</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta3810c425fa6d9ef/6a7f1a69b43770d02c4d70fc/1-slo-blog.png" alt="Create alerts" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f526ae1d0618b26/6a7f1a6ce02fac5abb5d69b3/2-slo-blog.png" alt="Create dashboards" /></p>
<p>SREs need to be able to manage business metrics.</p>
<h2 id="slosbasedonlogsnginxavailability">SLOs based on logs: NGINX availability</h2>
<p>Defining SLOs does not always mean metrics need to be used. Logs are a rich form of information, even when they have metrics embedded in them. Hence it’s useful to understand your business and operations status based on logs.</p>
<p>Elastic allows you to create an SLO based on specific fields in the log message, which don’t have to be metrics. A simple example is a simple multi-tier app that has a web server layer (nginx), a processing layer, and a database layer.</p>
<p>Let’s say that your processing layer is managing a significant number of requests. You want to ensure that the service is up properly. The best way is to ensure that all http.response.status_code are less than 500. Anything less ensures the service is up and any errors (like 404) are all user or client errors versus server errors.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte8b306f68814e9fa/6a7f1a6fe02fac7d295d69b7/3-slo-blog.png" alt="expanded document" /></p>
<p>If we use Discover in Elastic, we see that there are close to 2M log messages over a seven-day time frame.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt4f8638fe990d421f/6a7f1a72c2e9141e31016ff0/4-slo-blog.png" alt="17k" /></p>
<p>Additionally, the number of messages with http.response.status_code &gt; 500 is minimal, like 17K.</p>
<p>Rather than creating an alert, we can create an SLO with this query:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f476a5816f7c858/6a7f1a7533fa8a3787202b7e/5-slo-blog.png" alt="edit SLO" /></p>
<p>We chose to use occurrences as the budgeting method to keep things simple.</p>
<p>Once defined, we can see how well our SLO is performing over a seven-day time frame. We can see not only the SLO, but also the burn rate, the historical SLI, and error budget, and any specific alerts against the SLO.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2d749a94c689ccb3/6a7f1a7877b034ab7d3ff907/6-slo-blog.png" alt="SLOs" /></p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltde894037a4de1f6d/6a7f1a7bea068d5abaf0a2cb/7-slo-blog.png" alt="nginx server availability " /></p>
<p>Not only do we get information about the violation, but we also get:</p>
<ul>
<li><p>Historical SLI (7 days)</p></li>
<li><p>Error budget burn down</p></li>
<li><p>Good vs. bad events (24 hours)</p></li>
</ul>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt23453245544e2b0d/6a7f1a7f5967e551ff5dd6cf/8-slo-blog.png" alt="Percentages" /></p>
<p>We can see how we’ve easily burned through our error budget.</p>
<p>Hence something must be going on with nginx. To investigate, all we need to do is utilize the <a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">AI Assistant</a>, and use its natural language interface to ask questions to help analyze the situation.</p>
<p>Let’s use Elastic’s AI Assistant to analyze the breakdown of http.response.status_code across all the logs from the past seven days. This helps us understand how many 50X errors we are getting.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2e3dad6ad1a36d7f/6a7f1a8233fa8a6c82202b82/9-slo-blog.png" alt="count of http response status code" /></p>
<p>As we can see, the number of 502s is minimal compared to the number of overall messages, but it is affecting our SLO.</p>
<p>However, it seems like Nginx is having an issue. In order to reduce the issue, we also ask the AI Assistant how to work on this error. Specifically, we ask if there is an internal runbook the SRE team has created.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltc46a6e3bc18f8d57/6a7f1a8542a117ce0295c305/10-slo-blog.png" alt="ai assistant thread" /></p>
<p>AI Assistant gets a runbook the team has added to its knowledge base. I can now analyze and try to resolve or reduce the issue with nginx.</p>
<p>While this is a simple example, there are an endless number of possibilities that can be defined based on KQL. Some other simple examples:</p>
<ul>
<li><p>99% of requests occur under 200ms</p></li>
<li><p>99% of log message are not errors</p></li>
</ul>
<h2 id="applicationslosopentelemetrydemocartservice">Application SLOs: OpenTelemetry demo cartservice</h2>
<p>A common application developers and SREs use to learn about OpenTelemetry and test out Observability features is the <a href="https://github.com/elastic/opentelemetry-demo">OpenTelemetry demo</a>.</p>
<p>This demo has <a href="https://opentelemetry.io/docs/demo/feature-flags/">feature flags</a> to simulate issues. With Elastic’s alerting and SLO capability, you can also determine how well the entire application is performing and how well your customer experience is holding up when these feature flags are used.</p>
<p><a href="https://www.elastic.co/blog/opentelemetry-observability">Elastic supports OpenTelemetry by taking OTLP directly with no need for an Elastic specific agent</a>. You can send in OpenTelemetry data directly from the application (through OTel libraries) and through the collector.</p>
<p>We’ve brought up the OpenTelemetry demo on a K8S cluster (AWS EKS) and turned on the cartservice feature flag. This inserts errors into the cartservice. We’ve also created two SLOs to monitor the cartservice’s availability and latency.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltfbeda104042da07a/6a7f1a87ead8ec59b3baac54/11-slo-blog.png" alt="SLOs" /></p>
<p>We can see that the cartservice’s availability is violated. As we drill down, we see that there aren’t as many successful transactions, which is affecting the SLO.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltbd22cf26b8ff2180/6a7f1a8a2f00b25cbbefef23/12-slo-blog.png" alt="cartservice-otel" /></p>
<p>As we drill into the service, we can see in Elastic APM that there is a higher than normal failure rate of about 5.5% for the emptyCart service.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt8c99cfb53987e5c7/6a7f1a8deab5bee9cd20ab00/13-slo-blog.png" alt="apm" /></p>
<p>We can investigate this further in APM, but that is a discussion for another blog. Stay tuned to see how we can use Elastic’s machine learning, AIOps, and AI Assistant to understand the issue.</p>
<h2 id="conclusion">Conclusion</h2>
<p>SLOs allow you to set clear, measurable targets for your service performance, based on factors like availability, response times, error rates, and other key metrics. Hopefully with the overview we’ve provided in this blog, you can see that:</p>
<ul>
<li><p>SLOs can be based on logs. In Elastic, you can use KQL to essentially find and filter on specific logs and log fields to monitor and trigger SLOs.</p></li>
<li><p>AI Assistant is a valuable, easy-to-use capability to analyze, troubleshoot, and even potentially resolve SLO issues.</p></li>
<li><p>APM Service based SLOs are easy to create and manage with integration to Elastic APM. We also use OTel telemetry to help monitor SLOs.</p></li>
</ul>
<p>For more information on SLOs in Elastic, check out <a href="https://www.elastic.co/guide/en/observability/current/slo.html">Elastic documentation</a> and the following resources:</p>
<ul>
<li><p><a href="https://www.elastic.co/guide/en/observability/8.12/slo.html">What’s new in Elastic Observability 8.12</a></p></li>
<li><p><a href="https://www.elastic.co/blog/context-aware-insights-elastic-ai-assistant-observability">Introducing the Elastic AI Assistant</a></p></li>
<li><p><a href="https://www.elastic.co/blog/opentelemetry-observability">Elastic OpenTelemetry support</a></p></li>
</ul>
<p>Ready to get started? Sign up for <a href="https://cloud.elastic.co/registration">Elastic Cloud</a> and try out the features and capabilities I’ve outlined above to get the most value and visibility out of your SLOs.</p>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
<p><em>In this blog post, we may have used or referred to third party generative AI tools, which are owned and operated by their respective owners. Elastic does not have any control over the third party tools and we have no responsibility or liability for their content, operation or use, nor for any loss or damage that may arise from your use of such tools. Please exercise caution when using AI tools with personal, sensitive or confidential information. Any data you submit may be used for AI training or other purposes. There is no guarantee that information you provide will be kept secure or confidential. You should familiarize yourself with the privacy practices and terms of use of any generative AI tools prior to use.</em></p>
<p><em>Elastic, Elasticsearch, ESRE, Elasticsearch Relevance Engine and associated marks are trademarks, logos or registered trademarks of Elasticsearch N.V. in the United States and other countries. All other company and product names are trademarks, logos or registered trademarks of their respective owners.</em></p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/service-level-objectives-slos-logs-metrics</link>
    <guid isPermaLink="false">service-level-objectives-slos-logs-metrics</guid>
    <category><![CDATA[Incident Management]]></category>
    <category><![CDATA[OpenTelemetry]]></category>
    <category><![CDATA[Logs Analytics]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Kubernetes]]></category>
    <dc:creator><![CDATA[Bahubali Shetti]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt126c07eb43762792/6a7f1a91b4377020074d7104/139686_-_Elastic_-_Headers_-_V1_3.jpg" length="0" type="image/jpeg"/>
    <pubDate>Fri, 23 Feb 2024 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[How to enable Kubernetes alerting with Elastic Observability]]></title>
    <description><![CDATA[In the Kubernetes world, different personas demand different kinds of insights. In this post, we’ll focus on alerting and provide an overview of how alerts in Elastic Observability can help users quickly identify Kubernetes problems.]]></description>
    <content:encoded><![CDATA[<p>In the Kubernetes world, different personas demand different kinds of insights. Developers are interested in granular metrics and debugging information. <a href="https://www.elastic.co/blog/elastic-observability-sre-incident-response">SREs</a> are interested in seeing everything at once to quickly get notified when a problem occurs and spot where the root cause is. In this post, we’ll focus on alerting and provide an overview of how alerts in Elastic Observability can help users quickly identify Kubernetes problems.</p>
<h2 id="whydoweneedalerts">Why do we need alerts?</h2>
<p>Logs, metrics, and traces are just the base to build a complete <a href="https://www.elastic.co/blog/kubernetes-cluster-metrics-logs-monitoring">monitoring solution for Kubernetes clusters</a>. Their main goal is to provide debugging information and historical evidence for the infrastructure.</p>
<p>While out-of-the-box dashboards, infrastructure topology, and logs exploration through Kibana are already quite handy to perform ad-hoc analyses, adding notifications and active monitoring of infrastructure allows users to deal with problems detected as early as possible and even proactively take actions to prevent their Kubernetes environments from facing even more serious issues.</p>
<h3 id="howcanthisbeachieved">How can this be achieved?</h3>
<p>By building alerts on top of their infrastructure, users can leverage the data and effectively correlate it to a specific notification, creating a wide range of possibilities to dynamically monitor and observe their Kubernetes cluster.</p>
<p>In this blog post, we will explore how users can leverage Elasticsearch’s search powers to define alerting rules in order to be notified when a specific condition occurs.</p>
<h2 id="slisalertsandsloswhyaretheyimportantforsres">SLIs, alerts, and SLOs: Why are they important for SREs?</h2>
<p>For site reliability engineers (SREs), the <a href="https://www.elastic.co/blog/elastic-observability-sre-incident-response">incident response time</a> is tightly coupled with the success of everyday work. Monitoring, alerting, and actions will help to discover, resolve, or prevent issues in their systems.</p>
<blockquote>
  <ul>
  <li><em>An SLA (Service Level Agreement) is an agreement you create with your users to specify the level of service they can expect.</em></li>
  <li><em>An SLO (Service Level Objective) is an agreement within an SLA about a specific metric like uptime or response time.</em></li>
  <li><em>An SLI (Service Level Indicator) measures compliance with an SLO.</em></li>
  </ul>
</blockquote>
<p>SREs’ day-to-day tasks and projects are driven by SLOs. By ensuring that SLOs are defended in the short term and that they can be maintained in the medium to long term, we lay the basis of a stable working infrastructure.</p>
<p>Having said this, identifying the high-level categories of SLOs is crucial in order to organize the work of an SRE. Then in each category of SLOs, SREs will need the corresponding SLIs that can cover the most important cases of their system under observation. Therefore, the decision of which SLIs we will need demands additional knowledge of the underlying system infrastructure.</p>
<p>One widely used approach to categorize SLIs and SLOs is the <a href="https://landing.google.com/sre/sre-book/chapters/monitoring-distributed-systems/#xref_monitoring_golden-signals">Four Golden Signals</a> method. The categories defined are Latency, Traffic, Errors, and Saturation.</p>
<p>A more specific approach is the <a href="https://thenewstack.io/monitoring-microservices-red-method/">The RED method</a> developed by Tom Wilkie, who was an SRE at Google and used the Four Golden Signals. The RED method drops the saturation category because this one is mainly used for more advanced cases — and people remember better things that come in threes.</p>
<p>Focusing on Kubernetes infrastructure operators, we will consider the following groups of infrastructure SLIs/SLOs:</p>
<ul>
<li>Group 1: Latency of control plane (apiserver,</li>
<li>Group 2: Resource utilization of the nodes/pods (how much cpu, memory, etc. is consumed)</li>
<li>Group 3: Errors (errors on logs or events or error count from components, network, etc.)</li>
</ul>
<h2 id="creatingalertsforakubernetescluster">Creating alerts for a Kubernetes cluster</h2>
<p>Now that we have a complete outline of our goal to define alerts based on SLIs/SLOs, we will dive into defining the proper alerting. Alerts can be built using <a href="https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html">Kibana</a>.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt57faef9de27c8bb1/6a85cefc9829269340583960/blog-elastic-create-rule.png" alt="kubernetes create rule" /></p>
<p>See Elastic <a href="https://www.elastic.co/guide/en/kibana/current/alerting-getting-started.html">documentation</a>.</p>
<p>In this blog, we will define more complex alerts based on complex Elasticsearch queries provided by <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-getting-started.html">Watcher</a>’s functionality. <a href="https://www.elastic.co/guide/en/kibana/8.8/watcher-ui.html">Read more about Watcher</a> and how to properly use it in addition to the examples in this blog.</p>
<h3 id="latencyalerts">Latency alerts</h3>
<p>For this kind of alert, we want to define the basic SLOs for a Kubernetes control plane, which will ensure that the basic control plane components can service the end users without an issue. For instance, facing high latencies in queries against the Kubernetes API Server is enough of a signal that action needs to be taken.</p>
<h3 id="resourcesaturation">Resource saturation</h3>
<p>The next group of alerting will be resource utilization. Node’s CPU utilization or changes in Node’s condition is something critical for a cluster to ensure the smooth servicing of the workloads provisioned to run the applications that end users will interact with.</p>
<h3 id="errordetection">Error detection</h3>
<p>Last but not least, we will define alerts based on specific errors like the network error rate or Pods’ failures like the OOMKilled situation. It’s a very useful indicator for SRE teams to either detect issues on the infrastructure level or just be able to notify developer teams about problematic workloads. One example that we will examine later is having an application running as a Pod and constantly getting restarted because it hits its memory limit. In that case, the owners of this application will need to get notified to act properly.</p>
<h2 id="fromkubernetesdatatoelasticsearchqueries">From Kubernetes data to Elasticsearch queries</h2>
<p>Having a solid plan about the alerts that we want to implement, it's time to explore the data we have collected from the Kubernetes cluster and stored in Elasticsearch. For this we will consult the list of the available data fields that are ingested using the Elastic Agent Kubernetes <a href="https://docs.elastic.co/en/integrations/kubernetes">integration</a> (the full list of fields can be found <a href="https://www.elastic.co/guide/en/beats/metricbeat/current/exported-fields-kubernetes.html">here</a>). Using these fields we can create various alerts like:</p>
<ul>
<li>Node CPU utilization</li>
<li>Node Memory utilization</li>
<li>BW utilization</li>
<li>Pod restarts</li>
<li>Pod CPU/memory utilization</li>
</ul>
<h3 id="cpuutilizationalert">CPU utilization alert</h3>
<p>Our first example will use the CPU utilization fields to calculate the Node’s CPU utilization and create an alert. For this alert, we leverage the metrics:</p>
<pre><code>kubernetes.node.cpu.usage.nanocores
kubernetes.node.cpu.capacity.cores.
</code></pre>
<p>The following calculation (nodeUsage / 1000000000 ) /nodeCap grouped by node name will give us the CPU utilization of our cluster’s nodes.</p>
<p>The Watcher definition that implements this query can be created with the following API call to Elasticsearch:</p>
<pre><code>curl -X PUT "https://elastic:changeme@localhost:9200/_watcher/watch/Node-CPU-Usage?pretty" -k -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": {
      "interval": "10m"
    }
  },
  "input": {
    "search": {
      "request": {
        "body": {
          "size": 0,
          "query": {
            "bool": {
              "must": [
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-10m",
                      "lte": "now",
                      "format": "strict_date_optional_time"
                    }
                  }
                },
                {
                  "bool": {
                    "must": [
                      {
                        "query_string": {
                          "query": "data_stream.dataset: kubernetes.node OR data_stream.dataset: kubernetes.state_node",
                          "analyze_wildcard": true
                        }
                      }
                    ],
                    "filter": [],
                    "should": [],
                    "must_not": []
                  }
                }
              ],
              "filter": [],
              "should": [],
              "must_not": []
            }
          },
          "aggs": {
            "nodes": {
              "terms": {
                "field": "kubernetes.node.name",
                "size": "10000",
                "order": {
                  "_key": "asc"
                }
              },
              "aggs": {
                "nodeUsage": {
                  "max": {
                    "field": "kubernetes.node.cpu.usage.nanocores"
                  }
                },
                "nodeCap": {
                  "max": {
                    "field": "kubernetes.node.cpu.capacity.cores"
                  }
                },
                "nodeCPUUsagePCT": {
                  "bucket_script": {
                    "buckets_path": {
                      "nodeUsage": "nodeUsage",
                      "nodeCap": "nodeCap"
                    },
                    "script": {
                      "source": "( params.nodeUsage / 1000000000 ) / params.nodeCap",
                      "lang": "painless",
                      "params": {
                        "_interval": 10000
                      }
                    },
                    "gap_policy": "skip"
                  }
                }
              }
            }
          }
        },
        "indices": [
          "metrics-kubernetes*"
        ]
      }
    }
  },
  "condition": {
    "array_compare": {
      "ctx.payload.aggregations.nodes.buckets": {
        "path": "nodeCPUUsagePCT.value",
        "gte": {
          "value": 80
        }
      }
    }
  },
  "actions": {
    "log_hits": {
      "foreach": "ctx.payload.aggregations.nodes.buckets",
      "max_iterations": 500,
      "logging": {
        "text": "Kubernetes node found with high CPU usage: {{ctx.payload.key}} -&gt; {{ctx.payload.nodeCPUUsagePCT.value}}"
      }
    }
  },
  "metadata": {
    "xpack": {
      "type": "json"
    },
    "name": "Node CPU Usage"
  }
}
</code></pre>
<h3 id="oomkilledpodsdetectionandalerting">OOMKilled Pods detection and alerting</h3>
<p>Another Watcher that we will explore is the one that detects Pods that have been restarted due to an OOMKilled error. This error is quite common in Kubernetes workloads and is useful to detect this early on to inform the team that owns this workload, so they can either investigate issues that could cause memory leaks or just consider increasing the required resources for the workload itself.</p>
<p>This information can be retrieved from a query like the following:</p>
<pre><code>kubernetes.container.status.last_terminated_reason: OOMKilled
</code></pre>
<p>Here is how we can create the respective Watcher with an API call:</p>
<pre><code>curl -X PUT "https://elastic:changeme@localhost:9200/_watcher/watch/Pod-Terminated-OOMKilled?pretty" -k -H 'Content-Type: application/json' -d'
{
  "trigger": {
    "schedule": {
      "interval": "1m"
    }
  },
  "input": {
    "search": {
      "request": {
        "search_type": "query_then_fetch",
        "indices": [
          "*"
        ],
        "rest_total_hits_as_int": true,
        "body": {
          "size": 0,
          "query": {
            "bool": {
              "must": [
                {
                  "range": {
                    "@timestamp": {
                      "gte": "now-1m",
                      "lte": "now",
                      "format": "strict_date_optional_time"
                    }
                  }
                },
                {
                  "bool": {
                    "must": [
                      {
                        "query_string": {
                          "query": "data_stream.dataset: kubernetes.state_container",
                          "analyze_wildcard": true
                        }
                      },
                      {
                        "exists": {
                          "field": "kubernetes.container.status.last_terminated_reason"
                        }
                      },
                      {
                        "query_string": {
                          "query": "kubernetes.container.status.last_terminated_reason: OOMKilled",
                          "analyze_wildcard": true
                        }
                      }
                    ],
                    "filter": [],
                    "should": [],
                    "must_not": []
                  }
                }
              ],
              "filter": [],
              "should": [],
              "must_not": []
            }
          },
          "aggs": {
            "pods": {
              "terms": {
                "field": "kubernetes.pod.name",
                "order": {
                  "_key": "asc"
                }
              }
            }
          }
        }
      }
    }
  },
  "condition": {
    "array_compare": {
      "ctx.payload.aggregations.pods.buckets": {
        "path": "doc_count",
        "gte": {
          "value": 1,
          "quantifier": "some"
        }
      }
    }
  },
  "actions": {
    "ping_slack": {
      "foreach": "ctx.payload.aggregations.pods.buckets",
      "max_iterations": 500,
      "webhook": {
        "method": "POST",
        "url": "https://hooks.slack.com/services/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",
        "body": "{\"channel\": \"#k8s-alerts\", \"username\": \"k8s-cluster-alerting\", \"text\": \"Pod {{ctx.payload.key}} was terminated with status OOMKilled.\"}"
      }
    }
  },
  "metadata": {
    "xpack": {
      "type": "json"
    },
    "name": "Pod Terminated OOMKilled"
  }
}
</code></pre>
<h3 id="fromkubernetesdatatoalertssummary">From Kubernetes data to alerts summary</h3>
<p>So far we saw how we can start from plain Kubernetes fields, use them in ES queries, and build Watchers and alerts on top of them.</p>
<p>One can explore more possible data combinations and build queries and alerts following the examples we provided here. A <a href="https://github.com/elastic/integrations/tree/main/packages/kubernetes/docs">full list of alerts</a> is available, as well as a <a href="https://github.com/elastic/k8s-integration-infra/tree/main/scripts/alerting">basic scripted way of installing them</a>.</p>
<p>Of course, these examples come with simple actions defined that only log messages into the Elasticsearch logs. However, one can use more advanced and useful outputs like Slack’s webhooks:</p>
<pre><code>"actions": {
    "ping_slack": {
      "foreach": "ctx.payload.aggregations.pods.buckets",
      "max_iterations": 500,
      "webhook": {
        "method": "POST",
        "url": "https://hooks.slack.com/services/T04SW3JHXasdfasdfasdfasdfasdf",
        "body": "{\"channel\": \"#k8s-alerts\", \"username\": \"k8s-cluster-alerting\", \"text\": \"Pod {{ctx.payload.key}} was terminated with status OOMKilled.\"}"
      }
    }
  }
</code></pre>
<p>The result would be a Slack message like the following:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blta7d49603f39ee1d7/6a85ceffbc5bb3503af81b75/blog-elastic-k8s-cluster-alerting.png" alt="" /></p>
<h2 id="nextsteps">Next steps</h2>
<p>In our next steps, we would like to make these alerts part of our Kubernetes integration, which would mean that the predefined alerts would be installed when users install or enable the Kubernetes integration. At the same time, we plan to implement some of these as Kibana’s native SLIs, providing the option to our users to quickly define SLOs on top of the SLIs through a nice user interface. If you’re interested to learn more about these, follow the public GitHub issues for more information and feel free to provide your feedback:</p>
<ul>
<li><a href="https://github.com/elastic/package-spec/issues/484">https://github.com/elastic/package-spec/issues/484</a></li>
<li><a href="https://github.com/elastic/kibana/issues/150050">https://github.com/elastic/kibana/issues/150050</a></li>
</ul>
<p>For those who are eager to start using Kubernetes alerting today, here is what you need to do:</p>
<ol>
<li>Make sure that you have an Elastic cluster up and running. The fastest way to deploy your cluster is to spin up a <a href="https://www.elastic.co/elasticsearch/service">free trial of Elasticsearch Service</a>.</li>
<li>Install the latest Elastic Agent on your Kubernetes cluster following the respective <a href="https://www.elastic.co/guide/en/fleet/master/running-on-kubernetes-managed-by-fleet.html">documentation</a>.</li>
<li>Install our provided alerts that can be found at <a href="https://github.com/elastic/integrations/tree/main/packages/kubernetes/docs">https://github.com/elastic/integrations/tree/main/packages/kubernetes/docs</a> or at <a href="https://github.com/elastic/k8s-integration-infra/tree/main/scripts/alerting">https://github.com/elastic/k8s-integration-infra/tree/main/scripts/alerting</a>.</li>
</ol>
<p>Of course, if you have any questions, remember that we are always happy to help on the Discuss <a href="https://discuss.elastic.co/">forums</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/enable-kubernetes-alerting-observability</link>
    <guid isPermaLink="false">enable-kubernetes-alerting-observability</guid>
    <category><![CDATA[Kubernetes]]></category>
    <category><![CDATA[Metrics]]></category>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Christos Markou]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt691f1d7ad04639d8/6a85cf02501a854cfbfbb3a7/alert-management.jpg" length="0" type="image/jpeg"/>
    <pubDate>Tue, 30 May 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Elastic Synthetics Projects: A Git-friendly way to manage your synthetics monitors in Elastic Observability]]></title>
    <description><![CDATA[Elastic Observability can easily integrate into your DevOps git flow when managing applications with synthetics. Our new Synthetics Projects will enable you to develop and manage synthetics monitor configurations written in YAML with git.]]></description>
    <content:encoded><![CDATA[<p>Elastic has an entirely new Heartbeat/Synthetics workflow superior to the current workflow. If you’re a current user of the Elastic Uptime app, read on to learn about the improved workflow you can use today and should eventually migrate toward.</p>
<p>We’ve recently released a beta feature that provides a Git-friendly IaaC oriented workflow. You can now push Heartbeat monitors with the same ease with which you push code changes in Git or config changes in Terraform. The features discussed in this blog are all currently in beta, and we urge users trying these features out to upgrade to the latest stack version first. When these features become GA, this new workflow will be the preferred way of configuring monitors in the Elastic Stack. If you’re starting a new project, you may want to consider setting it up this way instead of via our more classic configuration.</p>
<p>Today, using Heartbeat is simple. You just need to write a little YAML and monitoring data shows up in Elasticsearch, visible in the Uptime UI. While the UI is indeed simple, there’s some hidden complexity there that we’ve improved with a new UI (the Synthetics app) and augmented with an even more automation friendly CLI workflow via our new Projects feature, which will be discussed below.</p>
<p>How do you manage your configs written in YAML? Many of our users will manage YAML in Git and use tooling such as Ansible, Helm, or similar to manage their infrastructure as code (IaaC). As with any other organization, Elastic also heavily utilizes IaaC in all parts of our operations. Hence it’s only natural we developed a capability to provide you with similar support for the current Heartbeat capability and the upcoming synthetics monitoring capabilities.</p>
<h2 id="projectsanewwaytoorganizeanddistributeconfigs">Projects: A new way to organize and distribute configs</h2>
<p>Let’s dive right into what we’re calling “Synthetics Projects” and how they differ from traditional Heartbeat config files. To use this feature, you would start by <a href="https://www.elastic.co/guide/en/observability/current/synthetics-get-started-project.html">creating a project</a> in a Git repo containing your configs. At a high level, setting up a project requires performing the following tasks:</p>
<ol>
<li>Run npx @elastic/synthetics init to create a project skeleton in a directory. See more details on the <a href="https://www.npmjs.com/package/@elastic/synthetics">npmjs.com</a> site.</li>
<li>Run git init and git push on the generated directory to version it as a Git repository.</li>
<li>Add your lightweight YAML files and browser javascript/typescript files to the journeys folder.</li>
<li>Test that it works by running npx @elastic/synthetics push command to sync your project to your Elastic Stack.</li>
<li>Configure a CI/CD pipeline to test pull requests to your Git repo and to execute npx @elastic/synthetics push on merges to the main branch.</li>
</ol>
<p>So, once configured, adding, removing, and editing monitors involves:</p>
<ol>
<li>Editing a monitor’s config, either YAML for lightweight monitors, or Javascript/Typescript for browser based ones locally</li>
<li>Testing your local configs with npx @elastic/synthetics journeys</li>
<li>Creating a new PR to your main branch via a Git push</li>
<li>Waiting for your CI server to perform the same validation and waiting for someone else on your team to review your PR</li>
<li>Merging your result to the main branch</li>
<li>Waiting for your CI server to push the changes to your Elastic stack</li>
</ol>
<p>We’ve depicted the flow of data in the diagram below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt049e954161c4e232/6a85ceedd7b2e779a5fe856e/blog-elastic-flow-of-data-diagram.png" alt="" /></p>
<p>This is, in fact, the way many of our users work today, with other software taking the place of npx @elastic/synthetics push as mentioned earlier. Indeed, in the future, we will most likely look into building a Terraform provider, though that isn’t something we’re actively working on now.</p>
<h2 id="justhaveafewmonitorsusethegui">Just have a few monitors? Use the GUI!</h2>
<p>The above approach is great for sophisticated users with larger numbers of configurations, but if you just want to monitor a few URLs, it’s overkill. If that sounds like you, consider the new Monitor Management UI in the Uptime app! It works in the exact same way, saving configs to your Elastic Stack, but with no need for Git, or a project, or all that other infrastructure. Simply, log in, fill out the form pictured below, and hit save. If you want to set up a private location, that is still done in the same way via Fleet.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt88369459205aba16/6a85cef01aa1e10039ff8ddf/blog-elastic-add-monitor.png" alt="" /></p>
<h2 id="whataboutmyexistingfleetmonitors">What about my existing Fleet monitors?</h2>
<p>A small subset of users have monitors configured today using the Synthetics Fleet integration. If that describes you, you’ll want to move onto either the GUI based approach or the Project based approach, as those methods supersede direct usage of the Fleet integration, which will eventually be restricted only to use via the above described methods.</p>
<p>The Fleet approach is inferior in a few ways:</p>
<ol>
<li>It can only configure monitors for a single location.</li>
<li>It creates a different UX for monitors configured on the service versus private locations.</li>
<li>It’s less fluid of an integration with the Uptime UI.</li>
</ol>
<p>It’s rare for us to deprecate beta features, but in this case we had a clearly superior alternative. Maintaining both would have created a more confusing and unwieldy product. We don’t yet have an exact date for removing support for these monitors, but you can track this via <a href="https://github.com/elastic/kibana/issues/137508">this GitHub issue</a>.</p>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/synthetics-git-ops-observability</link>
    <guid isPermaLink="false">synthetics-git-ops-observability</guid>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Andrew Cholakian]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt867b22bc0c306ec3/6a85cef3f9373d74f596f61e/blog-charts-packages.png" length="0" type="image/png"/>
    <pubDate>Thu, 23 Feb 2023 00:00:00 GMT</pubDate>
  </item>
  <item>
    <title><![CDATA[Two sides of the same coin: Uniting testing and monitoring with Synthetic Monitoring]]></title>
    <description><![CDATA[DevOps aims to establish complementary practices across development and operations. See how Playwright, @elastic/synthetics, GitHub Actions, and Elastic Synthetics can unite development and SRE teams in validating and monitoring the user experience.]]></description>
    <content:encoded><![CDATA[<p>Historically, software development and SRE have worked in silos with different cultural perspectives and priorities. The goal of DevOps is to establish common and complementary practices across software development and operations. However, for some organizations true collaboration is rare, and we still have a way to go to build effective DevOps partnerships.</p>
<p>Outside of cultural challenges, one of the most common reasons for this disconnect is using different tools to achieve similar goals — case in point, end-to-end (e2e) testing versus <a href="https://www.elastic.co/observability/synthetic-monitoring">synthetic monitoring</a>.</p>
<p>This blog shares an overview of these techniques. Using the example repository <a href="https://github.com/carlyrichmond/synthetics-replicator">carlyrichmond/synthetics-replicator</a>, we’ll also show how Playwright, @elastic/synthetics, and GitHub Actions can combine forces with Elastic Synthetics and the recorder to unite development and SRE teams in validating and monitoring the user experience for a simple web application hosted on a provider such as <a href="https://www.netlify.com/">Netlify</a>.</p>
<p>Elastic recently <a href="https://www.elastic.co/blog/new-synthetic-monitoring-observability">introduced synthetics monitoring</a>, and <a href="https://www.elastic.co/blog/why-and-how-replace-end-to-end-tests-synthetic-monitors">as highlighted in our prior blog</a>, it can replace e2e tests altogether. Uniting around a single tool to validate the user workflow early provides a common language to recreate user issues to validate fixes against.</p>
<h2 id="syntheticsmonitoringversuse2etests">Synthetics Monitoring versus e2e tests</h2>
<p>If development and operations tools are at war, it’s difficult to unify their different cultures together. Considering the definitions of these approaches shows that they in fact aim to achieve the same objective.</p>
<p>e2e tests are a suite of tests that recreate the user path, including clicks, user text entry, and navigations. Although many argue it’s about testing the integration of the layers of a software application, it’s the user workflow that e2e tests emulate. Meanwhile, Synthetic Monitoring, specifically a subset known as browser monitoring, is an application performance monitoring practice that emulates the user path through an application.</p>
<p>Both these techniques emulate the user path. If we use tooling that crosses the developer and operational divide, we can work together to build tests that can also provide production monitoring in our web applications.</p>
<h2 id="creatinguserjourneys">Creating user journeys</h2>
<p>When a new user workflow, or set of features that accomplish a key goal, is under development in our application, developers can use @elastic/synthetics to create user journeys. The initial project scaffolding can be generated using the init utility once installed, as in the below example. Note that Node.js must be installed prior to using this utility.</p>
<pre><code>npm install -g @elastic/synthetics
npx @elastic/synthetics init synthetics-replicator-tests
</code></pre>
<p>Before commencing the wizard, make sure you have your Elastic cluster information and the Elastic Synthetics integration set on your cluster. You will need:</p>
<ol>
<li>Monitor Management must be enabled within the Elastic Synthetics app as per the prerequisites in the <a href="https://www.elastic.co/guide/en/observability/8.8/synthetics-get-started-project.html#_prerequisites">documentation getting started</a>.</li>
<li>The Elastic Cloud cluster Cloud ID if using Elastic Cloud. Alternatively, if you are using on-prem hosting you need to enter your Kibana endpoint.</li>
<li>An API key generated from your cluster. There is a shortcut in the Synthetics application Settings to generate this key under the Project API Keys tab, as shown <a href="https://www.elastic.co/guide/en/observability/current/synthetics-get-started-project.html#synthetics-get-started-project-init">in the documentation</a>.</li>
</ol>
<p>This wizard will take you through and generate a sample project containing configuration and example monitor journeys, with a structure similar to the below:</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt13eec887c0713011/6a85ce5ebc5bb3309af81b59/blog-elastic-synthetics-replicator-tests.png" alt="synthetics replicator tests" /></p>
<p>For web developers, most of the elements such as the README and package.json and lock files will be familiar. The main configuration for your monitors is available in synthetics.config.tsas shown below. This configuration can be amended to include production and development-specific configuration. This is essential for combining forces and reusing the same monitors for e2e tests and allowing for any journeys to be used as e2e tests and production monitors. Although not in this example, details of <a href="https://www.elastic.co/guide/en/observability/current/synthetics-private-location.html">private locations</a> can be included if you would prefer to monitor from your own dedicated Elastic instance rather than from Elastic infrastructure.</p>
<pre><code>import type { SyntheticsConfig } from "@elastic/synthetics";

export default (env) =&gt; {
  const config: SyntheticsConfig = {
    params: {
      url: "http://localhost:5173",
    },
    playwrightOptions: {
      ignoreHTTPSErrors: false,
    },
    /**
     * Configure global monitor settings
     */
    monitor: {
      schedule: 10,
      locations: ["united_kingdom"],
      privateLocations: [],
    },
    /**
     * Project monitors settings
     */
    project: {
      id: "synthetics-replicator-tests",
      url: "https://elastic-deployment:port",
      space: "default",
    },
  };
  if (env === "production") {
    config.params = { url: "https://synthetics-replicator.netlify.app/" };
  }
  return config;
};
</code></pre>
<h2 id="writingyourfirstjourney">Writing your first journey</h2>
<p>Although the above configuration applies to all monitors in the project, it can be overridden for a given test.</p>
<pre><code>import { journey, step, monitor, expect, before } from "@elastic/synthetics";

journey("Replicator Order Journey", ({ page, params }) =&gt; {
  // Only relevant for the push command to create
  // monitors in Kibana
  monitor.use({
    id: "synthetics-replicator-monitor",
    schedule: 10,
  });

  // journey steps go here
});
</code></pre>
<p>The @elastic/synthetics wrapper exposes many <a href="https://www.elastic.co/guide/en/observability/current/synthetics-create-test.html#synthetics-syntax">standard test methods</a> such as the before and after constructs that allow for setup and tear down of typical properties in the tests, as well as support for many common assertion helper methods. A full list of supported expect methods are listed in the <a href="https://www.elastic.co/guide/en/observability/current/synthetics-create-test.html#synthetics-assertions-methods">documentation</a>. The Playwright page object is also exposed, which enables us to perform <a href="https://playwright.dev/docs/api/class-page">all the expected activities provided in the API</a> such as locating page elements and simulating user events such as clicks that are depicted in the below example.</p>
<pre><code>import { journey, step, monitor, expect, before } from "@elastic/synthetics";

journey("Replicator Order Journey", ({ page, params }) =&gt; {
  // monitor configuration goes here

  before(async () =&gt; {
    await page.goto(params.url);
  });

  step("assert home page loads", async () =&gt; {
    const header = await page.locator("h1");
    expect(await header.textContent()).toBe("Replicatr");
  });

  step("assert move to order page", async () =&gt; {
    const orderButton = await page.locator("data-testid=order-button");
    await orderButton.click();

    const url = page.url();
    expect(url).toContain("/order");

    const menuTiles = await page.locator("data-testid=menu-item-card");
    expect(await menuTiles.count()).toBeGreaterThan(2);
  });

  // other steps go here
});
</code></pre>
<p>As you can see in the above example, it also exposes the journey and step constructs. This construct mirrors the behavior-driven development (BDD) practice of showing the user journey through the application in tests.</p>
<p>Developers are able to execute the tests against a locally running application as part of their feature development to see successful and failed steps in the user workflow. In the below example, the local server startup command is outlined in blue at the top. The monitor execution command is presented in red further down.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9f02fa233d6d84d3/6a85ce619d2b71ae26f93a1c/blog-elastic-synthetics-replicator-npm-start.png" alt="" /></p>
<p>As you can see from the green ticks next to each journey step, each of our tests pass. Woo!</p>
<h2 id="gatingyourcipipelines">Gating your CI pipelines</h2>
<p>It’s important to use the execution of the monitors within your CI pipeline as a gate for merging code changes and uploading the new version of your monitors. Each of the jobs in our <a href="https://github.com/carlyrichmond/synthetics-replicator/blob/main/.github/workflows/push-build-test-synthetics-replicator.yml">GitHub Actions workflow</a> will be discussed in this and the subsequent section.</p>
<p>The test job spins up a test instance and runs our user journeys to validate our changes, as illustrated below. This step should run for pull requests to validate developer changes, as well as on push.</p>
<pre><code>jobs:
  test:
    env:
      NODE_ENV: development
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm install
      - run: npm start &amp;
      - run: "npm install @elastic/synthetics &amp;&amp; SYNTHETICS_JUNIT_FILE='junit-synthetics.xml' npx @elastic/synthetics . --reporter=junit"
        working-directory: ./apps/synthetics-replicator-tests/journeys
      - name: Publish Unit Test Results
        uses: EnricoMi/publish-unit-test-result-action@v2
        if: always()
        with:
          junit_files: "**/junit-*.xml"
          check_name: Elastic Synthetics Tests
</code></pre>
<p>Note that, unlike the journey execution on our local machine, we make use of the --reporter=junit option when executing npx @elastic/synthetics to provide visibility of our passing, or sadly sometimes failing, journeys to the CI job.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt2706dcb2e6e735dd/6a85ce65d6cf295da3bb093f/blog-elastic-synthetics-tests.png" alt="" /></p>
<h2 id="automaticallyuploadmonitors">Automatically upload monitors</h2>
<p>To ensure the latest monitors are available in Elastic Uptime, it’s advisable to push the monitors programmatically as part of the CI workflow such as the example task below does. Our workflow has a second job push, shown below, which is dependent on the successful execution of our test job that uploads your monitors to your cluster. Note that this job is configured in our workflow to run on push to ensure changes have been validated rather than just raised within a pull request.</p>
<pre><code>jobs:
  test: …
  push:
    env:
      NODE_ENV: production
      SYNTHETICS_API_KEY: ${{ secrets.SYNTHETICS_API_KEY }}
    needs: test
    defaults:
      run:
        working-directory: ./apps/synthetics-replicator-tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm install
      - run: npm run push
</code></pre>
<p>The @elastic/synthetics init wizard generates a push command for you when you create your project that can be triggered from the project folder. This is shown below through the steps and working_directory configuration. The push command requires the API key from your Elastic cluster, which should be stored as a secret within a trusted vault and referenced via a workflow environment variable. It is also vital that monitors pass ahead of pushing the updated monitor configuration to your Elastic Synthetics instance to prevent breaking your production monitoring. Unlike e2e tests running against a testing environment, broken monitors impact SRE activities and therefore any changes need to be validated. For that reason, applying a dependency to your test step via the needs option is recommended.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt9d4f0a8f3de6c4f7/6a85ce674710c63bd7d3cbca/blog-elastic-push-build-test-synthetics-replicator.png" alt="" /></p>
<h2 id="monitoringusingelasticsynthetics">Monitoring using Elastic Synthetics</h2>
<p>Once monitors have been uploaded, they give a regular checkpoint to SRE teams as to whether the user workflow is functioning as intended — not just because they will run on a regular schedule as configured for the project and individual tests as shown previously, but also due to the ability to check the state of all monitor runs and execute them on demand.</p>
<p>The Monitors Overview tab gives us an immediate view of the status of all configured monitors, as well as the ability to run the monitor manually via the card ellipsis menu.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltd9acc9fb6caeeaa6/6a85ce6a11893c610ea7abf2/blog-elastic-monitors.png" alt="elastic observability monitors" /></p>
<p>From the Monitor screen, we can also navigate to an overview of an individual monitor execution to investigate failures.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blte0c90b7dd43807e7/6a85ce6d33f2444dcf49f575/blog-elastic-test-run-details.png" alt="test run details" /></p>
<p>The other monitoring superpower SREs now have is the integration between these monitors to familiar tools SREs already use in scrutinizing the performance and availability of applications such as APM, metrics, and logs. The aptly named <strong>Investigate</strong> menu allows easy navigation while SREs are performing investigations into potential failures or bottlenecks.</p>
<p>There is also a balance between finding issues and being notified of potential problems automatically. SREs already familiar with setting rules and thresholds for notification of issues will be happy to know that this is also possible for browser monitors. The editing of an example rule is shown below.</p>
<p><img src="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/bltb2b035e5591f4cd2/6a85ce70f5f1a073182ec979/blog-elastic-rules.png" alt="elastic observability rules" /></p>
<p>The status of browser monitors can be configured not only to consider if any individual or collective monitors have been down several times, such as in the status check above, but also to gauge the overall availability by looking at the percentage of passed checks within a given time period. SREs are not only interested in reacting to issues in a traditional production management way — they want to improve the availability of applications, too.</p>
<h2 id="recordinguserworkflows">Recording user workflows</h2>
<p>The limitation of generating e2e tests through the development lifecycle is that sometimes teams miss things, and the prior toolset is geared toward development teams. Despite the best intentions to design an intuitive product using multi-discipline teams, users may use applications in unintended ways. Furthermore, the monitors written by developers will only cover those expected workflows and raise the alarm either when these monitors fail in production or when they start to behave differently if anomaly detection is applied to them.</p>
<p>When user issues arise, it’s useful to recreate that problem in the same format as our monitors. It’s also important to leverage the experience of SREs in generating user journeys, as they will consider failure cases intuitively where developers may struggle and focus on happy cases. However, not all SREs will have the experience or confidence to write these journeys using Playwright and @elastic/synthetics.</p>
<p></p>
<p>Enter the Elastic Synthetics Recorder! The above video gives a walkthrough of how it can be used to record the steps in a user journey and export them to a JavaScript file for inclusion in your monitor project. This is useful for feeding back into the development phase and testing developed fixes to solve the problem. This approach cannot be made unless we all combine forces to use these monitors together.</p>
<h2 id="tryitout">Try it out!</h2>
<p>As of 8.8, @elastic/synthetics and the Elastic Synthetics app are generally available, and the trusty recorder is in beta. Share your experiences of bridging the developer and operations divide with Synthetic Monitoring via the <a href="https://discuss.elastic.co/c/observability/uptime/75">Uptime category</a> in the Community Discuss forums or via <a href="https://ela.st/slack">Slack</a>.</p>
<p>Happy monitoring!</p>
<p><em>Originally published February 6, 2023; updated May 23, 2023.</em></p>
<blockquote>
  <ol>
  <li><a href="https://www.elastic.co/blog/why-and-how-replace-end-to-end-tests-synthetic-monitors">Why and how to replace end-to-end tests with synthetic monitors</a></li>
  <li><a href="https://www.elastic.co/guide/en/observability/current/monitor-uptime-synthetics.html#monitor-uptime-synthetics">Uptime and Synthetic Monitoring</a></li>
  <li><a href="https://www.elastic.co/guide/en/observability/current/synthetics-journeys.html">Scripting browser monitors</a></li>
  <li><a href="https://www.elastic.co/guide/en/observability/current/synthetics-recorder.html">Use the Synthetics Recorder</a></li>
  <li><a href="https://playwright.dev/">Playwright</a></li>
  <li><a href="https://docs.github.com/en/actions">GitHub Actions</a></li>
  </ol>
</blockquote>]]></content:encoded>
    <link>https://www.elastic.co/observability-labs/blog/testing-monitoring-synthetic-monitoring</link>
    <guid isPermaLink="false">testing-monitoring-synthetic-monitoring</guid>
    <category><![CDATA[Incident Management]]></category>
    <dc:creator><![CDATA[Carly Richmond]]></dc:creator>
    <enclosure url="https://static-www.elastic.co/v3/assets/bltefdd0b53724fa2ce/blt04bb184f7c15eae4/6a85ce7380984c39bc669042/digital-experience-monitoring.jpg" length="0" type="image/jpeg"/>
    <pubDate>Mon, 06 Feb 2023 00:00:00 GMT</pubDate>
  </item>
  </channel>
</rss>