Every observability vendor we know of ships a different query syntax for logs than for metrics. We had a lot of reasons to do the same. We didn't.
Roughly every quarter, someone on the team proposes a grep-like log-specific mini-language, arguing that engineers already know regex and don't want to learn a pipe syntax just to search text. It's a reasonable argument. We keep saying no, and here's the actual reasoning, not just "consistency is nice."
The cost of a second language is paid at the worst possible time
An incident doesn't stay contained to one signal type. You start on a metric spike, pivot to traces to find the slow request, pivot to logs to find the error message, and maybe pivot to a profile to find the hot function. If each of those pivots requires switching mental models and syntax, you're spending your incident budget on translation instead of diagnosis.
One language, one plan
HelixQL treats logs as just another table with a message field and a full-text index. A log search is a where clause like any other:
from logs
| where service == "payments-api" and message contains "connection refused"
| summarize count() by bin(5m), host
| order by bin desc
Under the hood, that contains operator compiles down to the same full-text index a dedicated log tool would use — we didn't sacrifice log search quality to keep one syntax, we just refused to expose a different surface for it.
Where we did add log-specific ergonomics
We didn't pretend logs are identical to metrics everywhere. HelixQL has log-specific parsing helpers (parse_json, extract_field) that don't make sense for a metric stream, and those live as functions callable from the same pipeline rather than a separate mode.
The counterargument we take seriously
The strongest case for a dedicated log syntax is that grep and regex are genuinely faster to type for a narrow text search than a pipe expression, and we don't dismiss that. Our answer was a shorthand: typing a bare string into the HelixQL search bar with no operators compiles to the same contains full-text search a dedicated syntax would offer, so the common case stays one keystroke away, while anything more structured graduates naturally into the full pipeline instead of hitting a syntax wall.
- One query language across metrics, logs, traces, and profiles
- Full-text log search compiled to a dedicated index under one syntax
- Log-specific parsing functions, not a separate query mode
- No context-switch cost mid-incident between signal types