The Elasticsearch data source is one of the most popular plugins in the Grafana ecosystem, and it now ships ES|QL support as an experimental feature, available starting in Grafana 13.0. ES|QL is Elasticsearch's modern pipe-based query language that enables querying logs, metrics, and traces, and using Elasticsearch as a native Prometheus PromQL data source, all directly from the Grafana query editor. Built by Elastic in collaboration with Grafana Labs and contributed upstream to the Grafana open source project, this integration is enabled through a single feature flag (elasticsearchESQLQuery = true) that unlocks a Monaco-powered editor with syntax highlighting, autocompletion, and inline error messages. We'll walk through how to enable it and write your first queries for log analysis, time series visualization, and metrics aggregation.
Context
Elasticsearch is one of the top plugins used with the Grafana UI. Until now, the Elasticsearch plugin only supported Lucene and raw Query DSL for querying. ES|QL is Elastic's modern pipe-based query language, designed for analytics on log, metrics, and trace data. Its intuitive syntax makes it easier to filter, aggregate, and transform data compared to Query DSL or Lucene.
This has been tracked as a community request since 2024: grafana/grafana#81765.
How to enable ES|QL support in Grafana
The feature is behind the elasticsearchESQLQuery feature flag.
To turn it on, add the following to your grafana.ini:
[feature_toggles]
elasticsearchESQLQuery = true
Restart Grafana after saving. The feature is available starting with Grafana 13.0.
ES|QL in the Grafana query editor
Once the flag is enabled, the Elasticsearch query editor gains a Query language selector. You can switch between Lucene, Raw DSL, and ES|QL from the same editor panel.
When you select ES|QL, the editor switches to a Monaco-powered code editor, the same engine that powers VS Code. You get syntax highlighting, error highlighting, and basic autocompletion out of the box.
Smart index pre-population makes getting started quick: if an index pattern is configured in your data source settings, clicking into the ES|QL editor for the first time auto-inserts FROM <index>.
You can change or delete it freely.
If no index is configured, the FROM clause is left blank.
Running your first queries
Count log entries by severity
This is a good first query to confirm ES|QL is working and to get a feel for the syntax.
FROM logs*
| STATS count = COUNT(*) BY log.level
| SORT count DESC
Run it in the Raw Data panel type to see a table with counts per log level:
Browse the latest errors
WHERE, KEEP, SORT, and LIMIT make it easy to filter down to exactly the fields and rows you care about.
FROM logs*
| WHERE log.level == "ERROR"
| KEEP @timestamp, message, host.name
| SORT @timestamp DESC
| LIMIT 20
Log volume over time
Use BUCKET to group log counts into hourly intervals.
This works well with the Metrics panel type, which can render it as a time series graph.
FROM logs*
| STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 1 hour)
| SORT bucket ASC
Top hosts by log activity
Identifying the most active hosts is a common operations task.
STATS with BY and LIMIT makes it concise.
FROM logs*
| STATS log_count = COUNT(*) BY host.name
| SORT log_count DESC
| LIMIT 10
The TS command for time series metrics
For metrics data in Time Series Data Streams (TSDS), the TS source command in ES|QL enables metrics analytics and time series aggregation.
Examples:
RATE(): rate of change over timeAVG_OVER_TIME(): average value over a sliding windowINCREASE(): total increase over a periodDELTA(): difference between first and last valueLAST_OVER_TIME(): most recent value in a window
The pattern follows a two-level aggregation: an inner function applied per individual time series, then an outer function aggregating across groups (for example, per host or per service).
TS metrics*
| STATS SUM(RATE(metrics.system.cpu.time)) BY TBUCKET(10 m)
You can also use AVG_OVER_TIME() to compute the average value of a metric over a sliding window, then split the results by host and 10-minute buckets:
TS metrics*
| STATS MAX(AVG_OVER_TIME(metrics.system.memory.utilization)) BY host.name,TBUCKET(10m)
TS also runs queries through the ES|QL vectorized compute engine.
Internal benchmarks show performance improvements of an order of magnitude or more compared to equivalent Query DSL queries for TSDS-backed data.
Reference:
Inline error messages
ES|QL queries run against the /_query HTTP endpoint on Elasticsearch.
If your query has a syntax error or references a non-existent field, Elasticsearch returns a structured error response.
The plugin surfaces this directly in the query editor as an inline message, so you see exactly what went wrong right in the Grafana UI.
In the example above, host.nam is missing the final e.
Elasticsearch catches this as a verification exception and returns the field name that could not be resolved.
That message appears inline, right below the query editor.
Technical details
Under the hood, the plugin handles ES|QL and other query types on separate code paths.
ES|QL queries go to the /_query endpoint with Content-Type: application/json.
Lucene and Query DSL queries continue to use /_msearch with Content-Type: application/x-ndjson.
This separation is intentional: /_query returns a different response shape that the plugin parses independently before passing data to Grafana panels.
Try it out!
That also means this is a good moment to try it and give feedback.
The upstream PR and the original tracking issue are public. If you run into problems or have requests, both are open for comments.
Next steps
- Enable the feature in your Grafana 13.0 instance with
elasticsearchESQLQuery = true - Try the example queries above against your own indices
- For metrics data, give the ES|QL
TScommand a spin, against an Elasticsearch 9.2 or Serverless data source. - Read the full ES|QL overview to explore what else the language can do
If you are not yet on Elasticsearch, you can start a free trial at Elastic Cloud.