The appendAggregation() function applies an aggregation to a sequence of events and appends the result at the end of that sequence.

Unlike a simple pipe operation, which replaces the existing event sequence with the aggregation result, the appendAggregation() function preserves the original events and adds the aggregation output after them. Preserving the original events makes it possible to include summary rows, such as totals or averages, alongside the detail rows that produced them.

ParameterTypeRequiredDefault ValueDescription
function[a]queryrequired   The aggregation expression to evaluate against the input events. The result of this expression is appended after the last original event in the output sequence. The expression may return one or more events.

[a] The parameter name function can be omitted.

Hide omitted argument names for this function

Show omitted argument names for this function

appendAggregation() Syntax Examples

This example demonstrates appending a single summary row to a sequence of detail events using the appendAggregation() function.

Consider an event set recording individual transaction values keyed by category. The goal is to display each transaction alongside a final row showing the total value across all transactions.

logscale
head()
| appendAggregation({ sum(value, as="value") | key:="total" })

If event data looks like this:

keyvalue
a2
b3
c7

it would return:

keyvalue
a2
b3
c7
total12

This example demonstrates that the appendAggregation() function appends the result of an aggregation to the existing event stream, using avg() to calculate the mean of the value field across all events, and assigning the result a custom key of average_value.

logscale
appendAggregation({
  avg(value, as="value") | key := "average_value"

If event data looks like this:

keyvalue
a2
b3
c5
d6

it would return:

keyvalue
a2
b3
c5
d6
average_value4

Click + next to an example below to get the full details.

Append Grand Total Row to Failed Login Counts

Summarize failed authentication attempts per user and append an overall total using the appendAggregation() function

Query
logscale
auth_result = "failure"
| groupBy(username, function=count(as="failed_attempts"))
| appendAggregation({ sum(failed_attempts, as="failed_attempts")
| username:="All Users" })
Introduction

In this example, the appendAggregation() function is used to produce a per-user count of failed login attempts and then append a grand total row at the bottom of the result table. This allows a security analyst to immediately see both which accounts are being targeted and the total volume of failed authentication events across the environment — a key indicator when investigating a potential brute-force or credential stuffing attack.

The incoming events represent authentication log entries from an identity provider or operating system authentication service. Each event contains a username, a source IP address, and an authentication result. The query filters for failed authentication events, groups and counts them by username, and finally appends a grand total row using appendAggregation().

Example incoming data might look like this:

@timestampusernamesource_ipauth_result
2026-04-01T02:01:00Zalice203.0.113.45failure
2026-04-01T02:01:45Zbob203.0.113.45failure
2026-04-01T02:02:10Zalice203.0.113.45failure
2026-04-01T02:02:55Zcarol198.51.100.22failure
2026-04-01T02:03:20Zalice203.0.113.45failure
2026-04-01T02:03:50Zbob203.0.113.45failure
2026-04-01T02:04:15Zdave198.51.100.22failure
2026-04-01T02:04:40Zcarol198.51.100.22failure
2026-04-01T02:05:05Zalice203.0.113.45success
2026-04-01T02:05:30Zbob203.0.113.45failure
2026-04-01T02:06:00Zdave198.51.100.22failure
2026-04-01T02:06:25Zcarol198.51.100.22failure
2026-04-01T02:07:00Zalice203.0.113.45failure
2026-04-01T02:07:30Zdave198.51.100.22failure
2026-04-01T02:08:00Zbob203.0.113.45failure
Step-by-Step
  1. Starting with the source repository events.

  2. logscale
    auth_result = "failure"

    Filters events to retain only those where the auth_result field equals failure, discarding all successful authentication events from the pipeline. This ensures that only failed login attempts are passed to the subsequent aggregation steps.

  3. logscale
    | groupBy(username, function=count(as="failed_attempts"))

    Groups all remaining events by the username field and, for each group, applies count() to count the number of failed login events. The as parameter returns the result in a new field named failed_attempts. Each row in the output at this stage represents one distinct username and the total number of failed authentication attempts associated with that account.

  4. logscale
    | appendAggregation({ sum(failed_attempts, as="failed_attempts")

    Appends a grand total row to the result set by evaluating a sub-query independently against the current result set and adding its output as a new row at the bottom of the table. Inside the sub-query, sum() calculates the total of all values in the failed_attempts field across all per-user rows, returning the result in a field also named failed_attempts using the as parameter.

  5. logscale
    | username:="All Users" })

    Sets the username field on the appended summary row to the literal string All Users, making the grand total row clearly identifiable in the output table. This assignment closes the sub-query passed to appendAggregation(), resulting in a final output that contains all per-user failed attempt counts from the groupBy() step followed by a single appended row showing the combined failed login total across the entire environment.

  6. Event Result set.

Summary and Results

The query is used to produce a per-user failed authentication count table that includes a grand total row appended at the bottom, enabling security analysts to assess both individual account exposure and the total volume of failed login activity across the environment in a single result set.

This query is useful, for example, to support triage during a brute-force or credential stuffing investigation, where an analyst needs to quickly identify the most targeted accounts while also understanding the overall scale of the attack — without running separate queries for per-user counts and environment-wide totals.

Sample output from the incoming example data:

usernamefailed_attempts
alice4
bob4
carol3
dave3
All Users14

Note that the All Users row is always appended as the last row in the result set regardless of any sorting applied to the per-user rows above it.

Note also that the one successful login event for alice is correctly excluded from the counts because the initial filter retains only failure events before any aggregation takes place.

The appendAggregation() sub-query sums the already-aggregated failed_attempts values from the groupBy() output rather than re-counting the raw events, so the grand total accurately reflects the sum of all per-user counts.

Append Overall Failure Rate to Individual Events

Calculating and appending an overall failure rate to each event record using the appendAggregation() function with percentage()

Query
logscale
head()
| appendAggregation({ percentage({auth_result="failure"}, as="failure_rate")
| format("%,.2f %%", field=failure_rate, as=failure_rate) })
Introduction

In this example, the appendAggregation() function is used to calculate the overall authentication failure rate across all login events and append it as a formatted percentage string to every individual event record.

In an authentication audit scenario, it may be necessary to retain each individual login event while also appending a summary statistic showing the overall failure rate across all events. The head() function preserves the raw events, and appendAggregation() with percentage() calculates the proportion of events where auth_result is failure. The result is then formatted as a percentage string using format() before being appended to the original result set row.

Example incoming data might look like this:

@timestampusernamesource_ipauth_result
2026-03-01T08:01:00Zalice203.0.113.45failure
2026-03-01T08:02:00Zbob203.0.113.45failure
2026-03-01T08:03:00Zcarol198.51.100.22failure
2026-03-01T08:04:00Zalice203.0.113.45failure
2026-03-01T08:05:00Zbob203.0.113.45failure
2026-03-01T08:06:00Zdave198.51.100.22failure
2026-03-01T08:07:00Zcarol198.51.100.22failure
2026-03-01T08:08:00Zalice203.0.113.45success
2026-03-01T08:09:00Zalice203.0.113.45success
2026-03-01T08:10:00Zbob203.0.113.45failure
2026-03-01T08:11:00Zdave198.51.100.22success
Step-by-Step
  1. Starting with the source repository events.

  2. logscale
    head()

    Preserves the raw individual event records in the result set, passing all events through to the next stage of the pipeline without aggregating or dropping any events. By default, head() returns the first 200 events if no limit is specified. This step ensures that the individual event rows remain available for appendAggregation() to join the computed statistic back onto each record.

  3. logscale
    | appendAggregation({ percentage({auth_result="failure"}, as="failure_rate")

    Appends a computed aggregate statistic to every individual event row by evaluating a sub-query independently against the full result set and joining its output back onto each row. Inside the sub-query, percentage() calculates the proportion of events where auth_result equals failure relative to the total number of events. The as parameter returns the result in a new field named failure_rate as a raw floating-point number, for example 72.727272... for 8 failures out of 11 total events.

  4. logscale
    | format("%,.2f %%", field=failure_rate, as=failure_rate) })

    Formats the raw numeric value in failure_rate using the format string %,.2f %%, which rounds the value to two decimal places and appends a literal percent symbol. The field parameter specifies failure_rate as the source field, and the as parameter writes the formatted result back into the same failure_rate field, overwriting the raw numeric value with the human-readable string, for example 72.73 %. The closing brace and parenthesis end the sub-query passed to appendAggregation(), resulting in the formatted failure_rate value being joined back onto every individual event row in the result set.

  5. Event Result set.

Summary and Results

The query is used to calculate the overall authentication failure rate across all login events and append it as a formatted percentage string to each individual event record, allowing both per-event detail and a global summary metric to be visible in the same result set.

This query is useful, for example, to audit authentication logs where security analysts need to review individual login attempts while simultaneously seeing the overall failure rate across the entire dataset, without having to run a separate aggregation query.

Sample output from the incoming example data:

@timestampusernamesource_ipauth_resultfailure_rate
2026-03-01T08:01:00Zalice203.0.113.45failure-
2026-03-01T08:02:00Zbob203.0.113.45failure-
2026-03-01T08:03:00Zcarol198.51.100.22failure-
2026-03-01T08:04:00Zalice203.0.113.45failure-
2026-03-01T08:05:00Zbob203.0.113.45failure-
2026-03-01T08:06:00Zdave198.51.100.22failure-
2026-03-01T08:07:00Zcarol198.51.100.22failure-
2026-03-01T08:08:00Zalice203.0.113.45success-
2026-03-01T08:09:00Zalice203.0.113.45success-
2026-03-01T08:10:00Zbob203.0.113.45failure-
2026-03-01T08:11:00Zdave198.51.100.22success-
----72.73 %

Note that the failure_rate field carries the value of 72.73 % on the last event row, reflecting that 8 of the 11 events had an auth_result of failure.