Diagnose rule behavior with the rule query inspector
The rule query inspector lets you view the Elasticsearch request that a rule sends when it evaluates your data. Use it to understand the query structure, confirm the rule is targeting the right data, and diagnose why an alert did or didn't fire.
Currently, the rule query inspector is only available for custom threshold rules.
The inspector is available from two places, each showing a different query:
- From the rule details page (current rule parameters)
- Open Stack Management > Rules, find your rule, and click its name to open the rule details page. Click Rule query inspector. The inspector builds the query from the rule's current parameters. Use this view to verify that the rule is configured correctly and would match the data you expect.
- From an alert details page (historical parameters)
- Go to the Alerts page, then open an individual alert. Click Rule query inspector. The inspector uses the rule parameters as they existed when that specific alert fired, including the exact evaluation time range. Use this view to understand why a particular alert was or wasn't triggered.
The key difference: the rule details page reflects the rule as it is now, while the alert details page reflects the rule as it was then. If you've edited the rule since an alert fired, the two inspectors will show different queries.
The following sections describe the query structure for custom threshold rules. As support for additional rule types is added, this reference will expand.
The inspector displays the full Elasticsearch request. Each part of the query maps to a setting in your rule configuration.
The top-level index and range filter reflect your rule's data source and time window:
{
"index": ["<your-data-view-index-pattern>"],
"body": {
"query": {
"bool": {
"filter": [
{
"range": {
"@timestamp": {
"gte": "...",
"lte": "..."
}
}
}
]
}
}
}
}
- The time field from your data view.
- The start of the evaluation window (
nowminus your rule's time window setting). - The end of the evaluation window. From an alert details page, this matches the exact moment the alert was evaluated, not the current time.
If the time range looks unexpected from an alert details page, this confirms the exact window Elasticsearch searched when the alert fired. This can help explain alerts that seem outdated or cover an unexpected period.
If you set a query filter on the rule, it appears as an additional clause in the bool filter:
{
"query": {
"bool": {
"filter": [
{ "range": { "@timestamp": { ... } } },
{ "query_string": { "query": "host.name: host-1" } }
]
}
}
}
- The KQL query filter you set on the rule, translated to a
query_stringortermclause. If this filter excludes more data than expected, the rule won't find the documents you intended.
If the filter is missing or different from what you set, double-check the rule configuration.
Each criterion you defined in the rule becomes an aggregation in the query. A rule with two criteria (for example, Aggregation A and Aggregation B) produces two sub-aggregations:
{
"aggs": {
"A": {
"avg": { "field": "system.cpu.user.pct" }
},
"B": {
"avg": { "field": "system.cpu.system.pct" }
}
}
}
- The letter label matches the criterion label shown in the rule configuration (A, B, and so on).
| Rule criterion | Aggregation in query |
|---|---|
| Average of a field | avg |
| Max of a field | max |
| Min of a field | min |
| Sum of a field | sum |
| Count (all docs) | value_count or filter + value_count |
| Cardinality of a field | cardinality |
| 95th percentile of a field | percentiles with { "percents": [95] } |
| Rate of a field | Two max aggregations plus a bucket script |
If you set a KQL filter on a criterion (
filter aggregation wrapping the metric aggregation.
If your rule uses Group alerts by, the aggregations are wrapped in a composite aggregation that partitions results by those fields:
{
"aggs": {
"groupBy": {
"composite": {
"sources": [
{ "host.name": { "terms": { "field": "host.name" } } }
],
"size": 10000
},
"aggs": {
"A": { "avg": { "field": "system.cpu.user.pct" } }
}
}
}
}
- One entry per Group alerts by field. Multiple group-by fields produce multiple
sources.
Without group-by, the aggregations run over all matched documents and return a single value.
The inspector also shows the Elasticsearch response alongside the request. Match each aggregation bucket back to your rule configuration to understand what value was computed.
When there are no group-by fields, the response contains a single set of aggregation values under aggregations:
{
"aggregations": {
"A": { "value": 0.82 },
"B": { "value": 0.15 }
}
}
- Aggregation
Areturned0.82. If your rule equation is(A + B) / C * 100with thresholdIS ABOVE 95, you'd compute the equation value with these numbers to confirm whether the threshold was met.
If the response value is below the threshold and no alert fired, this confirms the rule evaluated correctly. If you expected an alert and the value is below the threshold, review your aggregations and KQL filters.
When group-by fields are used, the response returns one bucket per group under aggregations.groupBy.buckets:
{
"aggregations": {
"groupBy": {
"buckets": [
{
"key": { "host.name": "host-1" },
"doc_count": 342,
"A": { "value": 0.97 }
},
{
"key": { "host.name": "host-2" },
"doc_count": 58,
"A": { "value": 0.42 }
}
]
}
}
}
host-1had a value of0.97. If the threshold isIS ABOVE 0.95, this group breached it and an alert should have fired forhost-1.host-2had a value of0.42— below the threshold, so no alert fired for this group.
If a group you expected to appear is missing from the buckets, it had no matching documents during the evaluation window. This can happen when doc_count is 0 or when the query filter excluded all documents for that group.
If Elasticsearch returned no documents, the aggregation values will be null or the buckets array will be empty:
{
"aggregations": {
"A": { "value": null }
}
}
A null value means no data matched the query during the evaluation window. If you have no data alerts configured, this is the state that triggers them. Check the time range and query filter to confirm no documents were genuinely present, or investigate whether an index or data view configuration issue is preventing data from being found.
Alert fired but I don't know why
Open the inspector from the alert details page. Review the time range to confirm it matches the evaluation period. Find the aggregation bucket for your group and check the value against the threshold. If the value exceeds the threshold, the alert fired correctly.
Alert didn't fire when I expected it to
Open the inspector from the rule details page and confirm the query targets the right index pattern and time range. Check the query filter for unintended restrictions. If the aggregation values in the response are below the threshold, the rule evaluated correctly but your data didn't breach the threshold during that window.
Rule looks correct now but the alert used different parameters
If you've modified the rule since the alert fired, open the inspector from the alert details page rather than the rule details page. The alert inspector uses the parameters that were active at the time the alert fired, so the query will reflect the older configuration.
Empty or null aggregation values
The query matched no documents. Check whether the index pattern in the data view is correct, whether your time range is appropriate, and whether any query filter is too restrictive. Also verify that the data stream or index has data in the expected time period by running the same query in Discover or Dev Tools.
Unexpected group missing from results
If a group you expected (such as a specific host) doesn't appear in the buckets, no documents for that group matched the query during the evaluation window. This can happen when the group was inactive, when a filter excluded its documents, or when the field used for grouping has a different value in the actual documents than you expected.