LogTide

Query Syntax

One search language for the logs explorer, live tail, saved searches, export and threshold alert rules. It is the same on TimescaleDB, ClickHouse and MongoDB, so a query you write once returns the same logs on every storage engine.

Overview

A query is a list of clauses separated by whitespace. Every clause must match, so timeout db finds logs containing both words. Clauses are either free text (matched against the log message) or key:value field filters.

Syntax Meaning
timeoutThe message contains the word timeout (case-insensitive).
"connection refused"The message contains this exact phrase (case-insensitive).
-debug · -"health check"Exclude logs matching the word or phrase.
timeout OR refusedEither word. OR must be uppercase.
service:apiField equals value.
-service:apiField does not equal value.
level:(error OR critical)Field is one of the listed values.
-level:(debug OR info)Field is none of the listed values.
user.id:*Metadata key exists.
-user.id:*Metadata key does not exist.
path:*admin*Metadata value contains admin (case-insensitive).
error.message:"disk full"Quote a field value that contains spaces: key:"value with spaces".

The logs explorer highlights the query as you type, underlines the offending token when something is wrong, and autocompletes field names and values. An explicit AND between two clauses is accepted and means the same as whitespace.

Words and Phrases

  • Word - a run of letters, digits and underscores, such as timeout or user_id. It matches whole words in the message, ignoring case. time does not match timeout.
  • Phrase - text in double quotes. It matches anywhere in the message as a case-insensitive substring. Use \" for a quote and \\ for a backslash inside a phrase.
  • Anything else is a phrase too. A term containing punctuation, such as 10:42:13, http://example.com/health, 10.0.0.5, Error: or an IPv6 address, is searched as plain text, so you can paste a log line fragment straight into the search bar.

When does key:value count as a field?

Only when the key starts with a letter or underscore, contains only letters, digits, _, . or -, and is immediately followed by a non-empty value that does not start with //. Everything else stays free text, which is why 10:42:13 and http://x need no quoting.

Field Clauses

Clause Operator Matches
key:valueequalsThe value, compared as text, is exactly equal.
-key:valuenot equalsAnything else, including logs without the key.
key:(a OR b)inEqual to any value in the list.
-key:(a OR b)not inEqual to none of the values, including logs without the key.
key:*existsThe key is present.
-key:*not existsThe key is absent.
key:*text*containsThe value contains the text, ignoring case.

Numbers and booleans compare by their text, so status:500 and ok:false work as you would expect. Arrays and objects have no text form and never match an equals, in or contains clause. Excluding a contains match (-path:*admin*) is not supported and is reported as an error.

Negation and Missing Fields

Put - directly in front of a word, phrase or field clause to exclude it. A negated clause matches logs that do not have the key at all. For example -env:prod returns staging logs and logs that carry no env field. To require the key and exclude one value, combine the two clauses:

env:* -env:prod

OR Groups

OR (uppercase) joins the two terms next to it into an any-of group. Groups are AND-ed with the rest of the query, so timeout OR refused db means (timeout OR refused) AND db. There are no nested parentheses.

  • Between free-text terms: timeout OR "connection reset".
  • Between values of one field: env:prod OR env:staging is the same as env:(prod OR staging).
  • OR between clauses of different fields (service:api OR level:error) is an error with a hint. Run two searches, or use a list on a single field.
  • OR cannot join excluded terms (-a OR -b).
  • Parentheses are only valid as a value list after a key, like key:(a OR b). Quote text that contains parentheses.

Built-in Fields

Key Alias Supported clauses
level-equals, not equals, in, not in. Values: debug, info, warn, error, critical (any case).
service-equals, not equals, in, not in.
hosthostnameequals, not equals, in, not in.
tracetrace_idequals only.
sessionsession_idequals only.
spanspan_idequals only.

Any other operator on a built-in field (service:*api*, level:*, -trace:abc) is a parse error, so the search bar, the API and live tail always agree. Each built-in can appear once as a positive filter; use a list (service:(api OR worker)) for several values. Key names are case-sensitive.

Metadata Fields

Any other key filters on the log's metadata. Nested objects are reached with dots: user.id:42 finds {"user": {"id": 42}}. If a log has a metadata key that literally contains a dot, such as http.status_code, the exact key wins over the nested path. Paths never index into arrays.

env:(prod OR staging)
user.id:*
-region:eu-west-1
http.status_code:500
path:*admin*

