Important
This feature is in an experimental state. Future updates might change or remove functionality without maintaining compatibility with existing implementations.
The explain:asTable() function analyzes
query performance and identifies bottlenecks or incorrect
filters. explain:asTable() activates
profiling mode, which collects performance statistics during
event processing, including processing time, event counts, and
prefilter effectiveness.
The explain:asTable() function overrides
any previous query results and outputs performance data instead.
Only use the explain:asTable() function for
ad hoc performance analysis. Do not include
explain:asTable() in scheduled searches or
triggers.
| Parameter | Type | Required | Default Value | Description |
|---|---|---|---|---|
showPrefilters | boolean | optional[a] | true | Controls whether prefilter statistics are included in the output. When set to true, it includes information about prefilter effectiveness and optimization. |
[a] Optional parameters use their default value unless explicitly set. | ||||
explain:asTable() Function Operation
When added to a query, the
explain:asTable() function:
Collects performance statistics during query execution.
Measures processing time for each query step.
Tracks the number of events processed for each query step.
Captures timing data for
join()anddefineTable()subqueries.Reports prefilter effectiveness statistics that arise from tag filters and substring filters.
Note
Important usage considerations:
Use this function only for ad hoc performance analysis.
Do not include in scheduled searches.
Do not include in triggers.
The function analyzes the optimized query rather than the original query.
The function is not supported in combination with
correlate().
Output Format
The explain:asTable() function
generates a table with the following columns:
| Column | Description |
|---|---|
| stepID | Identifies each query step |
| step | Describes the operation performed |
| timeMs | Shows processing time in milliseconds |
| events | Lists the number of processed input events to the given step |
| additionalData | Contains supplementary information for specific operations |
Time Measurement Details
The query plan measures processing time for each event using nanosecond-precision wallclock measurement.
LogScale executes query steps in parallel until it reaches the first aggregate function (the aggregate included). The total time represents the sum of all parallel processes. This parallel execution can make the reported time appear larger than the actual real-time duration.
Note
These factors affect time measurement accuracy:
Concurrent system operations affect query timing precision. Any concurrent work on the system can cause imprecision of time spent in the query.
System interrupts appear in query execution time. Any system interrupts to the process running the query will also show up as time spent in the query.
Parallel processing creates cumulative time measurements. The parallel execution nature of Logscale means that reported times are sums across processes.
Query Optimization Effects
Logscale optimizes queries before execution. This means that
the query plan that is actually executed might be different
from the query submitted by the user.
explain:asTable() outputs the
performance of the actual query that is executed, thus its
output might contain steps which are different from the
query submitted by the user.
Query optimization might create these typical differences:
Tag filters become prefilters. For example,
#kind=logsmoves to the prefilter section. Prefilters select data sources from storage and appear in the prefilter output section ofexplain:asTable().Field existence checks appear as new steps. For example,
x = 42creates anx = *filter at the query start.Steps execute in optimized order. The query optimizer places the most efficient filters first.
For example, consider this query:
#kind=logs
| rename(x, as=y)
| category="ScheduledSearch"
When profiling this query with
explain:asTable(), the output shows:
| stepID | step | timeMs | events | additionalData |
|---|---|---|---|---|
| <no value> | prefilters | <no value> | <no value> | bytes searched: 47,494,832, bytes skipped: 141,462,104, skip rate: 74 |
| 1 | category = * | 17 | 115680 | <no value> |
| 2 | category = "ScheduledSearch" | 1 | 3604 | <no value> |
| 3 | rename(x, as=y) | 0 | 772 | <no value> |
explain:asTable() Syntax Examples
Profile a simple filtering and counting operation to
understand the performance characteristics of the
explain:asTable() function.
This example demonstrates basic query profiling with prefilter
statistics disabled
(showPrefilters
parameter set to false).
x = 42
| count()
| explain:asTable(showPrefilters=false)
If input data contains x=42,
x=42, x=41, it would
return:
| stepID | step | timeMs | events |
|---|---|---|---|
| 1 | x = * | 0 | 3 |
| 2 | x = "42" | 1 | 3 |
| 3 | count() | 1 | 2 |
Note that the profiled query plan gets an additional step that
was not present in the original query, namely the x =
* part. This is because the profiling mode runs on
the optimized query plan, which in this case got an extra
field existence check for improved filtering performance.
This example shows profiling a query with
defineTable()
defineTable(
query={sort(name, order=ascending)},
include=[userid, name, role],
name="users"
)
| match(users, field=["user_id"], column=userid)
| explain:asTable()If input data was:
| userid | name | role | user_id |
|---|---|---|---|
| user1 | Alice | admin | <no value> |
| user2 | Bob | user | <no value> |
| user1 | Alice Updated | superadmin | <no value> |
| <no value> | <no value> | <no value> | user1 |
| <no value> | <no value> | <no value> | user3 |
It would return:
| stepID | step | timeMs | events | additionalData |
|---|---|---|---|---|
| <no value> | table: users | 500 | <no value> | <no value> |
| <no value> | prefilters | <no value> | <no value> | bytes_searched: 320, bytes skipped: 0, skip rate: 0 |
| 1 | match(users, field=["user_id"], column=userid) | 10 | 5 | <no value> |
This example shows profiling a query with a join operation.
join(field=x,query={x=42})
| count()
| explain:asTable(showPrefilters=false)
With input data x=42,
x=42, x=40, it produces:
| stepID | step | timeMs | events |
|---|---|---|---|
| <no value> | join subquery at stepID=2 | 480 | <no value> |
| 1 | x = * | 0 | 3 |
| 2 | join(field=x,query={x=42}) | 0 | 3 |
| 3 | count() | 1 | 2 |
explain:asTable() Examples
Click next to an example below to get the full details.
Profile Performance of Join Operations
Analyze query performance of join operations with filtering using
the explain:asTable() function with
join()
Query
join(field=x, query={x=42})
| count()
| explain:asTable(showPrefilters=false)Introduction
In this example, the explain:asTable() function is
used to profile a query that performs a join operation with a filter
condition.
Example incoming data might look like this:
| @timestamp | x | value |
|---|---|---|
| 2025-11-05T10:00:00Z | 42 | 100 |
| 2025-11-05T10:00:01Z | 42 | 200 |
| 2025-11-05T10:00:02Z | 41 | 300 |
| 2025-11-05T10:00:03Z | 42 | 400 |
| 2025-11-05T10:00:04Z | 43 | 500 |
Step-by-Step
Starting with the source repository events.
- logscale
join(field=x, query={x=42})Performs a join operation on the field x, matching only events where x equals
42. Thefieldparameter specifies which field to join on, while thequeryparameter filters the events to include in the join. - logscale
| count()Counts the number of events resulting from the join operation.
- logscale
| explain:asTable(showPrefilters=false)Generates a performance profile of the query execution, with the
showPrefiltersparameter set tofalseto exclude prefilter statistics from the output. Event Result set.
Summary and Results
The query is used to analyze the performance characteristics of a filtered join operation followed by an aggregation.
This query is useful, for example, to understand the performance impact of join operations and their associated filters, helping to optimize queries that require correlating events based on field values.
Sample output from the incoming example data:
| stepID | step | timeMs | events | |
|---|---|---|---|---|
| <no value> | join subquery at stepID=2 | 10 | <no value> | |
| 1 | x = * | 5 | 1 | |
| 2 | join(field=x, query={x=42}) | 5 | 2 | |
| "3","count()","3",1" |
Note that the output shows three distinct steps: the join operation itself, the filter condition, and the final count. The events column shows how many events are processed at each step, with the join operation processing all events, the filter reducing to matching events, and the count producing a single result.
The execution time (timeMs field) for each step helps identify which parts of the query are most resource-intensive.
Profile Performance of Lookup Operations
Analyze query performance when using the
explain:asTable() with
defineTable() and
match() for data enrichment
Query
| defineTable(query={sort(name, order=ascending)}, include=[userid, name, role], name="users")
| match(users, field=["user_id"], column=userid)
| explain:asTable()Introduction
In this example, the explain:asTable() function is
used to profile a query that creates a lookup table and performs data
enrichment using match().
Example incoming data might look like this:
| @timestamp | user_id | action | resource |
|---|---|---|---|
| 2025-11-05T10:00:00Z | U123 | login | app1 |
| 2025-11-05T10:00:01Z | U456 | logout | app2 |
| 2025-11-05T10:00:02Z | U789 | access | app1 |
| 2025-11-05T10:00:03Z | U123 | logout | app2 |
And the user reference data (used in the lookup table) might look like this:
| @timestamp | userid | name | role |
|---|---|---|---|
| 2025-11-05T10:00:00Z | U123 | John Smith | admin |
| 2025-11-05T10:00:00Z | U456 | Jane Doe | user |
| 2025-11-05T10:00:00Z | U789 | Bob Wilson | user |
Step-by-Step
Starting with the source repository events.
- logscale
| defineTable(query={sort(name, order=ascending)}, include=[userid, name, role], name="users")Creates a lookup table named
userscontaining user reference data, sorted in ascending order by the name field. The table includes only the fields specified in theincludeparameter: userid, name, and role. - logscale
| match(users, field=["user_id"], column=userid)Performs a lookup operation that enriches the main event stream with data from the
userslookup table. The lookup matches the user_id field from the main events with the userid column from the lookup table, adding the corresponding name and role fields to matching events. - logscale
| explain:asTable()Generates a performance profile of the entire query execution, including the table definition and lookup operations. Uses default settings to show all execution steps, including prefilters.
Event Result set.
Summary and Results
The query is used to analyze the performance characteristics of table creation and lookup operations in LogScale.
This query is useful to understand the performance impact of subqueries as well as lookup table operations.
Sample output from the incoming example data:
| stepID | step | additionalData | events | timeMs |
|---|---|---|---|---|
| <no value> | table: users | <no value> | <no value> | 200 |
| "","prefilters","bytes searched: 49,834 | ||||
| bytes skipped: 0 | ||||
| skip rate: 0","","" | ||||
| 1 | match(users, field=["user_id"], column=userid) | <no value> | 1 | 4 |
Note that the output shows the execution time for each major operation: table definition, sorting, and matching. The prefilter steps indicate how many events were initially considered before the main operations began.
The match step shows the total number of events that
were successfully enriched with data from the lookup table, while the
table definition steps show the number of events processed for creating
the lookup table.
Profile Query Performance
Analyze query execution steps and their performance metrics using
the explain:asTable() function
Query
x = 42
| count()
| explain:asTable(showPrefilters=false)Introduction
In this example, the explain:asTable() function is
used to profile a simple filtering and counting operation with prefilter
statistics disabled.
Example incoming data might look like this:
| @timestamp | x |
|---|---|
| 2025-11-05T10:00:00Z | 42 |
| 2025-11-05T10:00:01Z | 42 |
| 2025-11-05T10:00:02Z | 41 |
Step-by-Step
Starting with the source repository events.
- logscale
x = 42Filters events where the field x equals
42. - logscale
| count()Counts the number of events that match the filter condition.
- logscale
| explain:asTable(showPrefilters=false)Generates a performance profile of the query execution, with the
showPrefiltersparameter set tofalseto exclude prefilter statistics from the output. Event Result set.
Summary and Results
The query is used to analyze the performance characteristics of a simple filtering and counting operation.
This query is useful, for example, to identify potential performance bottlenecks in queries, how events are filtered through steps, and to understand how the query is optimized.
Sample output from the incoming example data:
| stepID | step | timeMs | events |
|---|---|---|---|
| 1 | x = * | 0 | 3 |
| 2 | x = "42" | 1 | 3 |
| 3 | count() | 1 | 2 |
Note that the profiled query plan includes an additional step
(x = *) that was not present in the original query.
This is because the profiling mode runs on the optimized query plan,
which in this case added an extra field existence check for improved
filtering performance.