Log search
Free text or a SQL predicate, chosen columns in three layouts, server-side sorting, filter history — and a tail that is its own cursor.

match_all() search; a SQL predicate is passed through verbatim.One box, two languages
The filter box takes either:
- free text — wrapped into OpenObserve’s
match_all('…')full-text function; or - a SQL predicate —
level = 'error' AND status_code >= 500— passed through verbatim.
Which one you typed is decided by a small heuristic that is pure and unit-tested, with quoting and literal escaping so a stray apostrophe cannot break the statement. You do not choose a mode; you just type.
Searches run on submit (or on a stream or range change), never per keystroke, and page by infinite scroll. A hit opens its raw record in a collapsible JSON tree.
Reading the results
Built to the same brief as the Elasticsearch search, so the two log screens stay at parity:
- a column picker over the stream’s schema, with chosen fields as removable chips;
- three layouts — list rows, aligned table, card grid;
- server-side sorting by tapping a table header. Unlike Elasticsearch, every column sorts — this is SQL, not a Lucene mapping;
- values under a
Timestampschema field rendered as local time; - a persisted filter history menu, because the useful query is the one you ran yesterday.
The filter as chips
The committed filter is also shown as one toggleable chip per criterion, so you can drop a condition with a tap instead of editing a string on a phone keyboard.
Splitting is careful: a SQL predicate splits on its top-level AND/OR only — an
operator inside quotes or parentheses is never a split point — and operators ride on the
following criterion, so re-joining after you remove the first one cannot leave a dangling
AND. Free text stays a single criterion, because halving connection refused would
change what match_all() matches.
A live tail without a streaming endpoint
OpenObserve has no streaming endpoint. The tail polls — but it never re-reads the window.
Each tick searches only the slice after the newest record already held (a window
starting one microsecond past it), and paging backwards ends one microsecond before the
oldest. The newest-first search is therefore its own cursor: no search_after equivalent
is needed, and no record is fetched twice.
Scrolling away from the top auto-pauses the tail; a “Resume live” pill reloads from newest; pull-to-refresh reloads.
Bookmarks
A logs view is bookmarkable as the stream + the filter + the chosen columns. The window is deliberately not saved, so reopening a bookmark shows what is happening now rather than replaying an hour from last week.
Back to OpenObserve.