Query logging in Elasticsearch
Elasticsearch can log every query operation performed on the cluster.
The following query types are supported:
Query logging helps you answer questions like:
- Which queries are slow or expensive? Use the duration and shard fields to find queries that take the longest or touch the most shards.
- What does the query workload look like? Analyze query types, frequency, and timing to understand traffic patterns across the cluster.
- Why did a query fail or return unexpected results? Inspect the full query text, error details, and targeted indices to debug issues.
- Where is a query coming from? Use the
X-Opaque-Idheader and trace ID fields to identify the originating application or request. - Who is running queries? When user logging is enabled, trace queries back to specific users or API keys.
Query logging is designed to minimize performance impact, but it does consume resources to create and store log entries. Enable it when you need it, and disable it when the investigation is complete. Use the threshold setting to filter out fast queries that are not relevant to your analysis.
Query logging uses an asynchronous mechanism that does not block query execution. If the volume of incoming queries exceeds the logging system's write capacity, some entries may be dropped. If that happens, raise the threshold to log only the most impactful queries.
Query logging supersedes slow logs and ES|QL query logging. We recommend migrating to query logging for more comprehensive and robust logging.
To migrate:
- Enable query logging.
- Set the threshold to match your previous slow log or ES|QL query log levels.
- Disable the old logging.
- Switch to the new query log output.
The log format remains JSON, but some field names differ. To compare field names, refer to the field reference and examples.
Query logging differs from slow logs and ES|QL query logging in the following ways:
- Query logs capture the end-to-end request duration as measured by Elasticsearch, while slow logs only capture shard-level execution time.
- Only query operations are supported, not indexing operations.
- Per-index logging is not available.
- Only one threshold level is supported, not multiple threshold levels.
By default, query logging is turned off. The setup to enable, collect, and ship query logs depends on your deployment type. In all cases, query log entries are written on the coordinating node for each request as *_querylog.json files in the Elasticsearch log directory.
If you ship query logs to a separate destination cluster, that cluster must run Elasticsearch 9.4 or later. Sending this stream to pre-9.4 Elasticsearch versions is not supported. Ingest can fail, or the cluster may only apply some of the index mappings and settings.
The following configuration options are available. Set them in elasticsearch.yml or use the cluster settings API.
elasticsearch.querylog.enabled: Enables or disables query logging. Set totrueto enable. Defaults tofalse.elasticsearch.querylog.threshold: Sets the request duration threshold (in time units, like100msor5s) for logging events. If greater than 0, only requests with durations equal to or greater than the threshold are logged. The default is 0.elasticsearch.querylog.include.user: Enables or disables logging of user information. Set tofalseto disable. Defaults totrue.elasticsearch.querylog.include.system_indices: Controls whether queries targeting only system indices are included in the logs. Set totrueto include them. Defaults tofalse.
To set up query logging on Elastic Cloud Hosted deployments:
- Turn on Logs and metrics for the deployment so it can ship logs to your monitoring or log destination cluster.
- Enable query logging using the cluster settings API:
PUT _cluster/settings
{
"persistent": {
"elasticsearch.querylog.enabled": true
}
}
ECK clusters automatically collect from the *_querylog.json path using the default configuration. Set elasticsearch.querylog.enabled to true in elasticsearch.yml or using the cluster settings API:
PUT _cluster/settings
{
"persistent": {
"elasticsearch.querylog.enabled": true
}
}
To set up query logging on self-managed or ECE clusters:
Set
elasticsearch.querylog.enabledtotrueinelasticsearch.ymlor using the cluster settings API:PUT _cluster/settings{ "persistent": { "elasticsearch.querylog.enabled": true } }Set up a shipper to collect and index the log files:
- Filebeat: The Filebeat Elasticsearch module includes a
querylogfileset that reads the*_querylog.jsonfiles and sends events to thelogs-elasticsearch.querylog-defaultdata stream. Configure Filebeat on the nodes that emit query logs and enable thequerylogfileset. - Elastic Agent: The
elasticsearchElastic Agent integration (1.21.0+) can also collect and ship the same file to the data stream. To enable collection, refer to the query log settings in the integration.
Index privileges for the user that Filebeat or Agent uses
The logs-elasticsearch.querylog-default data stream is a separate destination from filebeat-* indices that often store Elastic Stack monitoring metrics and logs. Grant the output user in Elasticsearch the index and ingest privileges the managed data stream needs for logs-elasticsearch.querylog-*, in addition to anything you allow for filebeat-* or other indices. The exact role name is your choice. The critical part is the extra privilege on the querylog data stream.
The managed index template, logs-elasticsearch.querylog@template, is used for the logs-elasticsearch.querylog-* data stream. The template provides mappings and data stream options to ensure shipped events land in a single, ECS-aligned destination you can use with Kibana and the Elastic Agent or Beats assets that ship with the product.
The logs-elasticsearch.querylog-* data stream is initialized with the following configurations and default behaviors:
- Index mode: The data stream uses LogsDB indexing.
- Query volume: When logging is enabled, the default
elasticsearch.querylog.thresholdis0in time units, so every request can be eligible depending on other options. A busy cluster can produce a very large number of lines. Raise the threshold if you need to cap volume. - Retention and failure handling: A default data stream lifecycle is attached with a 2 day retention window so the data stream does not grow without bound. The failure store is on, with a 7 day retention for failed ingest.
- Management UI: You can also manage the data stream lifecycle, routing, and related controls in the Streams app.
Once the shipper starts ingesting into logs-elasticsearch.querylog-* on the destination cluster, you can explore the logs in Kibana.
The elasticsearch Elastic Agent integration (1.21.0+) bundles the following Kibana assets, which you can install even if you do not use the Agent to ship the query logs:
- A data view, "Elasticsearch query logs," for
logs-elasticsearch.querylog-* - A dashboard, "Elasticsearch query analytics," for analyzing your historical queries
Install the assets from Fleet (or the integration's Assets tab) when you are ready to explore the indexed stream.
Alternatively, create a data view for logs-elasticsearch.querylog-* and use Discover to filter on event.dataset: elasticsearch.querylog.
The following examples show the JSON structure of query log entries for different query types and scenarios.
This example shows a standard search request using Query DSL. The log entry includes the JSON-escaped query body, execution duration in nanoseconds and milliseconds, and the security context of the user who initiated the request.
{
"@timestamp": "2026-03-13T01:01:57.391Z",
"log.level": "INFO",
"auth.type": "REALM",
"elasticsearch.querylog.indices": [
"query_log_test_index"
],
"elasticsearch.querylog.query": "{\"size\":10,\"query\":{\"match_all\":{\"boost\":1.0}}}",
"elasticsearch.querylog.result_count": 3,
"elasticsearch.querylog.dsl.total_count": 3,
"elasticsearch.querylog.shards.successful": 1,
"elasticsearch.querylog.took": 2465042,
"elasticsearch.querylog.took_millis": 2,
"elasticsearch.querylog.type": "dsl",
"elasticsearch.task.id": 4839,
"event.duration": 2465042,
"event.outcome": "success",
"http.request.headers.x_opaque_id": "opaque-1773363717",
"trace.id": "0af7651916cd43dd8448eb211c80319c",
"user.name": "elastic",
"user.realm": "reserved",
"ecs.version": "1.2.0",
"service.name": "ES_ECS",
"event.dataset": "elasticsearch.querylog",
"process.thread.name": "elasticsearch[node-1][search][T#6]",
"log.logger": "elasticsearch.querylog",
"elasticsearch.cluster.uuid": "gjYgb-uQQAuLmDoKlQInZw",
"elasticsearch.node.id": "juurGSfgRYGwTP2ttZbtOQ",
"elasticsearch.node.name": "node-1",
"elasticsearch.cluster.name": "querying"
}
- The full Query DSL body, JSON-escaped
- Request duration in milliseconds
- Query language identifier
- Whether the request succeeded or failed
- User information, included when
elasticsearch.querylog.include.useristrue
This example shows a query that targets indices on remote clusters. The entry includes additional fields for remote cluster counts, remote cluster names, and per-phase profiling metrics for the request execution.
{
"@timestamp": "2026-03-23T16:59:53.538Z",
"log.level": "INFO",
"auth.type": "REALM",
"elasticsearch.querylog.clusters.remote_count": 2,
"elasticsearch.querylog.clusters.remotes": [
"remote2",
"remote1"
],
"elasticsearch.querylog.clusters.successful": 3,
"elasticsearch.querylog.clusters.total": 3,
"elasticsearch.querylog.esql.profile.analysis.took": 1121750,
"elasticsearch.querylog.esql.profile.dependency_resolution.took": 5040750,
"elasticsearch.querylog.esql.profile.parsing.took": 989417,
"elasticsearch.querylog.esql.profile.planning.took": 8038459,
"elasticsearch.querylog.esql.profile.preanalysis.took": 30417,
"elasticsearch.querylog.esql.profile.query.took": 40847750,
"elasticsearch.querylog.indices": [
"query_log_test_index",
"remote2:query_log_test_index",
"remote1:query_log_test_index"
],
"elasticsearch.querylog.query": "FROM query_log_test_index,*:query_log_test_index | LIMIT 11",
"elasticsearch.querylog.result_count": 5,
"elasticsearch.querylog.shards.successful": 3,
"elasticsearch.querylog.took": 40847750,
"elasticsearch.querylog.took_millis": 40,
"elasticsearch.querylog.type": "esql",
"elasticsearch.task.id": 7215,
"event.duration": 40847750,
"event.outcome": "success",
"http.request.headers.x_opaque_id": "opaque-1774285192",
"trace.id": "0af7651916cd43dd8448eb211c80319c",
"user.name": "elastic",
"user.realm": "reserved",
"ecs.version": "1.2.0",
"service.name": "ES_ECS",
"event.dataset": "elasticsearch.querylog",
"process.thread.name": "elasticsearch[node-1][esql_worker][T#11]",
"log.logger": "elasticsearch.querylog",
"elasticsearch.cluster.uuid": "gjYgb-uQQAuLmDoKlQInZw",
"elasticsearch.node.id": "juurGSfgRYGwTP2ttZbtOQ",
"elasticsearch.node.name": "node-1",
"elasticsearch.cluster.name": "querying"
}
- Number of remote clusters involved in the query
- ES|QL profiling metrics, broken down by query phase, in nanoseconds
- Remote indices are prefixed with the cluster name
This example shows a log entry for a request that did not complete successfully. When a query fails, the log includes the error.type and error.message fields to provide the exception class and a description of the failure.
{
"@timestamp": "2026-03-04T19:40:35.271Z",
"log.level": "INFO",
"auth.type": "REALM",
"elasticsearch.querylog.indices": [
"nonexistent_index_xyz"
],
"elasticsearch.querylog.query": "any where true",
"elasticsearch.querylog.result_count": 0,
"elasticsearch.querylog.took": 1326334,
"elasticsearch.querylog.took_millis": 1,
"elasticsearch.querylog.type": "eql",
"error.message": "no such index [Unknown index [nonexistent_index_xyz]]",
"error.type": "org.elasticsearch.index.IndexNotFoundException",
"event.duration": 1326334,
"event.outcome": "failure",
"http.request.headers.x_opaque_id": "opaque-1772653234",
"user.name": "elastic",
"user.realm": "reserved",
"ecs.version": "1.2.0",
"service.name": "ES_ECS",
"event.dataset": "elasticsearch.querylog",
"process.thread.name": "elasticsearch[node-1][search_coordination][T#6]",
"log.logger": "elasticsearch.querylog",
"elasticsearch.cluster.uuid": "gjYgb-uQQAuLmDoKlQInZw",
"elasticsearch.node.id": "juurGSfgRYGwTP2ttZbtOQ",
"elasticsearch.node.name": "node-1",
"elasticsearch.cluster.name": "querying"
}
- Human-readable error description
- Java exception class for programmatic error handling
- Set to
failurewhen the request errors
Each query log entry is a JSON object with fields from two sources:
- Standard Elastic Common Schema (ECS) fields present in every entry.
- Query-specific fields under the
elasticsearch.querylog.*namespace with details about the operation.
These fields are present regardless of query type. Some fields may be present only in specific circumstances, see field descriptions below.
@timestamp: The timestamp of the log entry.event.outcome: Whether the request was successful (success) or not (failure).event.duration: How long (in nanoseconds) the request took to complete.error.typeanderror.message: Error information fields if the request failed.user.*: User information fields if enabled.http.request.headers.x_opaque_id: The X-Opaque-Id header value if enabled. To learn more, refer to X-Opaque-Id HTTP header.trace.id: Trace ID information, if provided by the client.elasticsearch.task.id: The task ID of the request.elasticsearch.node.id: The node ID of the request.elasticsearch.parent.task.id: The task ID of the parent task, if this request is a child of another request.elasticsearch.parent.node.id: The node ID of the parent task, if this request is a child of another request.
Using parent task and node IDs, you can correlate the log entries of queries initiated by other queries.
These fields are specific to query logging and common for all query languages.
elasticsearch.querylog.type: The type of operation (dsl,esql,sql,eql).elasticsearch.querylog.took: How long the request took to complete, in nanoseconds. This is the same value asevent.duration.elasticsearch.querylog.took_millis: How long (in milliseconds) the request took to complete.elasticsearch.querylog.timed_out: Boolean specifying whether the query timed out.elasticsearch.querylog.query: The query text (depending on the query language, could be string or JSON).elasticsearch.querylog.indices: Array containing the indices that were requested. These may not be fully resolved. May contain wildcards and index expressions, and it is not guaranteed these resolve to any specific index or exist at all. Not supported forsqloresqlqueries.elasticsearch.querylog.result_count: The number of results actually returned in the response.elasticsearch.querylog.is_system: If system index logging is enabled, indicates whether the request was performed only on system indices.elasticsearch.querylog.has_aggregations: For adslsearch result, this boolean flag specifies whether the result has a non-empty aggregations section.elasticsearch.querylog.shards.successful,elasticsearch.querylog.shards.skipped,elasticsearch.querylog.shards.failed: How many shards were successful, skipped, and failed during the query execution.
When the query is cross-cluster, the following fields are available:
elasticsearch.querylog.clusters.total: Indicates the total number of clusters involved in the query execution. Note that this field does not include the local cluster if no indices from it were involved in the query.elasticsearch.querylog.clusters.remote_count: Indicates the number of remote clusters involved in the query execution.elasticsearch.querylog.clusters.successful: Indicates the number of clusters involved where the query execution was successful.elasticsearch.querylog.clusters.failed: Indicates the number of clusters involved in which the query execution failed. Only set if there were any failed clusters.elasticsearch.querylog.clusters.partial: Indicates the number of clusters involved in which the query execution was partially successful. Only set if there were any partially successful clusters.elasticsearch.querylog.clusters.remotes: Enumerates other clusters or projects involved in the query execution.elasticsearch.querylog.is_remote: Fordslqueries, indicates whether the query was initiated by another cluster.
Additional fields specific to the Elasticsearch environment may appear in log entries. To view complete examples, refer to Example query log entries.
Each query language may also include its own fields, prefixed with elasticsearch.querylog..
elasticsearch.querylog.dsl.total_count: The “total hits” value, as reported by the search response.elasticsearch.querylog.dsl.total_count_partial: Set totruewhen the total count does not reflect the full number of matches, for example due to thetrack_total_hitslimitation.
elasticsearch.querylog.esql.profile.*.took: ES|QL query profiling metrics, in nanoseconds