LogQL Limitations :: Kloudfuse Docs

LogQL Limitations

Kloudfuse supports LogQL for compatibility with Grafana Loki tooling, but the language carries structural limitations — particularly around string and regular-expression searching — that FuseQL was designed to avoid. This page describes each limitation, shows the LogQL behavior, and a comparable FuseQL query.

Summary

Search need LogQL FuseQL
Whole-word (token) search Not expressible; substring or regex scan only 'error' — indexed term search
Case-insensitive search (?i) regex scan Term search is case-insensitive by design
Wildcard match Full regex required "err*"grep wildcards
Substring / prefix / suffix on a field Regex with escaping **, *~, ~* operators
Search a structured field Parse at query time, then label filter Indexed facet comparison, no parser stage
Search without selecting streams Not allowed; requires a stream selector Bare 'term' or * queries

No token (whole-word) search

LogQL’s only body-search primitives are the substring and regex line filters, which scan the log text character by character. There is no way to ask for a whole word. Searching for error also matches errored, errors, and preerror:

{source="payment"} |= "error"

The only whole-word workaround is a regex with word boundaries — which is still a scan, only slower:

{source="payment"} |~ "\\berror\\b"

FuseQL solution. Term search queries the Lucene inverted index that Kloudfuse builds from every log line at ingest. It matches whole tokens only, and because it is an index lookup rather than a scan, it is also the fastest way to search the log body:

'error'

This matches error, Error, and error_count (punctuation creates a token boundary) but not errored or errors. Multiple words — 'connection refused' — require all tokens in the line, in any order, still from the index. See FuseQL text search operators.

Case-sensitive matching only

The LogQL |= filter is strictly case-sensitive: |= "Timeout" misses timeout and TIMEOUT. The workaround is a case-insensitive regex, which abandons the cheaper substring path for a regex scan:

{source="payment"} |~ "(?i)timeout"

FuseQL solution. Term search is case-insensitive by design — the index lower-cases tokens at ingest, so one query covers every capitalization at index speed:

'timeout'

When you do want exact-case matching, FuseQL grep ("Timeout") remains case-sensitive, so both behaviors are available without using a regex expression.

No wildcard syntax

LogQL line filters have no wildcard support: matching err, error, or errored by prefix requires a full regular expression (|~ "err[a-z]*"), with regex escaping and regex cost.

FuseQL solution. Grep supports * (any characters) and ? (any single character) directly:

"err*"

Regex engine and escaping constraints

LogQL regular expressions run on RE2, which has no look-ahead, look-behind, or back-references. Two further pitfalls make LogQL regex error-prone:

{source=~"nginx.*"} |~ "\"(GET|POST) /api/v[0-9]+/\""

FuseQL solution. Most searches that force regex in LogQL have purpose-built FuseQL operators that need no escaping at all: ** (contains), *~ (starts with), ~* (ends with) for label and facet values, and grep wildcards for the log body:

url*~"/api/" and 'post'

Field search requires query-time parsing

LogQL cannot filter on a field inside the message body until a parser stage (json, logfmt, regexp, pattern) has extracted it — and that parsing happens on every line, on every query, at read time:

{source="payment"} | json | status_code="503"

FuseQL solution. Kloudfuse extracts facets at ingest and indexes them, so field conditions are direct, pre-pipe, and served from the inverted index — no parser stage, no per-query parsing cost:

@status_code=503

A stream selector is mandatory

Every LogQL query must start with at least one non-empty label matcher. There is no way to search everything for a string; the closest form selects all streams first, then scans them:

{source=~".+"} |= "OutOfMemoryError"

FuseQL solution. A bare search is a valid query, and because term search resolves through the index, searching everything does not mean scanning everything:

'OutOfMemoryError'

Performance of unanchored scans

Because every LogQL line filter and parser is a scan over the selected streams, broad regex or pattern searches over high-volume sources are expensive. A single unanchored regex can consume significant query capacity. To mitigate problems with LogQL narrow stream selectors used, shortening the time range, and putting the most selective |= filters first.

FuseQL solution. Use index-backed operators changes will change the costs of the query rather than trimming it: term search ('…') and facet comparisons (@facet=value) resolve against the inverted index without touching raw log bodies. This will drastically increase performance.

Wildcard greps pre-filter the index before any per-row work. Use FuseQL term and facet searches whenever possible. Reserve the use of raw substring or regex scans for the final step.