QQL (Quick Query Language)
QQL is the unified query language in Cruncher for searching, filtering, and transforming logs from any data source.
Inspired by observability languages like Splunk SPL2 and Kusto KQL, QQL is designed to be easy to learn while providing powerful capabilities for log analysis and investigation.
Why QQL?
Section titled “Why QQL?”Different log sources (Grafana, Loki, Kubernetes, Docker) have different native query languages. QQL abstracts away these differences, letting you write once and query any source. You get:
- Consistent syntax across all adapters
- Powerful pipeline model for data transformation
- Simple learning curve with familiar operators
- Local execution for fast feedback
The Pipeline Model
Section titled “The Pipeline Model”QQL uses a pipeline model: data flows through a series of commands, with each command filtering, transforming, or visualizing the data.
How Data Flows Through QQL
Query Structure
Section titled “Query Structure”A QQL query has two parts:
- Controller parameters (optional): Passed to the adapter for server-side filtering
- Pipeline commands: Executed locally on results
[controller_params] | command1 | command2 | command3Example:
level=error | where contains(message, "timeout") | stats count by service- No controller params in this example
where contains(...)— filters records where message contains “timeout”stats count by service— counts records grouped by service field
Commands Quick Reference
Section titled “Commands Quick Reference”where Filter records by condition where status == "error" eval Create or modify fields eval duration_ms = duration / 1000 regex Extract fields using a regex regex "(?P<code>\d+)" message table Select columns and display as table table timestamp, message, level stats Aggregate with count, sum, avg… stats count() by level timechart Time-series aggregation timechart count by level sort Sort results by a field sort timestamp desc unpack Flatten JSON / nested fields unpack json See Commands for full details.
Functions
Section titled “Functions”QQL provides functions for boolean logic, string manipulation, math, and more:
Boolean
contains() Check if a string contains a substring contains(message, "error") startsWith() Check if a string starts with a prefix startsWith(path, "/api") endsWith() Check if a string ends with a suffix endsWith(file, ".log") match() Check if a string matches a regex pattern match(message, "^Error:") isNull() Check if a value is null or missing isNull(optional_field) isNotNull() Check if a value is not null isNotNull(user) String
lower() Convert a string to lowercase lower(user) upper() Convert a string to uppercase upper(level) length() Return the character count of a string length(message) trim() Remove leading and trailing whitespace trim(raw_field) Number
abs() Return the absolute value of a number abs(delta) round() Round a number to the nearest integer round(duration_ms) ceil() Round a number up to the nearest integer ceil(ratio) floor() Round a number down to the nearest integer floor(ratio) Conditional
if() Return one of two values based on a condition if(status == "error", "critical", "ok") case() Return a value from multiple condition branches case(status == "error", "A", "B") See Functions for full details.
Data Types
Section titled “Data Types”QQL supports: string, number, boolean, and null. The adapter assigns types when extracting fields, and type coercion happens implicitly in expressions.
See Data Types for details.
Complete Example
Section titled “Complete Example”Suppose you want to investigate slow API requests in your logs:
service="api" method="POST"
| eval duration_ms = duration / 1000
| where duration_ms > 100
| stats avg(duration_ms), max(duration_ms), count() by endpoint
| sort count desc
What happens:
- Adapter filters for
service=apiandmethod=POSTbefore sending data - Pipeline creates
duration_msfield in milliseconds - Filters to requests slower than 100ms
- Aggregates by endpoint: avg/max duration, count
- Sorts by count descending
Result: A table showing which endpoints have the most slow requests.
Next steps:
- Query Structure — Controller params and syntax details
- Commands — Full command reference
- Functions — All available functions