The logs explorer discovers metadata keys from recent logs and offers them as autocomplete suggestions and facets. Clicking a value in the details panel adds or removes the matching clause in the query text, so the query bar is always the full description of the current view.

Time and Scope

Time and project are not part of the query text. They are set by the scope bar at the top of every explore page and carried in the page URL, so a shared link opens with the same scope:

URL parameter Meaning
projectComma-separated project ids. Missing means all projects you can see.
rangeA preset: 15m, 1h, 6h, 12h, 24h, 48h, 3d, 7d, 14d or 30d.
from · toA custom window as ISO 8601 timestamps, used instead of range.

Through the API, pass projectId, from and to as query parameters next to q. The last used scope is remembered per browser.

Examples

Query Finds
level:(error OR critical) service:checkoutErrors and criticals from the checkout service.
"connection refused" -host:web-1The phrase on every host except web-1.
timeout OR deadline dbLogs that mention db and either timeout or deadline.
user.id:42 -level:debugEverything user 42 did, without debug noise.
trace:4bf92f3577b34da6a3ce929d0e0e4736All logs of one trace.
order_id:* -payment:*Order logs that carry no payment field.
path:*/admin* -"health check"Admin paths, excluding health check chatter.

Errors

Only genuinely malformed input is rejected: an unclosed quote or parenthesis, an OR without a term on both sides, an OR between different fields, an unknown level value, or an operator a built-in field does not support. Each error carries the character position of the problem. The UI underlines that token, and the API answers HTTP 400. For service:api OR level:error the response is:

{
  "error": "OR can only join free-text terms, or values of the same field; use key:(a OR b)",
  "position": 12
}

API

The query goes in the q parameter. It is accepted by the same endpoints, with the same authentication as GET /api/v1/logs:

Endpoint Purpose
GET /api/v1/logsSearch logs. limit up to 1,000, cursor pagination.
GET /api/v1/logs/countExact count of matching logs.
GET /api/v1/logs/histogramVolume over time, stacked by level. Optional bucketSeconds (1 to 604,800); chosen automatically when omitted.
GET /api/v1/logs/facetsTop values with counts. fields is a comma list of query keys (default level,service,host), limit defaults to 10 and is at most 50.
GET /api/v1/logs/fieldsMetadata keys seen in recent logs. sampleSize 100 to 5,000, default 1,000.
GET /api/v1/logs/streamLive tail over SSE. The full query is applied on the server.
curl -G http://localhost:8080/api/v1/logs \
  -H "X-API-Key: lp_your_api_key" \
  --data-urlencode 'projectId=PROJECT_UUID' \
  --data-urlencode 'from=2026-10-30T08:00:00Z' \
  --data-urlencode 'to=2026-10-30T09:00:00Z' \
  --data-urlencode 'q=level:(error OR critical) "connection refused" -host:web-1'

Legacy parameters. searchMode=substring treats the whole q as one phrase, as before. searchMode=fulltext or no searchMode parses q with this grammar, so existing callers keep working. Alert rules accept the same syntax in their query field (threshold rules only); an invalid query is rejected with HTTP 400 and a position.

Limits

Limit Value
Query length through the API2,000 characters
Metadata field clauses per query10
Values in one list50
Length of one value1,000 characters
Length of a field key200 characters
Histogram bucketsAt most 1,000. A wider range with a small bucketSeconds is rejected with HTTP 400; omit bucketSeconds to let LogTide pick a size.
Facet fields per request12
Facet values per field50
Metadata keys discovered500, from the most recent sample of logs
Analytics reads (count, histogram, facets, fields)30 second time limit on every engine

Numeric comparisons (status:>=500) and nested parentheses are not supported.

Storage Engines

The grammar, the field rules and the handling of missing keys are identical on all three engines and pinned by a shared conformance suite. The differences are in how free-text words are looked up:

Engine Words and phrases
TimescaleDBWords use the existing full-text index with English stemming, so connections also finds connection. Phrases are case-insensitive substring matches.
ClickHouseWhole-word and case-insensitive substring matching, no stemming.
MongoDBWhole-word and case-insensitive substring matching, no stemming. Requires MongoDB 5.2 or later (7.0 recommended).

English stemming on TimescaleDB is the one documented difference. Quoted phrases are never stemmed. See Storage Engines for engine requirements.

Esc

Type to search across all documentation pages