Explore documentation
Exception grouping
Errors are automatically grouped based on their stack trace and other identifying characteristics. This helps you see a consolidated view of similar issues, reducing noise and making it easier to prioritize and resolve problems.
When you view the Errors, you'll see a list of these grouped errors. Each group displays the total number of occurrences, affected users, and a trend chart so you can quickly assess its impact.
Click on any error group to see its detailed view:
- Exceptions: Individual occurrences with timestamps and user details.
- Users: A list of all affected users.
- Tags: Metadata and context extracted from the errors.
- Fix with AI: AI-generated explanations and debugging assistance.
Manage the errors
- Resolve: Mark errors as fixed once you've addressed them.
- Ignore: Silence errors you don't need to see, with options to ignore a specific number of future occurrences.
- Unresolve: Reopen an error if it reoccurs or if you change your mind.
- Bulk actions: Apply these actions to multiple error groups at once for efficient management.
How does it work
By default, Better Stack generates a ._pattern field, which is a hash generated from the characteristics of an error using Vector Remap Language (VRL). For complete details on VRL syntax and capabilities, please refer to the official VRL documentation.
If the error payload contains a fingerprint array, it's used as the primary grouping key. This gives you direct control from your application code.
Otherwise, it combines the call_stack_hash, type, and message of the error. This is the most common grouping logic.
Because it includes the message, errors like these will be treated as separate groups by default:
- User '123' not found.
- User '456' not found.
See it in Errors → Applications → Your application → Transformation.
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
if exists(.summary.type) && .summary.type != null {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + to_string!(.summary.message)
} else {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.message)
}
} else if exists(.summary.type) && .summary.type != null {
to_string!(.summary.type) + ": " + to_string!(.summary.message)
} else {
to_string!(.summary.message)
}
._pattern = sha2(_hash_input)
Customize exception grouping
In Errors → Applications → Your application → Transformation, you can modify the VRL script to change what information is included in the _hash_input.
By altering this input, you can omit the message from the calculated _pattern, remove numerical IDs from it, add a custom tag such as service_name to differentiate between your services, or tweak it in any other way:
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
if exists(.summary.type) && .summary.type != null {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type)
} else {
to_string!(.summary.call_stack_hash)
}
} else if exists(.summary.type) && .summary.type != null {
# Keep message if no call stack is provided
to_string!(.summary.type) + ": " + to_string!(.summary.message)
} else {
to_string!(.summary.message)
}
._pattern = sha2(_hash_input)
_reduced_message = replace(to_string!(.summary.message), r'\d+', "<id>")
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
if exists(.summary.type) && .summary.type != null {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + _reduced_message
} else {
to_string!(.summary.call_stack_hash) + ":" + _reduced_message
}
} else if exists(.summary.type) && .summary.type != null {
to_string!(.summary.type) + ": " + _reduced_message
} else {
_reduced_message
}
._pattern = sha2(_hash_input)
_hash_input = if is_array(.fingerprint) && length!(.fingerprint) > 0 {
to_string(.fingerprint) ?? ""
} else if exists(.summary.call_stack_hash) && .summary.call_stack_hash != null {
if exists(.summary.type) && .summary.type != null {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.type) + ":" + to_string!(.summary.message)
} else {
to_string!(.summary.call_stack_hash) + ":" + to_string!(.summary.message)
}
} else if exists(.summary.type) && .summary.type != null {
to_string!(.summary.type) + ": " + to_string!(.summary.message)
} else {
to_string!(.summary.message)
}
if exists(.tags.service_type) && .tags.service_type != null {
_hash_input = _hash_input + ": " + to_string!(.tags.service_type)
}
._pattern = sha2(_hash_input)