Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

logana

A fast terminal log viewer for files of any size — including multi-GB logs. Built on SIMD-accelerated line indexing, search, and filtering. Auto-detects log formats, filters by pattern, regex, field value, or date range — bookmark lines, add annotations, and export your analysis.

logana demo

What is it for

Log files are large and noisy. logana helps you cut through them — filter down to what matters, bookmark key lines, attach notes, and export your findings. Everything is saved between sessions, so you never lose your place. Filter sets can also be saved and reused across files: once you have filters for the key messages and components you care about, loading them on a new file gives you a focused view immediately.

The typical use cases are:

  • Incident investigation — narrow down a multi-gigabyte production log to the relevant window using date-range and pattern filters, mark the key lines, attach notes explaining what you found, and export your findings to Markdown or Jira.

  • Long-running process monitoring — stream a running process or Docker container, watch it in tail mode, and flip back to filter history without losing our place.

  • Recurring log review — save a filter set for a well-known log format (e.g. “show only ERRORs from the auth service”) and reuse it the next time you need it.

What makes it different

Log format detection

logana recognises common log formats automatically — JSON, syslog, journalctl, logfmt, logback, Spring Boot, Python logging, Apache access logs, DLT (AUTOSAR), and more — and shows each line broken into columns: timestamp, level, service name, message. You can hide columns you don’t care about and reorder the ones you do, per file.

Filtering

Include and exclude filters stack freely. Include filters narrow the view to matching lines; exclude filters hide lines on top of that. Both support plain text and regular expressions.

You can also filter by time: > Feb 21 01:00:00, 01:00:00 .. 02:00:00, >= 2024-02-22. Date filters work the same way regardless of which log format is open.

Filtering runs in the background — the UI stays responsive on large files, and changing a filter cancels the previous scan immediately.

Persistent sessions

Filters, scroll position, bookmarks, and notes are saved per file and restored automatically on next open. Filter sets can be exported to a file and loaded on the command line with --filters, so the same filters work across multiple log files. Combined with --tail, the last matching line is shown immediately after loading.

Notes and export

Bookmark individual lines with m. When you want to attach context, select a range with V (line selection) or v (character selection) and press c to write a note. :export produces a document with your notes and the relevant log lines ready to share.

Feels like vim. Full motion support: j/k, gg/G, Ctrl+d/u, //? search, n/N between matches, w/b/e word motions, f/t character find, count prefixes on all motions. All keys are configurable.

Feature Overview

FeatureDescription
Auto-detected formatsJSON, syslog, journalctl, logfmt, DLT, logback/log4j2, Spring Boot, Python, loguru, Apache CLF, and more
Structured columnsTimestamp, level, service, message as separate columns; show/hide/reorder per file
Persistent sessionsFilters, scroll position, bookmarks, and notes restored on next open
Include/exclude filtersPlain text or regex; include and exclude stack freely
Date and time filtersLimit the view to a time window or comparison
Background filteringRuns in the background; changing a filter cancels the previous scan immediately
Startup filters--filters loads a filter set at launch; --tail jumps to the last match
Notes and exportAttach comments to lines; export to Markdown or Jira with :export
Visual line modeSelect a line range to bookmark, annotate, copy, or build a filter from
Visual character modeSelect within a line using vim motions to filter, search, or copy
Vim navigationFull motions: j/k, gg/G, w/b/e, f/t, count prefixes, //? search
Multi-tabOpen multiple files, Docker streams, or DLT connections side-by-side
DockerAttach to any running container with :docker
DLTStream from a DLT daemon with :dlt, or open binary .dlt files directly
Value coloringHTTP methods, status codes, IP addresses, and UUIDs colored automatically; filter colors always take priority and multiple filter styles (fg + bg) compose
ConfigurableAll keys remappable; 19 bundled themes; custom themes and export templates

Installation

Download from the Releases page, or use the install script:

Linux / macOS

curl -fsSL https://github.com/pauloremoli/logana/releases/latest/download/install.sh | sh

Windows (PowerShell)

irm https://github.com/pauloremoli/logana/releases/latest/download/install.ps1 | iex

Homebrew (macOS / Linux)

brew tap pauloremoli/logana
brew install logana

Cargo (crates.io)

cargo install logana

Cargo (from source)

cargo install --git https://github.com/pauloremoli/logana

Quick Start

Opening Logs

# Open a file
logana app.log

# Open a directory — pick which files to open from a file picker
logana /var/log/

# Pipe from stdin
journalctl -f | logana
tail -f app.log | logana

# Stream a Docker container
logana            # then type :docker

# Preload a saved filter set — filters are applied in a single pass during indexing
logana app.log --filters my-filters.json

# Add inline filters directly on the command line
logana app.log -i error -o debug
logana app.log -i "--field level=ERROR" -t "> 2024-02-21"

# Start at the end of the file with tail mode enabled
logana app.log --tail

# Combined: preload filters and jump to the last matching line immediately
logana app.log --filters my-filters.json --tail

Opening Compressed and Archive Files

logana app.log.gz
logana logs.tar.gz
logana logs.zip

Opening a .gz/.bz2/.xz/.zip/.tar/.tar.gz/.tar.bz2/.tar.xz file — whether from the command line or with :open inside the TUI — shows a popup listing everything inside it as a tree, without extracting anything yet. If an entry is itself an archive (a .zip inside a .tar.gz, for example), one nested level is expanded automatically so its contents show as nested rows too; anything nested deeper than that shows as a collapsed row you can expand yourself.

  • Space toggles the file under the cursor. Toggling a nested archive’s own row selects or deselects everything inside it at once.
  • m marks the file under the cursor to be merged instead — independently of Space, and toggled the same way for a nested archive’s whole subtree.
  • Right reads and reveals a not-yet-expanded nested archive’s contents (or just reveals them again, with no re-read, if they were already fetched and merely folded shut). Left folds an expanded archive’s contents back out of view.
  • a / n select or deselect every file.
  • Enter extracts the confirmed selection: Space-toggled files each open as their own tab, and m-marked files are extracted and combined into a single timestamp-sorted tab. Both happen together in one press. If any m-marked file’s format can’t be recognized, only the merge is skipped (with an error naming the file) — toggled files still open normally.
  • Esc cancels without extracting anything.

First Steps

Once logana opens, you’ll see the log content with the detected format shown in the title bar.

Basic navigation:

  • j / k — scroll down / up one line
  • gg / G — jump to first / last line
  • Ctrl+d / Ctrl+u — half page down / up
  • q — quit

Add your first filter:

  • Press i and type a pattern to show only matching lines
  • Press o and type a pattern to hide matching lines
  • Press f to open the filter manager and see all active filters

Search:

  • Press / and type a query to search forward
  • Press n / N to jump between matches

Commands:

  • Press : to open command mode
  • Type a command and press Enter (Tab completes commands, flags, and paths)

Navigation

logana uses Vim-style keybindings for all navigation. All bindings are configurable — see Keybindings.

Scrolling

KeyAction
j / DownScroll down one line
k / UpScroll up one line
Ctrl+dHalf page down
Ctrl+uHalf page up
PageDownFull page down
PageUpFull page up
ggJump to first line
GJump to last line

Horizontal Scroll

When line wrap is off, long lines can be scrolled horizontally:

KeyAction
h / LeftScroll left
l / RightScroll right
0Jump to start of line (reset horizontal scroll)
$Jump to end of line

Count Prefix

Prepend a number to most motion keys to repeat them:

5j      — scroll down 5 lines
10k     — scroll up 10 lines
3Ctrl+d — scroll down 3 half-pages
50G     — jump to line 50
3gg     — jump to line 3

The active count is shown in the status bar (e.g. [NORMAL] 5). Counts are capped at 999,999.

Go to Line

From command mode, type a bare line number to jump there:

:500    — jump to line 500
:1      — jump to the first line

If the target line is hidden by an active filter, logana jumps to the nearest visible line instead.

Marks

Mark important lines to jump back to them or include them in an export.

KeyAction
mMark / unmark the current line
MToggle marks-only view (show only marked lines)

Marked lines show a highlighted indicator in the gutter. Marks are per-session and not persisted across runs.

Visual Selection

KeyAction
VEnter visual line mode — select whole lines for bulk mark / comment / yank / filter
vEnter visual char mode — move a cursor within the current line and select a text range

See Visual Line Mode and Visual Character Mode for the full key reference.

Log Level Navigation

Jump directly between error and warning lines without scrolling:

KeyAction
eJump to next ERROR / FATAL line
EJump to previous ERROR / FATAL line
wJump to next WARN line
WJump to previous WARN line

Navigation wraps to the nearest visible line that matches the level. Positions are pre-indexed whenever the visible set changes, so each jump is O(log n) regardless of file size.

Continuation Lines

Multiline entries (stack traces, wrapped messages, or a custom schema’s structured continuation lines) can be collapsed to just their first line:

KeyAction
>Collapse the entry under the cursor
<Expand the entry under the cursor

:collapse and :expand apply the same fold/reveal file-wide instead of to a single entry. >/< still work afterward to flip an individual entry against whatever the file-wide default currently is.

Line Wrap

Toggle line wrapping with :wrap or via the UI menu (uw). When wrap is enabled, long lines flow onto multiple terminal rows and all viewport math accounts for the extra rows automatically.

Visual Line Mode

Press V in normal mode to enter visual line mode. The current line becomes the anchor.

KeyAction
j / kExtend selection down / up
cAttach a comment to the selected lines
mMark / unmark all selected lines (toggles group)
yYank (copy) selected lines to system clipboard
/Open search bar pre-filled with the first selected line
EscCancel

Selected lines are highlighted in the log panel.

Visual Character Mode

Press v in normal mode to enter character-level visual mode. The cursor is placed on the current line — at the start of the active search match if one exists, otherwise at column 0. Move the cursor freely with vim motions before anchoring a selection.

Cursor motions

KeyAction
h / l / Left / RightMove left / right one character
w / b / eWord start forward / backward / word end
W / B / EWORD (whitespace-delimited) variants
0Move to start of line
^Move to first non-blank character
$Move to end of line
f<c>Find next occurrence of character c
F<c>Find previous occurrence of character c
t<c>Move to one before next c
T<c>Move to one after previous c
;Repeat last f/F/t/T motion
,Repeat last motion in reverse

Anchoring and actions

Press v again to anchor the selection at the current cursor position. Any subsequent cursor motion extends the selection. Without an anchor, actions operate on the single character under the cursor.

KeyAction
vAnchor selection at cursor
iOpen command bar pre-filled with filter <selected>
oOpen command bar pre-filled with exclude <selected>
/Open search bar pre-filled with selected text
yYank (copy) selection to system clipboard
EscCancel

The selected character range is highlighted with a reversed colour in the log panel. When a f/F/t/T motion is pending (waiting for the target character), the mode bar shows pending — type a character.

Filtering

Filters are the primary way to narrow the log view. They are layered: include patterns narrow the view, and exclude patterns hide matching lines on top of whatever include filters already selected.

Quick Keys

KeyAction
iAdd include filter (show only matching lines)
oAdd exclude filter (hide matching lines)
fOpen filter manager
FToggle all filtering on/off
HToggle highlight mode (see below)

How Filters Work

Include filters: If any include filter is enabled, only lines matching at least one include filter are shown.

Exclude filters: Any line matching an enabled exclude filter is hidden, regardless of include filters.

Highlight filters: Apply their color styling to matching lines but never affect visibility — every line stays shown. See Text Filters for details.

No filters: All lines are shown.

All filter types support:

  • Text search — fast multi-pattern matching
  • Regular expressions — full regex syntax, opt-in with --regex / -r
  • Case-insensitive matching — opt-in with --ignore-case / -i

Highlight Mode

Press H in normal mode to put all active filters — include, exclude, and highlight — into highlight mode: every line in the file stays visible, but filter colors still render on their matches. This is for reading the full context around the lines you actually care about — an include/exclude filter narrows the log down to just the matches, but the surrounding lines that explain why something happened are often not in the filter at all. Highlight mode gives you the whole log back, with your filters still marking what matters, so you can scroll through real context without losing track of what you were looking for. The sidebar title shows [HIGHLIGHT] while it’s active. Press H again to return to normal filtering.

Filter Persistence

Filters are saved to SQLite and automatically restored the next time you open the same file. When you reopen a file, logana detects whether the file has changed (via hash) and prompts you to restore the previous session.

Filter Manager

Press f to open the filter manager popup, which lists all active filters. Navigation matches the log panel: count-prefixed motions, page scrolling, jump-to-top/bottom, and search. A filter row can also be double-clicked directly in the sidebar (without opening the filter manager first) to toggle it.

KeyAction
j / kMove selection down / up — accepts a count prefix, e.g. 4j moves down 4
Ctrl+d / Ctrl+uHalf page down / up
PageDown / PageUpFull page down / up
gg / GJump to the first / last filter — {count}gg or {count}G jumps to filter N
/Search the filter list (see below)
SpaceToggle selected filter on/off
eEdit selected filter’s pattern
dDelete selected filter
cSet highlight color for selected filter
tAdd a date/time range filter
hAdd a highlight filter
J / KMove filter down / up (order affects priority)
AToggle all filters on/off
CClear all filters
EscClose filter manager

Searching the Filter List

Press / to start typing a search query — the sidebar title immediately shows a type to search... placeholder so it’s clear you’re now typing a query, even before you’ve entered any characters. The query is matched as a regex (case-insensitive) against each filter’s type, pattern, and group, e.g. error|warn narrows to filters whose row text contains either word; an invalid/incomplete regex (likely while still typing, e.g. an unclosed () falls back to a plain substring match instead of matching nothing. Backspace edits the query; j/k move between the narrowed matches. Enter confirms your selection and shows the full list again; Esc cancels and restores whatever was selected before you started searching. While searching, every key is captured as query text — including letters that are normally shortcuts (e, d, i, …) — so none of the usual filter-manager actions fire until you confirm or cancel.

Filter Colors

Each filter can have an optional highlight color. When a filter matches part of a line, that part is colored using the filter’s configured color. Colors are set per-filter with c in the filter manager, or via the :set-color command.

:set-color --fg red
:set-color --fg "#FF5555" --bg "#282A36"

Color values accept:

  • Named colors: black, red, green, yellow, blue, magenta, cyan, white, gray, darkgray, lightred, lightgreen, lightyellow, lightblue, lightmagenta, lightcyan
  • Hex: "#RRGGBB"

Style composition

When multiple filters overlap on the same text segment, their fg and bg attributes are composed independently — the highest-priority filter that has fg set contributes the foreground color, and the highest-priority filter that has bg set contributes the background color. So a level filter that sets --fg yellow and a text filter that sets --bg darkgray on the same word will both apply without one canceling the other.

Color priority

Filter colors take priority over automatic value colors (HTTP methods, status codes, IPs, UUIDs) and log-level colors. Value colors are applied only to spans that are not already covered by a filter — they can still appear alongside filter colors on the same line, just not on the same character span. Log-level colors are the lowest-priority fallback and apply only to text that carries no explicit color from any other source.

Filter Groups

Assign filters to a named group to manage several of them together:

:filter --group errors ERROR
:filter --group errors -r "FATAL|CRITICAL"
:exclude --group noise debug

--group/-g <name> works on :filter, :exclude, and :highlight. Toggle every filter in a group on/off together:

:toggle-group errors

A group can also have its own predefined color, used by any filter in the group that doesn’t set its own --fg/--bg:

:group errors --fg Red
:group errors --fg Red --bg Black -l
:group errors --auto        # random readable fg/bg pair
:group errors --clear       # remove the group's style
:group errors                # register the group with no style yet

Groups Sidebar

A Groups section at the bottom of the filter sidebar lists every group with its filter count and style — toggle it on/off from the UI options menu (ug). Each row shows a [x]/[ ]/[-] status matching the filter list (all enabled / all disabled / mixed). Click a row to select it, double-click to toggle every filter in that group.

Press Ctrl+g in normal mode to enter group management, scoped to the selected group:

KeyAction
j / kSelect next / previous group
Space / AToggle every filter in the group on/off
eEdit the group’s color style
xClear the group’s color style
aAdd a new group
EscExit

Save and Load Filters

Export the current filter set to a JSON file, and reload it later:

:save-filters my-filters.json
:load-filters my-filters.json

This is useful for sharing filter sets across machines or between log files with similar structure.

File Format

A filter file is a JSON object with a filters array and an optional groups array:

{
  "filters": [
    {
      "id": 0,
      "pattern": "error",
      "filter_type": "Include",
      "enabled": true,
      "color_config": { "fg": "Red", "match_only": true },
      "use_regex": false,
      "ignore_case": true,
      "group": "errors"
    },
    {
      "id": 0,
      "pattern": "debug",
      "filter_type": "Exclude",
      "enabled": true
    },
    {
      "id": 0,
      "pattern": "@field:level:WARN",
      "filter_type": "Highlight",
      "enabled": true,
      "color_config": { "bg": "#282A36", "match_only": false }
    },
    {
      "id": 0,
      "pattern": "@date:> 2024-02-21",
      "filter_type": "Include",
      "enabled": true
    }
  ],
  "groups": [
    { "name": "errors", "color_config": { "fg": "Red" } }
  ]
}

Each entry in filters:

FieldTypeRequiredNotes
idnumberyesIgnored on load — filters are re-assigned real IDs when imported. Any placeholder value (e.g. 0) works.
patternstringyesThe match text. For field or date filters, this is a special encoded string — see below.
filter_typestringyesOne of "Include", "Exclude", "Highlight" (see How Filters Work).
enabledbooleanyesWhether the filter is active.
color_configobjectnoHighlight color, omit for none — see below.
use_regexbooleannoTreat pattern as a regex. Defaults to false.
ignore_casebooleannoCase-insensitive matching. Defaults to false. Has no effect on field filters.
groupstringnoGroup name, for toggling several filters together — see Filter Groups.

color_config, when present:

FieldTypeRequiredNotes
fgstringnoForeground color.
bgstringnoBackground color.
match_onlybooleannotrue (default) highlights only the matched text; false highlights the whole line.

fg/bg in a hand-written filter file only accept ratatui’s 16 built-in color names (Black, Red, Green, Yellow, Blue, Magenta, Cyan, Gray, DarkGray, LightRed, LightGreen, LightYellow, LightBlue, LightMagenta, LightCyan, White) or "#RRGGBB" hex — not the extended names (orange, pink, purple, …) that --fg/--bg accept on the command line. Colors set via :set-color/--fg/--bg are always saved back out as one of these two forms, so a file produced by :save-filters never needs the extended names either way.

Each entry in groups:

FieldTypeRequiredNotes
namestringyesGroup name, matched against filters’ group field.
color_configobjectnoSame shape as above — the group’s fallback style.
enabledbooleannotrue (default) if the group’s toggle state should start on.

A bare array of filter objects ([{...}, {...}], with no groups) is also accepted, for files saved before group support was added.

Field and date filter patterns

A plain text/regex filter’s pattern is just the search text. A field filter (see Field Filters) instead stores @field:<key>:<value> — for example @field:level:ERROR matches :filter --field level=ERROR. A date filter (see Date & Time Filters) stores @date:<expression>, using the exact same expression syntax as :date-filter — for example @date:> 2024-02-21 or @date:09:00 .. 17:00.

Only single-condition field filters round-trip through this simple @field:key:value form. Field filters combining several --field conditions and/or trailing free text use an internal encoding not meant to be hand-written — create those in the TUI or with :filter --field ... and use :save-filters to export them instead.

Inline Filters at Startup

Add filters directly on the command line without creating a JSON file first:

FlagShortPurpose
--include <args>-iAdd include filter
--exclude <args>-oAdd exclude filter
--timestamp <args>-tAdd date/time range filter

The argument string passed to each flag accepts exactly the same options as the corresponding TUI command (:filter, :exclude, :date-filter):

# Simple pattern
logana app.log -i error -o debug

# Field-scoped filter
logana app.log -i "--field level=ERROR"

# Include filter with highlight color (flags before pattern)
logana app.log -i "--bg red error"

# Case-insensitive include filter (the outer -i is --include; the inner
# --ignore-case is the :filter flag documented in Text Filters)
logana app.log -i "--ignore-case error"

# Date range filter
logana app.log -t "> 2024-02-21"

# Combined
logana app.log -i error -o debug -t "01:00 .. 02:00"

All flags can be repeated. Inline filters are applied after any --filters file. Invalid argument strings are rejected before the TUI opens.

Preloading Filters at Startup

Pass --filters (or -f) on the command line to apply a saved filter set before the TUI opens:

logana app.log --filters my-filters.json

The filters are evaluated in a single pass during file indexing, so the filtered view is ready as soon as loading completes — no separate computation step. The same filters remain active for interactive use once the TUI is open (you can add, remove, or edit them normally).

Combined with --tail, the last matching line is shown immediately after loading:

logana app.log --filters errors.json --tail

Tip: Save your most-used filter sets with :save-filters once, then reuse them from the command line.

Sections

Text Filters

Text filters match against the raw content of each log line.

Adding Filters

From normal mode:

  • Press i to add an include filter (opens command mode pre-filled with filter )
  • Press a to add an include filter with an automatically generated, readable color pair (opens command mode pre-filled with filter --auto )
  • Press o to add an exclude filter (opens command mode pre-filled with exclude )

From the filter manager (f):

  • Press i to add an include filter, a for one with an automatically generated color pair
  • Press h to add a highlight filter (opens command mode pre-filled with highlight )

From command mode:

:filter <pattern>       # show only lines matching pattern
:exclude <pattern>      # hide lines matching pattern
:highlight <pattern>    # color matching lines without affecting visibility

The default mode. Fast multi-pattern scanning. Case-sensitive. Multi-word patterns work without quotes.

:filter ERROR
:filter connection refused
:filter 500 Internal Server Error
:filter database connection pool exhausted

Regex Filters

Opt in with --regex / -r. Supports full regex syntax. Words after -r are joined, so spaces in the pattern do not need quoting.

:filter -r (ERROR|WARN)                      # errors and warnings together
:filter -r (timeout|connection refused)      # any connectivity failure
:filter -r authentication failed.*user       # auth failures with user context
:filter -r response time: [5-9]\d{3}ms      # slow responses over 5 seconds

Flag ordering: Options (-r, -i, --fg, --bg, -l, --field, --auto) must appear before the pattern. Everything after the first pattern word is part of the pattern.

:filter --fg red -r timeout.*retry   # correct
:filter timeout.*retry --fg red      # wrong — "--fg" becomes part of the pattern

Case-Insensitive Filters

Opt in with --ignore-case / -i. Works with both text search and --regex, and combines with color/group flags the same way --regex does.

:filter --ignore-case error                  # matches "error", "ERROR", "Error", ...
:filter -i -r (error|warn)                   # case-insensitive regex
:exclude --ignore-case debug

--ignore-case has no effect on --field key=value filters (matching a parsed field is always case-sensitive), same as --regex. A case-insensitive filter shows an [i] tag in the filter sidebar.

Highlight Filters

A third filter kind alongside include/exclude. Highlight filters apply their color styling to matching lines but never hide or reveal anything — every line stays exactly as visible as it would be without the filter.

:highlight <pattern>                    # color matches, alias :h
:highlight -r (ERROR|WARN)              # regex highlight
:highlight --fg yellow ERROR            # with a color, same flags as :filter

Highlight filters accept the same flags as :filter (--regex/-r, --ignore-case/-i, --fg, --bg, -l, --field, --group, --auto/-a) and show up in the filter sidebar with an H type tag, e.g. [x] H: ERROR (12). They’re for marking the lines you care about while reading the log in full — mark an event, then keep scrolling to see everything around it, without an include/exclude filter narrowing the view down to just the matches.

To put your existing include/exclude filters into the same visible-but-marked state temporarily — for example when you need to see the full context around what they’re currently hiding — see Highlight Mode.

Multiple Filters

You can add as many filters as you like. They combine as follows:

  1. Include filters — a line must match at least one enabled include filter to be shown (if any exist).
  2. Exclude filters — a line matching any enabled exclude filter is hidden.
  3. Highlight filters — never affect which lines are shown, only their styling.

Exclude takes priority: a line that satisfies an include filter but also matches an exclude filter is hidden.

Toggling Filters

  • In the filter manager (f), press Space to enable/disable individual filters.
  • Press F in normal mode to toggle all filtering on/off instantly (useful for comparing filtered vs. unfiltered view).
  • Press A in the filter manager to enable/disable all filters at once.

Filter Groups

Assign a filter to a named group with --group <name> when adding it:

:filter --group errors ERROR
:filter --group errors FATAL
:exclude --group noise health.?check --regex

Toggle every filter in a group on/off together:

:toggle-group errors

If any filter in the group is enabled, this disables the whole group; otherwise it enables the whole group. Group names autocomplete from existing filters.

Grouped filters show their group name in brackets in the filter sidebar, e.g. [x] In: [errors] ERROR (12).

A group can also have its own predefined color, used by any filter in the group that doesn’t set its own --fg/--bg/-l:

:group errors --fg Red --bg Black
:group errors --auto
:group errors --clear

--auto generates a random readable color pair, same as :filter --auto. --clear removes the group’s style. A filter’s own color always takes priority over its group’s — only filters with no color of their own fall back to it. Groups can be styled before any filter uses them.

A style is optional — :group <name> with no flags registers the group with no predefined color, useful when you just want it to show up (and be manageable) in the sidebar’s Groups section ahead of assigning any filters to it:

:group errors

The [groupname] tag in the sidebar is also colored with the group’s style when it has one, regardless of whether the filter itself has its own color.

Groups Section

Every known group also gets its own row in a Groups section at the bottom of the sidebar, below the filter list, under a Groups [n] label. Each row shows the group’s name and how many filters belong to it, e.g. errors (2). A group with a predefined style renders in that color.

Click a group row, or press Ctrl+g in normal mode, to enter group management:

KeyAction
j / kMove to the next/previous group
AToggle every filter in the group on/off together (same as :toggle-group)
eEdit the group’s color, prefilling :group <name> --fg ...
xClear the group’s predefined style
aAdd a new group, opening :group for you to type a name
EscExit back to normal mode

With no groups yet, Ctrl+g still enters group management so a can create the first one.

Press g in :ui mode to toggle the Groups section on/off.

Highlight Colors

Each include filter highlights its matching byte spans in the log line. The color is configurable per filter. When no color is set, logana uses a default highlight style from the active theme.

To set a color for the currently selected filter in the filter manager, press c, then use :set-color:

:set-color --fg yellow
:set-color --fg "#FF5555" --bg "#44475A"

By default, only the matched portion of the line is colored. To highlight the entire line instead, pass -l when adding the filter:

:filter -l ERROR            # highlight the full line for every ERROR match
:filter --fg red -l ERROR   # full-line red highlight

The -l flag can also be applied later with :set-color -l from the filter manager.

Pass --auto/-a instead of --fg/--bg to generate a random color pair with guaranteed readable contrast, rather than picking one yourself:

:filter --auto ERROR

--auto cannot be combined with --fg/--bg.

When multiple filters overlap on the same span, their fg and bg are composed: one filter can contribute the foreground color while another contributes the background. Automatic value colors (HTTP methods, status codes, IPs, UUIDs) apply only to spans not already colored by a filter, and log-level colors are the lowest-priority fallback.

Editing Filters

Editing a filter’s pattern or color from the filter manager (e to edit pattern, c to change color) updates it in-place. The filter keeps its current position in the list — order is never changed by an edit.

Date & Time Filters

Date filters narrow the visible lines by their parsed timestamp. They work as a post-processing step after text filters — only lines already passing text filters are checked against date filters.

Adding a Date Filter

From the filter manager (ft): opens command mode pre-filled with date-filter .

From command mode:

:date-filter <expression>

Expression Syntax

Equals (no operator)

Omitting an operator matches the full period implied by the input’s granularity.

InputMatches
09:00the whole minute 09:00:00 – 09:00:59
09:00:30the exact second 09:00:30
Feb 21all of Feb 21 (00:00:00 – 23:59:59)
Feb/21same — / is accepted as month/day separator
02/21same — numeric month/day
02-21same — numeric month-day
02/21/2024all of Feb 21 2024
02-21-2024same with dash separators
2024-02-21all of Feb 21 2024
2024-02-21 10:15the whole minute 10:15:00 – 10:15:59
2024-02-21 10:15:30the exact second
:date-filter Feb/21
:date-filter 02/21
:date-filter 02-21
:date-filter 09:00

Range (..)

Both bounds are inclusive. Spaces around .. are optional.

The upper bound is expanded to the end of its granularity period: a day-level upper bound covers up to 23:59:59.999999, a minute-level upper bound covers up to :59.999999, and a second-level upper bound is exact.

# time-only (compares seconds since midnight)
:date-filter 09:00 .. 17:00       # 09:00:00 – 17:00:59
:date-filter 09:00..17:00         # same, no spaces required
:date-filter 09:00:00 .. 17:00:00 # exact seconds

# BSD month names
:date-filter Feb 21 .. Feb 22     # Feb 21 00:00:00 – Feb 22 23:59:59
:date-filter Feb/21 .. Feb/22

# numeric month/day
:date-filter 02/21 .. 02/22       # Feb 21 00:00:00 – Feb 22 23:59:59
:date-filter 02-21 .. 02-22
:date-filter 03-21..03-25         # no spaces

# ISO dates
:date-filter 2024-02-21 .. 2024-02-22

# full datetimes (second-exact bounds, no expansion)
:date-filter 2024-02-21T10:00:00 .. 2024-02-21T11:30:00
:date-filter 2024-02-21 10:00:00 .. 2024-02-21 11:30:00

Comparison operators

:date-filter > 2024-02-21T10:00:00    # after
:date-filter >= Feb 21 10:00:00       # from (inclusive)
:date-filter < 02/22                  # before Feb 22
:date-filter <= Feb 22                # up to and including

Supported operators: >, >=, <, <=


Accepted Date/Time Formats

Date bounds

FormatExampleYear
BSD month name + dayFeb 21, Feb/21none (month/day only)
Numeric MM/DD02/21none
Numeric MM-DD02-21none
Numeric MM/DD/YYYY02/21/2024included
Numeric MM-DD-YYYY02-21-2024included
ISO date2024-02-21included

Time bounds

FormatExampleGranularity
HH:MM09:00minute
HH:MM:SS09:00:30second

Combined datetime bounds

Any date format above followed by a space and a time:

Feb/21 09:00
02/21 09:00:30
02-21-2024 10:15
2024-02-21T10:15:30
2024-02-21 10:15:30

ISO 8601 T separator and a plain space are both accepted.


Rules and Limitations

  • Inclusive bounds: .. ranges include both endpoints (>= lower AND <= upper).
  • No midnight wraparound: 23:00 .. 01:00 is invalid. Use two comparison filters instead.
  • Mixed-mode ranges are rejected: both sides of a .. must use the same format (both time-only or both date).
  • Multiple date filters are OR-ed: a line passes if it satisfies any enabled date filter.
  • Lines without a timestamp pass through: continuation lines, stack traces, and multi-line messages are never hidden by date filters.
  • Requires a detected format parser: if logana cannot detect the log format, date filters return an error. This means plain-text logs without timestamps cannot be date-filtered.

Display

Date filters appear in the filter manager and sidebar as Date: <expression>, not as raw @date: patterns.


How It Works

Date filters are stored as regular FilterDef entries in the database with an @date: prefix in the pattern field (e.g. @date:01:00:00 .. 02:00:00). They are excluded from the text-filter pipeline and applied separately in refresh_visible() after text filters run, via retain() on visible_indices.

Timestamps are normalized to a canonical YYYY-MM-DD HH:MM:SS.ffffff string before comparison, so all supported log format timestamps (ISO 8601, BSD, logback datetime, CLF, journalctl, Apache error, etc.) are comparable regardless of their original format.

Field Filters

Field filters let you narrow the log view by the value of a specific parsed field rather than matching against the raw line text. This is useful when you want to, for example, show only error-level lines without accidentally matching the word “error” in a message body.

Syntax

:filter --field <key>=<value>
:exclude --field <key>=<value>

The --field flag tells logana to treat the pattern as a key=value pair. The value is matched as a substring of the named field.

:filter --field level=error         # show only lines where level contains "error"
:filter --field component=auth      # show only lines from the auth component
:exclude --field level=debug        # hide all debug-level lines

--field can be repeated within a single command to require several fields at once, and combined with trailing free text that must also match — all AND’d together in one filter:

:filter --field level=INFO --field component=Draco Power measurements:
# shows only lines where level contains "INFO" AND component contains "Draco"
# AND the line contains "Power measurements:"

Field Name Aliases

The following short aliases are recognised regardless of how the field is named in the raw log:

Alias(es)Field
level, lvllog level
timestamp, ts, timetimestamp
targetlogger / target name
message, msglog message body
anything elselooked up by exact key in extra fields

For example, :filter --field lvl=warn and :filter --field level=warn are equivalent.

Combining Field Filters

There are two distinct ways to combine field conditions, with different logic:

Multiple --field flags in one command — AND logic. Every condition (and any trailing text) must match:

:filter --field level=error --field component=auth
# only lines where level contains "error" AND component contains "auth"

Multiple separate :filter commands — OR logic, same as any other include filters. Each broadens what’s visible:

:filter --field level=error
:filter --field level=warn
# shows lines where level contains "error" OR level contains "warn"

Exclude field filters — hide any line where the field matches:

:exclude --field level=debug

Mixed include and exclude — exclude takes priority. A line that satisfies an include filter but also matches an exclude filter is hidden.

Pass-Through Behaviour

Lines that cannot be parsed (e.g. plain-text lines in an otherwise structured file) are always shown — they are not hidden by field filters. The same applies when the named field is absent from an otherwise parseable line.

This matches the behaviour of date filters for lines without timestamps.

Field filters appear in the filter manager sidebar with a [field] tag. A filter with multiple --field conditions and/or trailing text shows all of them, comma-separated:

[x] In: level=error [field]
[x] Out: level=debug [field]
[x] In: level=INFO, component=Draco, Power measurements: [field]

Group-Scoped Fields

A custom schema can declare a repeating group of sub-records (e.g. a batch job’s workers). Filter on a field inside any item of the group with <group>.<field>=<value> — it matches if any item in the group has that field:

:filter --field workers.hostname=worker-3
# shows records where any worker's hostname contains "worker-3"

This is “any item matches” semantics, independent from plain field lookup — an indexed path like workers.0.hostname (as shown in the structured fields columns) is display-only and can’t be used as a filter path.

Requires a Detected Format

Field filters only have an effect when logana has detected a structured log format (JSON, logfmt, syslog, etc.). On plain-text files with no detected format, all lines pass through field filters unchanged.

See Log Formats for the list of supported formats.

Search

Search operates on visible lines only — it respects active filters and only scans lines that are currently shown.

Keybindings

KeyAction
/Search forward
?Search backward
nJump to next match
NJump to previous match

Usage

Press / or ? to open the search bar at the bottom of the screen. Type your query and press Enter. logana highlights all matches on visible lines and scrolls to the first match.

  • n wraps around to the first match after the last line.
  • N wraps around to the last match before the first line.

Pattern Syntax

Search uses full regex syntax. Examples:

/NullPointerException              plain text
/user 42 .* failed                 regex — activity for a specific user
/POST /api/orders.* 5\d\d          regex — failed order requests
/^2024-06-15T14                    lines from a specific hour

Case Sensitivity

By default, search is case-sensitive. Case sensitivity can be toggled programmatically via the Search API (no UI toggle yet — the default behavior is case-sensitive matching).

Match Highlighting

Matched byte spans are highlighted with the search style (distinct from filter highlight colors). Search highlights take priority over filter highlights — if a search match overlaps a filter-colored span, the search color wins.

The current match (the one n/N is positioned on) is rendered with a distinct highlight color to distinguish it from other occurrences on screen.

When wrap is disabled, navigating to a match with n or N also adjusts the horizontal scroll to center the matched span in the viewport.

Search vs. Filters

SearchFilter
PersistedNoYes
Affects visible linesNoYes
HighlightedYesYes
Navigation (n/N)YesNo
Regex supportYesYes

Use filters to permanently narrow the view. Use search to navigate through specific patterns within the already-filtered view.

Structured Fields

When logana detects a structured log format (JSON, logfmt, syslog, tracing-subscriber, etc.), it parses each line into named columns: timestamp, level, target, span, and message, plus any extra fields specific to the format.

Columns

ColumnDescription
timestampParsed log timestamp
levelNormalized log level (TRACE, DEBUG, INFO, WARN, ERROR, FATAL)
targetLogger name, module path, or source identifier
spanTracing span context (name + fields), if present
messageThe log message body
extra fieldsFormat-specific extras (e.g. pid, thread, hostname, request_id)

Showing and Hiding Columns

Use :select-fields to open an interactive column picker:

  • j / k — navigate
  • Space — toggle column on/off
  • J / K — reorder columns
  • a — enable all
  • n — disable all
  • r — reset to the default order with everything visible (clears any reorder and any hidden fields — still requires Enter to apply, like a/n)
  • Enter — apply
  • Esc — cancel

Or use commands directly:

:hide-field span                      # hide a single column
:show-field span                      # show a previously hidden column
:show-all-fields                      # reset to default display — clears both hidden fields and any custom column order

Use :select-fields for reordering columns or changing several at once.

Field Key Display

Extra fields and span fields carry both a key and a value. By default logana shows only the values to keep lines compact. Use :show-keys to include the key names:

:show-keys     # request_id=abc123  status=200  request: method=GET uri=/api/users
:hide-keys     # abc123  200  request: GET /api/users  (default)

This applies to all structured formats — JSON extra fields, logfmt pairs, syslog structured data, span fields, and any other key-value extras that don’t map to a canonical column (timestamp, level, target, message). This setting is persisted per file in the session database.

Span Fields

Span context is parsed from formats that carry it (tracing-subscriber JSON, tracing-subscriber fmt text, and others). The span column shows the span name followed by its fields:

request: GET /api/users        # hide-keys (default)
request: method=GET uri=/api/users   # show-keys

Span sub-fields can also be selected as individual columns:

:fields timestamp level span.method span.uri message

Repeating Groups

A custom schema can declare a repeating group of sub-records inside a multiline entry (e.g. a batch job’s workers). Each item shows as its own indexed column, e.g. workers.0.hostname, workers.1.hostname. Filter across every item in a group at once with a group-scoped field filter — see Field Filters.

Value Coloring

Even within structured columns, known value patterns are colored automatically:

  • HTTP methods — GET (green), POST (yellow), PUT (blue), DELETE (red), PATCH (magenta)
  • HTTP status codes — 2xx (green), 3xx (cyan), 4xx (yellow), 5xx (red)
  • IP addresses — IPv4 and IPv6
  • UUIDs

Configure which categories are colored via :value-colors.

Tab Completion for Field Names

The :hide-field / :show-field commands complete against the field names discovered from the first 200 visible log lines, so you don’t need to remember exact field names.

Annotations & Export

Annotations let you attach multiline comments to log lines and export an analysis report. This is useful for incident investigations, code reviews, and sharing findings with your team.

Visual Selection

Use Visual Line Mode (V) to select whole lines, or Visual Character Mode (v) to select text within a line. From either mode you can attach a comment, mark lines, copy to clipboard, or build a filter.

Adding a Comment

With lines selected in visual mode, press c to open the comment editor:

  • Type your multiline comment
  • Enter — insert new line
  • Backspace — delete character / merge lines
  • Left / Right — move cursor (wraps between lines)
  • Up / Down — move between rows
  • Ctrl+s — save the comment
  • Esc — cancel without saving

After saving, annotated lines show a marker in the gutter.

Editing and Deleting Comments

In normal mode, move to an annotated line and:

KeyAction
rOpen the comment editor pre-filled with the existing text
dDelete the comment on the current line

Inside the editor, Ctrl+D also deletes the comment.

In normal mode, c opens the comment editor for the current line directly (without entering visual mode first).

Press C in normal mode to clear all marks and comments for the current tab.

Marks

Press m to mark the current line. Marked lines are included in exports even without a comment attached. Press M to toggle a marks-only view.

Exporting

Export all annotations and marked lines to a file:

:export report.md                    # Markdown (default)
:export report.md -t jira            # Jira wiki markup
:export report.md -t <template>      # custom template

The export includes:

  • A header with the filename and export date
  • Each comment group with the commented log lines and the comment text
  • Any standalone marked lines (without a comment) grouped consecutively

Export Window

When the selected template’s footer section contains any {{placeholder}} variables, :export opens a window before writing the file so you can fill in those sections interactively. Both bundled templates (markdown and jira) include {{conclusion}} and {{next_steps}} by default, and any custom placeholder name works the same way.

KeyAction
Tab / Shift+TabSwitch between Conclusion and Next Steps
EnterInsert a new line
BackspaceDelete character before cursor / merge lines
DeleteDelete character at cursor / merge next line
Left / RightMove cursor (wraps between lines)
Up / DownMove between rows
Ctrl+SWrite the file
EscCancel without writing

The active field scrolls to keep the cursor visible when content exceeds the window height.

Export Templates

Two templates are bundled: markdown and jira. Custom templates can be placed in ~/.config/logana/templates/.

Template syntax:

{{#header}}
# Analysis: {{filename}}
Date: {{date}}
{{/header}}

{{#comment_group}}

{{lines}}


{{commentary}}
{{/comment_group}}

Available placeholders:

PlaceholderContent
{{filename}}Source file name
{{date}}Export date
{{lines}}The annotated log lines, each prefixed with its 1-based line number
{{commentary}}The comment text
{{conclusion}}Conclusion text (footer)
{{next_steps}}Next steps text (footer)

Any {{custom_name}} placeholder you add to the footer section becomes an editable field in the export window. Use underscores for multi-word names ({{root_cause}} → “Root Cause”).

Template sections: header (rendered once), comment_group (rendered per annotation/mark group), footer (optional, rendered once at the end).

User templates in ~/.config/logana/templates/ shadow bundled ones by name. Tab completion lists all available templates.

Docker Logs

logana can stream logs from any running Docker container directly in the terminal, with the same filtering, search, and annotation features available for file-based logs.

Opening a Container Stream

From normal mode, type:

:docker

A picker lists all running containers. Navigate with j / k and press Enter to attach. The stream opens in a new tab.

KeyAction
j / kNavigate container list
EnterAttach to selected container
EscCancel

Auto-Reconnect

If the connection to a Docker container fails or drops, logana retries automatically with increasing backoff. The tab name shows [RETRY #N] while reconnecting.

Session Persistence

Docker tabs are persisted across sessions. When you reopen logana, it automatically re-attaches to any Docker containers that were open in the previous session, by container name. The source identifier stored in the session database is docker:<container-name>.

Tail Mode

Docker tabs benefit from tail mode — when enabled, the view auto-scrolls to show new log entries as they arrive:

:tail     # toggle tail mode on/off

When tail mode is active, [TAIL] appears in the log panel title.

Filtering and Annotations

All filter, search, and annotation features work identically for Docker streams. Filters are persisted per container name, just like file-based logs.

Piping Docker Compose Logs

You can also pipe docker compose logs directly into logana:

docker compose logs -f 2>&1 | logana

The 2>&1 redirect is important — without it, Docker’s warnings (e.g. unset variable notices) go straight to the terminal and corrupt the TUI display. Merging stderr into stdout ensures everything flows through the pipe and appears as log entries inside logana, where you can filter them as needed.

To suppress the warnings entirely instead:

docker compose logs -f 2>/dev/null | logana

Requirements

  • Docker must be installed and accessible via docker in PATH.
  • The docker ps command must return running containers.
  • Logs are streamed via docker logs -f <container-id>, with stdout and stderr merged.

DLT Streaming

logana can connect to a running DLT daemon over TCP and stream log messages in real time, with the same filtering, search, and annotation features available for file-based logs.

Opening a DLT Stream

From normal mode, type:

:dlt

A picker lists configured DLT devices. Navigate with j / k and press Enter to connect. The stream opens in a new tab.

KeyAction
j / kNavigate device list
EnterConnect to selected device
aAdd a new device inline
EscCancel

Configuring Devices

DLT devices can be configured in ~/.config/logana/config.json:

{
  "dlt_devices": [
    { "name": "local", "host": "127.0.0.1", "port": 3490 },
    { "name": "target-ecu", "host": "192.168.1.100", "port": 3490 }
  ]
}

The default port is 3490. Devices can also be added from the selection panel by pressing a.

Opening DLT Binary Files

DLT binary files (.dlt) are opened like any other log file:

logana trace.dlt

Three binary layouts are detected automatically: storage format (with DLT\x01 magic), wire format (concatenated messages without storage headers), and simplified format.

Auto-Reconnect

If the connection to the DLT daemon fails or drops, logana retries automatically with increasing backoff (0s, 2s, 5s, 10s). The tab name shows [RETRY #N] while reconnecting, and the error details appear in the status bar. Once the connection is re-established, streaming resumes normally.

Session-restored DLT tabs also reconnect automatically without blocking the UI.

Session Persistence

DLT tabs are persisted across sessions. When you reopen logana, it reconnects to any DLT daemons that were open in the previous session. The source identifier stored in the session database is dlt://host:port.

Tail Mode

DLT streams benefit from tail mode — when enabled, the view auto-scrolls to show new log entries as they arrive:

:tail

Fields

DLT messages expose the following fields for filtering and display:

FieldDescription
timestampWall-clock time (streaming) or relative time (file)
hw_tsHardware timestamp counter
mcntMessage counter (0-255)
ecuECU identifier
apidApplication ID (shown as target)
ctidContext ID
typeMessage type (log, trace, network, control)
subtypeSub-type (fatal, error, warn, info, debug, verbose)
modeVerbose or non-verbose

OTel Collector

logana can receive OpenTelemetry logs in real time over gRPC or HTTP/JSON, turning it into a live OTel log viewer with the same filtering, search, and annotation features available for file-based logs.

Starting a Receiver

From normal mode, type:

:otel              # gRPC on port 4317 (default — matches OTel SDK defaults)
:otel --http       # HTTP/JSON on port 4318
:otel 4317         # gRPC on a custom port
:otel --http 4318  # HTTP/JSON on a custom port

The receiver opens in a new tab and listens for incoming log export requests. Logs appear as they arrive.

Transport Modes

ModeCommandDefault PortProtocol
gRPC:otel4317OTLP/gRPC (protobuf)
HTTP/JSON:otel --http4318OTLP/HTTP (JSON or protobuf)

gRPC (default)

The gRPC receiver accepts ExportLogsServiceRequest messages on port 4317. This matches the default export protocol used by most OTel SDKs.

The server runs in plaintext mode (no TLS). Configure your SDK to use an insecure connection:

# Environment variable (works for all OTel SDKs)
OTEL_EXPORTER_OTLP_INSECURE=true

# Or use the http:// scheme in the endpoint URL
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

HTTP/JSON

The HTTP receiver accepts POST /v1/logs with application/json or application/x-protobuf content types, and handles gzip-compressed request bodies.

Auto-Reconnect

If the receiver encounters an error on startup (e.g. port already in use), logana reports the error in the tab. Fix the conflict and reopen with :otel again.

Session Persistence

OTel collector tabs are persisted across sessions. When you reopen logana, it automatically restarts the receiver on the same port. The source identifier stored in the session database is otlp-grpc://<port> (gRPC) or otlp://<port> (HTTP).

Parsed Fields

Logs received over OTLP are parsed with the same OTel parser used for file-based OTLP logs:

FieldSource
TimestamptimeUnixNano
LevelseverityNumber / severityText
Messagebody.stringValue
Targetservice.name, code.namespace, logger (from resource or log attributes)
Extra fieldsAll other resource attributes and log attributes

Multi-Tab

logana supports multiple tabs, each showing an independent log file, directory, stdin stream, or Docker container.

Tab Keybindings

KeyAction
TabSwitch to next tab
Shift+TabSwitch to previous tab
Ctrl+tOpen a new (empty) tab
Ctrl+wClose the current tab
Ctrl+pOpen a searchable popup to switch between open files

A tab can also be clicked directly in the tab bar to switch to it.

Opening Files in Tabs

From the command line, a file argument opens in its own tab; a directory argument shows a picker to choose which files to open (not yet supported for multiple positional args):

logana /var/log/         # shows a picker — pick which files to open, each in its own tab

From within logana, use the :open command:

:open app.log            # opens in the current tab
:open /var/log/          # shows the same picker (directory)

Tab State

Each tab maintains completely independent state:

  • Scroll position and viewport
  • Active filters (with their colors and enabled/disabled states)
  • Search query
  • Marks and annotations
  • Detected log format
  • Field layout (visible columns and order)
  • Display flags (wrap, sidebar, tail mode, show-keys)

Session Restore

When you close logana and reopen it without arguments, it prompts to restore the previous session — reopening all tabs that were open at exit, with their per-tab state restored. Docker tabs are re-attached by container name.

Merged View

:merge opens a source-selection popup where you can choose any combination of open tabs. Confirming creates a new merged(N) tab that interleaves all selected sources sorted by timestamp — no data is copied.

:merge    # open source-selection popup

In the merged tab each line is prefixed with the title of the tab it came from.

The merged tab stays live: as the source tabs receive new lines, the merged index is extended and re-sorted automatically. You can pause or stop updates with the usual commands:

:pause   # pause live updates for the merged tab
:resume  # resume live updates
:stop    # stop live updates permanently for the merged tab

Filters, search, marks, and annotations all work the same as on any other tab.

Files inside a .zip/.tar.gz/etc archive can be merged directly without opening them as separate tabs first — mark them with m in the archive picker instead of Space. See Opening Compressed and Archive Files. Unlike this live merged(N) tab, an archive-picker merge is a one-shot snapshot of the extracted files (there’s no live source tab to poll for new lines).

Tail Mode Per Tab

Each tab can independently have tail mode enabled or disabled:

:tail    # toggle tail mode for the current tab

When tail is active for a tab, [TAIL] appears in that tab’s log panel title.

MCP Server

logana includes an embedded Model Context Protocol (MCP) server. When enabled, it exposes marked lines and annotations as MCP resources and provides tools so AI assistants can interact with your log analysis session in real time.

Starting the Server

On launch

logana app.log --mcp           # default port 9876
logana app.log --mcp 8080      # custom port

From inside the TUI

:enable-mcp                    # default port 9876
:enable-mcp --port 8080        # custom port
:disable-mcp                   # stop the server

The server listens at http://localhost:<port>/mcp using the Streamable HTTP transport.

Default Port in Config

Set a persistent default port in ~/.config/logana/config.json:

{
  "mcp_port": 9876
}

When both the config and a --port flag are present, the config value takes precedence.

Resources

URIDescription
logana://marksAll marked lines — one entry per line formatted as <line_number>: <text>
logana://annotationsAll annotations — each block shows the 1-based line numbers and the comment text

Resources are updated every render frame so the MCP client always sees the current state of the active tab.

Tools

ToolParametersDescription
toggle_markline_index (1-based)Mark or unmark a log line
add_annotationtext, line_indices (1-based list)Attach a comment to one or more lines
remove_annotationindex (0-based)Remove an annotation by its position in the list

Tool calls are applied to the active tab and immediately reflected in the TUI.

Connecting an AI Assistant

Point your MCP client at the server endpoint. For example, to use it with Claude Desktop, add an entry to your claude_desktop_config.json:

{
  "mcpServers": {
    "logana": {
      "url": "http://localhost:9876/mcp"
    }
  }
}

Once connected, the assistant can read your marked lines and annotations and call tools to mark or annotate lines on your behalf.

Commands

CLI Flags

These flags are passed when launching logana from the shell:

FlagDescription
<file>File or directory to open. Omit to read from stdin.
-f, --filters <path>Preload a saved filter set (JSON). Filters are applied in a single pass during indexing and remain active for interactive use.
-i, --include <args>Add an include filter. Accepts the same arguments as :filter. May be repeated. Examples: -i "error", -i "--field level=ERROR"
-o, --exclude <args>Add an exclude filter. Accepts the same arguments as :exclude. May be repeated. Examples: -o "debug", -o "--field level=debug"
-t, --timestamp <args>Add a date/time range filter. Accepts the same arguments as :date-filter. May be repeated.
--tailStart at the end of the file and enable tail mode. Combined with --filters, the last matching line is available immediately after loading.
--mcp [PORT]Start the embedded MCP server on launch. Port defaults to 9876. See MCP Server.
--headlessRun without TUI — apply filters and write matching lines to stdout or --output.
--output <path>Write headless output to a file instead of stdout. Requires --headless.

In-App Commands

Press : in normal mode to open command mode. Tab completes commands, flags, colors, themes, and file paths. Command history is navigable with Up / Down.

Filtering

CommandDescription
:filter [--regex|-r] [--ignore-case|-i] [-l] [--fg COLOR] [--bg COLOR] <pattern>Add an include filter (show only matching lines)
:filter --field <key>=<value>Add a field-scoped include filter (e.g. level=error); repeat to require several fields at once
:filter --group|-g <name> <pattern>Assign the filter to a named group, toggleable together via :toggle-group
:filter --auto|-a <pattern>Add an include filter with a randomly generated, readable fg/bg color pair instead of specifying --fg/--bg
:exclude [--regex|-r] [--ignore-case|-i] <pattern>Add an exclude filter (hide matching lines)
:exclude --field <key>=<value>Add a field-scoped exclude filter (e.g. level=debug)
:exclude --group|-g <name> <pattern>Assign the exclude filter to a named group
:highlight [--regex|-r] [--ignore-case|-i] [-l] [--fg COLOR] [--bg COLOR] <pattern> (alias :h)Add a highlight filter — colors matches without affecting visibility
:highlight --auto|-a <pattern>Add a highlight filter with a randomly generated, readable fg/bg color pair
:date-filter <expr>Add a date/time range filter
:set-color [--fg COLOR] [--bg COLOR]Set highlight color for the selected filter
:toggle-group <name>Toggle every filter in a named group on/off together
:group <name> [--fg COLOR] [--bg COLOR] [-l] [--auto] [--clear]Set, update, or clear a group’s predefined color style, used by filters in the group with no color of their own
:filteringToggle all filtering on/off (bypass every filter)
:clear-filtersRemove all filter definitions
:disable-filtersDisable all filters without removing them
:enable-filtersEnable all disabled filters
:save-filters <file>Save current filters to a JSON file
:load-filters <file>Load filters from a JSON file
:import-filters <file> [-a|--append]Import a Notepad++ Analyze-plugin or User-Defined-Language XML config as Include filters. Replaces current filters by default; --append merges instead

Flag ordering: All options (--regex, --fg, --bg, -l, --field, --group/-g, --ignore-case/-i, --auto) must appear before the pattern. Everything after the first pattern word is treated as part of the pattern text. --auto cannot be combined with --fg/--bg.

See Filter Groups for the Groups sidebar and group management mode (Ctrl+g).

See Filtering, Date & Time Filters, and Field Filters for full details.

CommandDescription
:<N>Jump to line N (e.g. :500)

Files and Tabs

CommandDescription
:open <path>Open a file, directory, or compressed/archive file. Directories and archives both show the same contents picker first — see Quick Start
:close-tabClose the current tab (quits if it’s the last tab)
:save <path>Save the currently visible (filtered) lines to a file in raw format
:export-marked <path>Export marked lines to a file
:run <program> [args...]Execute a command and stream its output to a new tab; stderr lines show as errors
:schema [name]Show the active schema, or switch this tab to a named custom or built-in schema. Use :schema none to treat the file as plain text
:default-filters [format] [path]Configure a filter file to auto-load whenever a format is assigned to a tab with no filters yet. No args opens a popup listing every format

Display

CommandDescription
:wrapToggle line wrap on/off (persisted across sessions)
:line-numbersToggle the line number gutter on/off (persisted across sessions)
:relative-line-numbersToggle relative line numbers — other rows show their distance from the selected row (persisted across sessions)
:tailToggle tail mode (auto-scroll on new content)
:rawToggle raw mode — bypass the format parser and show unformatted log lines; title shows [RAW] when active
:collapseHide continuation lines file-wide, showing only each entry’s first line
:expandReveal continuation lines previously hidden by :collapse
:level-colorsOpen the level colors dialog — toggle coloring per level (TRACE, DEBUG, INFO, NOTICE, WARNING, ERROR, FATAL); INFO/TRACE/DEBUG/NOTICE are off by default
:value-colorsOpen the value colors dialog — toggle coloring for HTTP methods, status codes, IPs, UUIDs, and process/logger names
:set-theme <name>Switch the color theme (persisted across sessions)
:themeOpen a searchable picker to browse themes with live preview; Enter applies and persists, Esc restores the previous theme
:sidebar-position left|rightMove the filter sidebar to the left or right of the log panel (persisted across sessions)

OTel Collector

CommandDescription
:otel [port]Open an OTLP gRPC receiver tab (default port 4317)
:otel --http [port]Open an OTLP HTTP/JSON receiver tab (default port 4318)

See OTel Collector for full details.

MCP Server

CommandDescription
:enable-mcp [--port N]Start the embedded MCP server (default port 9876)
:disable-mcpStop the MCP server

See MCP Server for full details.

Live Data

These commands control how the current tab handles incoming data from a file watcher or stream (stdin, Docker).

CommandDescription
:stopPermanently stop all incoming data for the current tab — drops the file watcher and/or stream
:pauseFreeze the view; the background watcher/stream keeps running. Title shows [PAUSED]
:resumeResume applying incoming data; the latest snapshot is applied immediately

Note: :pause / :resume are non-destructive — no data is lost while paused. :stop is permanent; to resume watching a file after stopping, reopen it with :open.

Structured Fields

CommandDescription
:hide-field <col>Hide a single column
:show-field <col>Show a previously hidden column
:show-all-fieldsReset to default column display
:select-fieldsOpen an interactive column picker
:show-keysShow field keys alongside values (e.g. method=GET)
:hide-keysShow only values, hiding field keys (default)

Merged View

CommandDescription
:mergeOpen a source-selection popup, then create a new tab interleaving the selected tabs sorted by timestamp

See Multi-Tab for full details.

Export and Streaming

CommandDescription
:export <file> [-t <template>]Export annotations to a file (default template: markdown)
:dockerPick and stream a running Docker container
:dltPick and stream from a DLT daemon over TCP

Session

CommandDescription
:resetRestore all settings to defaults and clear all persisted state

Tab Completion

Command mode supports multi-tier tab completion:

  1. Color names — after --fg or --bg flags
  2. Template names — after -t / --template flags in :export
  3. File paths — for :open, :save-filters, :load-filters, :import-filters, :export
  4. Theme names — for :set-theme
  5. Command names — for everything else

Press Tab / Shift+Tab to cycle through completions. A highlighted suggestion appears in the hint area; Space accepts it.

Configuration

logana is configured via a config.json file. The file is entirely optional — all settings have sensible defaults and logana starts normally even if the file is missing. If the file exists but cannot be read or contains invalid JSON or unknown keys, a warning is shown in the notification area on startup.

How the Config File Works

logana never writes to the config file. Any settings defined there are applied on startup and take precedence over the values stored in the database.

Many settings can also be changed at runtime — UI toggles via the UI options menu (u) and display commands (:wrap, :line-numbers, :relative-line-numbers, :set-theme, :sidebar-position). When changed at runtime the new value is saved to the database and restored on the next session, unless the setting is also defined in the config file, in which case the config file value always wins.

Schema Validation

logana publishes a JSON Schema for config.json. Add a $schema line to enable validation and autocomplete in VS Code and other JSON-aware editors:

{
  "$schema": "https://raw.githubusercontent.com/pauloremoli/logana/main/schema/config.schema.json",
  "theme": "dracula"
}

VS Code will highlight unknown fields, suggest valid values for enums such as restore_session, and show inline documentation for each option.

Config File Location

The path depends on the operating system:

OSPath
Linux~/.config/logana/config.json
macOS~/Library/Application Support/logana/config.json
Windows%APPDATA%\logana\config.json

Full Example

{
  "theme": "dracula",
  "show_mode_bar": true,
  "show_borders": true,
  "show_sidebar": true,
  "show_line_numbers": true,
  "relative_line_numbers": false,
  "wrap": false,
  "sidebar_side": "right",
  "preview_bytes": 16777216,
  "restore_session": "always",
  "restore_file_context": "always",
  "mcp_port": 9876,
  "dlt_devices": [
    { "name": "my-ecu", "host": "192.168.1.100", "port": 3490 }
  ],
  "keybindings": {
    "navigation": {
      "scroll_down": ["j", "Down"],
      "scroll_up": ["k", "Up"],
      "half_page_down": "Ctrl+d",
      "half_page_up": "Ctrl+u",
      "page_down": "PageDown",
      "page_up": "PageUp"
    },
    "normal": {
      "filter_include": "i",
      "filter_exclude": "o",
      "filter_mode": "f",
      "toggle_filtering": "F",
      "mark_line": "m",
      "toggle_marks_only": "M",
      "visual_mode": "V",
      "enter_ui_mode": "u",
      "show_keybindings": "F1",
      "scroll_left": "h",
      "scroll_right": "l"
    },
    "global": {
      "quit": "q"
    }
  }
}

Top-level Options

KeyTypeDefaultDescription
themestring"github-dark"Active color theme name (without .json extension)
show_mode_barbooltrueShow the bottom status/mode bar on startup
show_bordersbooltrueShow panel borders on startup
show_sidebarbooltrueShow the filter sidebar on startup
show_line_numbersbooltrueShow the line number gutter
relative_line_numbersboolfalseShow line numbers relative to the selected row instead of absolute
wrapboolfalseWrap long lines
sidebar_sidestring"right"Pin the filter sidebar to "left" or "right" of the log panel
preview_bytesnumber16777216Bytes read for the instant preview shown while the full file index is built in the background (16 MiB)
restore_sessionstring"always"Whether to reopen tabs from the previous session ("always", "ask", "never")
restore_file_contextstring"always"Whether to restore per-file state (scroll, marks, search) when reopening a file ("always", "ask", "never")
mcp_portnumber9876Default port for the embedded MCP server when started via :enable-mcp
dlt_devicesarray[]Pre-configured DLT daemon connections; each entry has name, host, and optional port (default 3490)

Sections

  • Keybindings — remapping all keyboard shortcuts
  • Themes — built-in themes and creating custom themes

Keybindings

All keybindings are configurable via ~/.config/logana/config.json. Only the keys you want to change need to be specified — all others retain their defaults.

Key Syntax

Each binding is a string (or array of strings for multiple alternatives):

SyntaxExampleDescription
Single character"j"A printable key
Modified"Ctrl+d", "Shift+Tab"Modifier + key
Special keys"Enter", "Esc", "Space", "Backspace"Named keys
Function keys"F1", "F12"Function row keys
Navigation keys"Up", "Down", "Left", "Right", "PageUp", "PageDown", "Home", "End"Arrow/navigation keys

Multiple alternatives:

"scroll_down": ["j", "Down"]
"navigation": {
  "scroll_down": ["j", "Down"],
  "scroll_up": ["k", "Up"],
  "half_page_down": "Ctrl+d",
  "half_page_up": "Ctrl+u",
  "page_down": "PageDown",
  "page_up": "PageUp"
}

Normal Mode

"normal": {
  "scroll_left": ["h", "Left"],
  "scroll_right": ["l", "Right"],
  "start_of_line": "0",
  "end_of_line": "$",
  "command_mode": ":",
  "filter_mode": "f",
  "group_mode": "Ctrl+g",
  "toggle_filtering": "F",
  "toggle_highlight_mode": "H",
  "go_to_top_chord": "g",
  "go_to_bottom": "G",
  "mark_line": "m",
  "expand_continuation": ">",
  "collapse_continuation": "<",
  "search_forward": "/",
  "search_backward": "?",
  "next_match": "n",
  "prev_match": "N",
  "visual_mode": "V",
  "visual_char": "v",
  "toggle_marks_only": "M",
  "yank_line": "y",
  "yank_marked": "Y",
  "show_keybindings": "F1",
  "clear_all": "C",
  "edit_comment": "r",
  "delete_comment": "d",
  "comment_line": "c",
  "next_error": "e",
  "prev_error": "E",
  "next_warning": "w",
  "prev_warning": "W",
  "filter_include": "i",
  "filter_include_auto": "a",
  "filter_exclude": "o",
  "enter_ui_mode": "u",
  "clear_search": "Esc"
}

filter_mode opens the filter manager; group_mode opens group management scoped to the selected group. go_to_top_chord is the first key of the gg chord — pressing it twice jumps to the first line, matching go_to_bottom’s single-press jump to the last line. clear_search clears an active search highlight when nothing else is open.

Global (always active)

"global": {
  "quit": "q",
  "next_tab": "Tab",
  "prev_tab": "Shift+Tab",
  "new_tab": "Ctrl+t",
  "close_tab": "Ctrl+w",
  "file_switcher": "Ctrl+p"
}

Filter Manager

"filter": {
  "toggle_filter": "Space",
  "edit_filter": "e",
  "delete_filter": "d",
  "set_color": "c",
  "add_include_filter": "i",
  "add_include_filter_auto": "a",
  "add_exclude_filter": "o",
  "add_date_filter": "t",
  "add_highlight_filter": "h",
  "search": "/",
  "move_filter_down": "J",
  "move_filter_up": "K",
  "toggle_all_filters": "A",
  "clear_all_filters": "C",
  "exit_mode": "Esc",
  "sidebar_grow": ">",
  "sidebar_shrink": "<"
}

The filter manager also reuses the shared navigation group above (scroll_down/scroll_up/half_page_down/half_page_up/page_down/page_up) and, for jump-to-top/bottom, the normal.go_to_top_chord/normal.go_to_bottom bindings — no separate fields needed for those. sidebar_grow/sidebar_shrink resize the sidebar while it’s focused.

Group Mode

"group": {
  "clear_group_style": "x",
  "add_group": "a"
}

Group management (Ctrl+g) reuses several filter/navigation bindings rather than duplicating them: filter.toggle_all_filters/filter.toggle_filter to toggle the selected group, filter.edit_filter to edit its style, filter.exit_mode to exit, and navigation.scroll_down/scroll_up to move between groups. clear_group_style has no sensible existing key to borrow, so it gets this dedicated field.

Search / Filter Edit / Command Confirm & Cancel

"search": {
  "confirm": "Enter",
  "cancel": "Esc"
},
"filter_edit": {
  "confirm": "Enter",
  "cancel": "Esc"
},
"command": {
  "confirm": "Enter",
  "cancel": "Esc"
}

search confirms or cancels the log panel’s //? search or the filter manager’s / search. filter_edit confirms or cancels an in-place filter pattern edit. command confirms or cancels the : command line.

Visual Line Mode

"visual_line": {
  "comment": "c",
  "mark": "m",
  "yank": "y",
  "search": "/",
  "exit": "Esc"
}

Visual Char Mode

"visual": {
  "move_left": ["h", "Left"],
  "move_right": ["l", "Right"],
  "word_forward": "w",
  "word_backward": "b",
  "word_end": "e",
  "word_forward_big": "W",
  "word_backward_big": "B",
  "word_end_big": "E",
  "start_of_line": "0",
  "first_nonblank": "^",
  "end_of_line": "$",
  "find_forward": "f",
  "find_backward": "F",
  "till_forward": "t",
  "till_backward": "T",
  "repeat_motion": ";",
  "repeat_motion_rev": ",",
  "start_selection": "v",
  "filter_include": "i",
  "filter_exclude": "o",
  "search": "/",
  "yank": "y",
  "exit": "Esc"
}

Comment (Annotation) Mode

"comment": {
  "newline": "Enter",
  "save": "Ctrl+s",
  "cancel": "Esc",
  "delete": "Ctrl+d"
}

Confirm Dialogs

"confirm": {
  "yes": ["y", "Enter"],
  "no": ["n", "Esc"],
  "always": "Shift+Y",
  "never": "Shift+N"
}

always/never apply the same choice automatically to every future prompt of that kind, where the dialog supports it (e.g. session restore).

UI Options Mode

"ui": {
  "toggle_sidebar": "s",
  "toggle_mode_bar": "b",
  "toggle_borders": "B",
  "toggle_wrap": "w",
  "toggle_relative_line_numbers": "r",
  "toggle_groups_panel": "g",
  "exit": "Esc"
}

Select Fields Mode

"select_fields": {
  "toggle": "Space",
  "move_down": "J",
  "move_up": "K",
  "all": "a",
  "none": "n",
  "reset": "r",
  "apply": "Enter",
  "cancel": "Esc",
  "search": "/"
}

reset restores the popup’s staged fields to the format’s default order with everything visible — clearing both any J/K reorder and any hidden fields in one step. Like all/none, it only changes what’s staged; apply still commits it.

Archive Picker Mode

"archive_picker": {
  "toggle": "Space",
  "merge_toggle": "m",
  "expand": "Right",
  "collapse": "Left",
  "all": "a",
  "none": "n",
  "apply": "Enter",
  "cancel": "Esc",
  "search": "/",
  "search_toggle": "Ctrl+e",
  "search_merge_toggle": "Alt+m",
  "search_select_all": "Ctrl+a",
  "search_merge_all": "Ctrl+Alt+m"
}

toggle marks a file (or a container’s whole subtree) for extraction — each toggled file opens as its own tab on apply. merge_toggle marks a file independently for merging instead: every merge-marked file is extracted and combined into one timestamp-sorted tab on apply, rather than opening separately. A file can be toggled, merge_toggled, both, or neither, and apply performs both actions together in one press. If a merge-marked file’s format can’t be recognized, only the merge is skipped (with an error naming the file) — toggled files still extract and open normally.

Archive listing only auto-decompresses one nested level; a nested archive found any deeper shows as a collapsed row instead of being read upfront. expand reads and reveals it on demand (or, on an already-fetched row that’s merely folded shut, just reveals its children again with no re-fetch); collapse folds an expanded container’s children back out of view without discarding the already-fetched data.

search opens a live regex query that narrows the file tree to matching files (keeping their containing archive visible for context) — Enter confirms and un-narrows the list, Esc cancels back to the pre-search selection. An invalid/incomplete regex (e.g. while still typing) falls back to a plain substring match rather than matching nothing.

While searching, toggle/merge_toggle/all/none are unavailable — their keys are needed as literal query text. search_toggle and search_merge_toggle take their place, marking the selected row for extraction/merging without leaving search, so several files sharing a prefix can be selected from one query instead of re-searching for each. search_select_all/search_merge_all go further and mark every row whose name currently matches the query in one press, rather than just the selected row.

search_merge_toggle defaults to Alt+m rather than a Ctrl-chord at all: outside an enhanced keyboard protocol, Ctrl+M and a plain Enter keypress send the exact same byte, so the terminal reports Ctrl+M as Enter — a bare Ctrl+m binding would silently never fire, and apply’s Enter binding would consume the keypress instead. Alt+m sidesteps the ambiguity entirely.

search_merge_all defaults to Ctrl+Alt+m — a superset of search_merge_toggle’s Alt+m, keeping the two mnemonically paired (m for merge, an extra modifier for “all”). It doesn’t collide with Enter either, since Alt alone isn’t held.

Row navigation reuses the same keys as the filter sidebar and log panel (from the navigation group) rather than duplicating them here: j/k (optionally count-prefixed, e.g. 12j), Ctrl+d/Ctrl+u for a half page, PageDown/PageUp for a full page, and gg/G (optionally count-prefixed, e.g. 25G) to jump to the first/last or a specific row.

Docker Select Mode

"docker_select": {
  "confirm": "Enter",
  "cancel": "Esc"
}

DLT Select Mode

"dlt_select": {
  "confirm": "Enter",
  "cancel": "Esc",
  "delete": "d",
  "next_field": "Tab",
  "prev_field": "Shift+Tab"
}

next_field/prev_field move between the connection form’s input fields (host, port, …) before confirming.

Value Colors Mode

"value_colors": {
  "toggle": "Space",
  "all": "a",
  "none": "n",
  "apply": "Enter",
  "cancel": "Esc"
}

Keybindings Help

"help": {
  "close": ["Esc", "q", "F1"]
}

Custom Commands

Bind a key to run a fixed command line, checked in Normal Mode ahead of every built-in action:

"custom": [
  {
    "key": "F2",
    "command": "load-filters ~/logs/filters/draco-mars.json"
  }
]

command is whatever you’d type after : in command mode — no leading :. key accepts any binding from the Key Syntax above, including an array of alternatives. Add as many entries as you like; each one gets its own row in :show-keybindings.

A custom binding that reuses a built-in action’s key wins over it (deliberately — see Conflict Validation below for how you’ll be warned about the collision, not blocked from making it).

Conflict Validation

At startup, logana validates all configured keybindings for conflicts within each mode scope. Conflicts are printed to stderr with a description of the overlapping bindings, but do not prevent startup.

Themes

logana ships with 22 bundled themes and supports fully custom themes via JSON files.

Switching Themes

:set-theme catppuccin-mocha

Tab completes theme names. The chosen theme is persisted to the database and restored on the next session.

To pin a theme permanently (overriding any runtime changes), add it to ~/.config/logana/config.json:

{ "theme": "catppuccin-mocha" }

Bundled Themes

Dark

NameDescription
atomicVibrant, high-saturation
catppuccin-macchiatoPastel purple, slightly lighter than mocha
catppuccin-mochaPastel purple, the most popular Catppuccin variant
draculaPurple, default theme
everforest-darkEarthy green, easy on the eyes
github-darkGitHub dark — deep navy with blue accents
github-dark-dimmedGitHub dark dimmed — softer navy variant
gruvbox-darkWarm retro browns and yellows
jandedobbeleerColorful, high contrast
kanagawaJapanese ink — deep blues and warm golds
monokaiClassic dark with vivid accents
nordCool blue-grey Arctic palette
onedarkAtom-inspired, muted cool colors
paradoxHigh contrast
rose-pineMuted roses and purples
solarizedClassic muted palette
tokyonightDeep blue, inspired by Tokyo at night

Light

NameDescription
catppuccin-lattePastel, warm cream background
everforest-lightEarthy green, warm paper background
github-lightGitHub light — clean white with blue accents
onelightAtom-inspired, clean white background
rose-pine-dawnWarm rose tones on a parchment background

Custom Themes

Place .json files in ~/.config/logana/themes/. A user theme with the same name as a bundled one takes priority.

Minimal example

Only five fields are required — everything else falls back to built-in defaults:

{
  "root_bg":    "#1e1e2e",
  "border":     "#6272a4",
  "border_title": "#f8f8f2",
  "text":       "#f8f8f2",
  "error_fg":   "#ff5555",
  "warning_fg": "#f1fa8c",
  "process_colors": ["#ff5555", "#50fa7b", "#ffb86c", "#bd93f9", "#ff79c6", "#8be9fd"]
}

Full example (Dracula)

{
  "root_bg":          "#282a36",
  "border":           "#6272a4",
  "cursor_bg":        "#6272a4",
  "border_title":     "#f8f8f2",
  "text":             "#f8f8f2",
  "text_highlight_fg": "#ffb86c",
  "text_highlight_bg": "#7a4a10",
  "cursor_fg":        "#1c1c1c",
  "trace_fg":         "#6272a4",
  "debug_fg":         "#8be9fd",
  "notice_fg":        "#f8f8f2",
  "warning_fg":       "#f1fa8c",
  "error_fg":         "#ff5555",
  "fatal_fg":         "#ff5555",
  "search_fg":        "#1c1c1c",
  "visual_select_bg": "#44475a",
  "visual_select_fg": "#f8f8f2",
  "mark_bg":          "#463c0f",
  "mark_fg":          "#f8f8f2",
  "process_colors":   ["#ff5555", "#50fa7b", "#ffb86c", "#bd93f9", "#ff79c6", "#8be9fd"],
  "value_colors": {
    "http_get":    "#50fa7b",
    "http_post":   "#8be9fd",
    "http_put":    "#ffb86c",
    "http_delete": "#ff5555",
    "http_patch":  "#bd93f9",
    "http_other":  "#6272a4",
    "status_2xx":  "#50fa7b",
    "status_3xx":  "#8be9fd",
    "status_4xx":  "#ffb86c",
    "status_5xx":  "#ff5555",
    "ip_address":  "#bd93f9",
    "uuid":        "#6c71c4"
  }
}

Color Formats

All color values accept:

  • Hex string: "#RRGGBB"
  • RGB array: [r, g, b] (each 0–255)

Fields Reference

Required

FieldUsed for
root_bgMain background
borderPanel border lines and dimmed decorator text
border_titlePanel title text
textDefault log line text
error_fgERROR level lines
warning_fgWARN/WARNING level lines
process_colorsArray of colors cycled across process/logger name columns (can be toggled via :value-colors)

Optional (with defaults)

FieldDefaultUsed for
cursor_bg= borderBackground of the cursor line, command bar, and search bar
text_highlight_fg#ffb86cSearch match background; also the cursor for the current match
text_highlight_bg#7a4a10Background behind search highlight
cursor_fg#1c1c1cText color on the cursor line (sits on cursor_bg)
trace_fg#6272a4TRACE level lines
debug_fg#8be9fdDEBUG level lines
info_fg= textINFO level lines (disabled by default; enable via :level-colors)
notice_fg#f8f8f2NOTICE level lines
fatal_fg#ff5555FATAL/CRITICAL level lines
search_fg#1c1c1cForeground of search match highlights
visual_select_bg#44475aVisual line selection background
visual_select_fg#f8f8f2Visual line selection foreground
mark_bg#463c0fMarked line background
mark_fg#f8f8f2Marked line foreground
value_colorssee belowPer-token HTTP/IP/UUID colors

value_colors sub-object

All fields are optional and fall back to Dracula-palette defaults.

FieldDefaultToken type
http_get#50fa7bGET
http_post#8be9fdPOST
http_put#ffb86cPUT
http_delete#ff5555DELETE
http_patch#bd93f9PATCH
http_other#6272a4HEAD, OPTIONS, and others
status_2xx#50fa7b2xx success codes
status_3xx#8be9fd3xx redirect codes
status_4xx#ffb86c4xx client error codes
status_5xx#ff55555xx server error codes
ip_address#bd93f9IPv4 and IPv6 addresses
uuid#6c71c4UUID strings

Toggling token and level colors at runtime

Use :value-colors to open an interactive dialog where you can enable or disable individual token types — including Process / logger colors — without editing the theme file.

Use :level-colors to open a similar dialog for log levels. Each level (TRACE, DEBUG, NOTICE, WARNING, ERROR, FATAL) can be toggled independently. The choices are saved per-file across sessions.

Tips for Light Themes

Set cursor_bg to a color that is noticeably darker than root_bg so the cursor line and command bar are clearly visible. Keep border as a subtle separator — it can be close to root_bg if you prefer minimal panel borders.

Set cursor_fg and search_fg to a dark color — they appear as text on the cursor_bg background and must contrast against it.

{
  "root_bg":   "#fafafa",
  "border":    "#d0d0d0",
  "cursor_bg": "#aaaaaa",
  "cursor_fg": "#383a42",
  "search_fg": "#383a42"
}

Log Formats

logana detects the log format automatically by sampling the first lines of the file. No flags or configuration are required.

Supported Formats

FormatExamples
OpenTelemetry (OTLP)OTLP/JSON protobuf-JSON encoding, OTel SDK JSON
DLTAUTOSAR binary (storage, wire, simplified) and dlt-convert -a text
JSONtracing-subscriber JSON, bunyan, pino, any structured JSON logger
SyslogRFC 3164 (BSD), RFC 5424
Journalctlshort, short-iso, short-precise, short-full, short-monotonic, short-unix, json-sse, json-seq
Common / Combined LogApache access, nginx access
LogfmtGo slog, Heroku, Grafana Loki
Common log familyenv_logger, tracing-subscriber fmt (with/without spans), logback, log4j2, Spring Boot, Python logging, loguru, structlog

Detection

All registered parsers score a confidence value against the first 200 lines of the file. The parser with the highest score above 0.0 is selected. More specific parsers naturally score higher on their format; the common log parser applies a 0.95× penalty to yield to more specific parsers on ties. The OTLP parser scores up to 1.5 (above the 1.0 maximum for plain JSON) so it wins when OpenTelemetry fields are present.

User-defined schemas (see Custom Schemas below) are always evaluated first, before any built-in parser.

The detected format name is shown in the status bar. Run :schema to show the active one, or :schema <name> to force a specific format for the current tab — typing :schema shows every custom and built-in schema in the autocomplete list (custom ones first, alphabetically, each in its own color).

Format Details

DLT (AUTOSAR Diagnostic Log and Trace)

Three binary layouts are supported and converted to text at load time:

  • Storage format — standard AUTOSAR DLT files with 16-byte storage headers (magic bytes DLT\x01)
  • Wire format — concatenated DLT messages without storage headers, as received from a dlt-daemon TCP connection
  • Simplified format — compact DLT\x01 + ECU + APID + CTID + timestamp + payload

The text output produced by dlt-convert -a is also parsed directly.

Fields extracted: timestamp, hw_ts (hardware timestamp), mcnt (message counter), ecu, apid (application ID), ctid (context ID), type, subtype, mode (verbose/non-verbose).

Verbose payloads are decoded (strings, integers, floats, booleans, raw data). Non-verbose payloads are shown as hex.

OpenTelemetry (OTLP)

Two JSON-based OTel log formats are supported for file-based parsing. logana also accepts live OTLP streams over gRPC (:otel, port 4317) and HTTP/JSON (:otel --http, port 4318) — see OTel Collector.

OTLP/JSON (protobuf-JSON encoding — exported by collectors):

{"timeUnixNano":"1700000000000000000","severityNumber":9,"severityText":"INFO","body":{"stringValue":"request received"},"attributes":[{"key":"service.name","value":{"stringValue":"my-svc"}}]}
  • Timestamp: timeUnixNano (nanosecond epoch string)
  • Severity: severityNumber (1–4=TRACE, 5–8=DEBUG, 9–12=INFO, 13–16=WARN, 17–20=ERROR, 21–24=FATAL) and/or severityText
  • Body: body.stringValue (AnyValue object encoding)
  • Attributes: array of {key, value} objects

OTel SDK JSON (emitted directly by SDKs):

{"timestamp":"2024-01-01T00:00:00.000Z","severity_text":"INFO","severity_number":9,"body":"request received","attributes":{"service.name":"my-svc"}}
  • Timestamp: timestamp (ISO 8601)
  • Severity: severity_text and/or severity_number
  • Body: direct string value
  • Attributes: flat {key: value} dict

Both formats surface service.name, code.namespace, logger, and similar target attributes as the target column.

JSON

Structured JSON logs, one JSON object per line. Supports:

  • tracing-subscriber JSON{"timestamp":...,"level":...,"target":...,"span":{...},"fields":{"message":...}}
  • bunyan{"time":...,"level":...,"name":...,"msg":...}
  • pino{"time":...,"level":...,"msg":...}
  • Any structured JSON log with recognizable timestamp/level/message keys

Span sub-fields (e.g. span.name, span.id, fields.request_id) are discoverable and selectable as columns.

Syslog

  • RFC 3164 (BSD): <PRI>Mmm DD HH:MM:SS hostname app[pid]: message
  • RFC 5424: <PRI>VER TIMESTAMP HOSTNAME APP PROCID MSGID [SD] MSG

Priority is decoded to a log level; facility is exposed as an extra field.

Journalctl

Text output from journalctl in several formats:

  • short: Mmm DD HH:MM:SS hostname unit[pid]: message
  • short-iso: YYYY-MM-DDTHH:MM:SS±ZZZZ hostname unit[pid]: message
  • short-precise: Mmm DD HH:MM:SS.FFFFFF hostname unit[pid]: message
  • short-full: Www YYYY-MM-DD HH:MM:SS TZ hostname unit[pid]: message
  • short-monotonic: [SSSSS.FFFFFF] hostname unit[pid]: message
  • short-unix: [EPOCH.FFFFFF] hostname unit[pid]: message
  • json-sse: server-sent events wrapping JSON journal entries (data: {...})
  • json-seq: RFC 7464 JSON sequence (\x1e{...}\n)

Header/footer lines (-- Journal begins..., -- No entries --) are silently skipped.

Common / Combined Log Format

Apache and nginx access logs:

  • CLF: host ident authuser [dd/Mmm/yyyy:HH:MM:SS ±ZZZZ] "request" status bytes
  • Combined: CLF + "referer" "user-agent"

Fields with value - are omitted.

Logfmt

Space-separated key=value pairs. Used by Go slog, Heroku, Grafana Loki, and many 12-factor apps. Quoted values (key="value with spaces") are supported.

Requires at least 3 key=value pairs per line to distinguish from plain text.

Common Log Family

A broad family sharing the TIMESTAMP LEVEL TARGET MESSAGE structure, with several sub-strategies:

  • env_logger: [ISO LEVEL target] msg or [LEVEL target] msg
  • logback / log4j2: DATETIME [thread] LEVEL target - msg
  • Spring Boot: DATETIME LEVEL PID --- [thread] target : msg
  • Python basic: LEVEL:target:msg
  • Python prod: DATETIME - target - LEVEL - msg
  • loguru: DATETIME | LEVEL | location - msg
  • structlog: DATETIME [level] msg key=val...
  • tracing-subscriber fmt with spans: TIMESTAMP LEVEL span_name{k=v ...}: target: msg — span context is parsed and available as the span column
  • Generic fallback: TIMESTAMP LEVEL rest-as-message — any timestamp + level keyword combination

Custom Schemas

If none of the built-in parsers match your log format, you can define your own schema. Each schema lives in its own JSON file inside a schema/ directory next to config.json:

OSPath
Linux~/.config/logana/schema/<name>.json
macOS~/Library/Application Support/logana/schema/<name>.json
Windows%APPDATA%\logana\schema\<name>.json

On Windows, %APPDATA% resolves to C:\Users\<username>\AppData\Roaming, e.g. C:\Users\<username>\AppData\Roaming\logana\schema\<name>.json.

Template syntax

The easiest way to describe a format is with a template — write the literal shape of a log line with {field} placeholders where fields appear:

{id} {service} <{timestamp}> {pid} {level}/{component}/{feature}, {message}

logana compiles the template to a regex automatically:

  • {name} — matches non-whitespace characters, stopping at the next literal delimiter or whitespace
  • {name} when adjacent to a literal character (e.g. <{timestamp}>) — stops at that character
  • The last placeholder always captures the rest of the line

Alternatively, supply a raw pattern with named capture groups for formats the template language cannot express.

Field roles

Placeholder names that match a known semantic are mapped automatically:

NameSemantic
timestampTimestamp column
levelLevel column (normalized: INF→Info, ERR→Error, etc.)
messageMessage column
targetTarget column
componentComponent field
featureFeature field
hostnameHostname field
pidPID field
threadThread field
facilityFacility field

Any other placeholder name defaults to an extra field. Use the fields map to assign a different role to a non-standard name.

Example

Acme node log line:

04 LINUX-0-syscon <2035-04-04T21:54:53.283856Z> 62A INF/Syscon/StartupMgr, StateChange: dirtyrfservice::instance1 state=CONNECTED

Schema file at ~/.config/logana/schema/acme.json:

{
  "name": "acme",
  "description": "Acme node log format",
  "template": "{id} {service} <{timestamp}> {pid} {level}/{component}/{feature}, {message}",
  "fields": {
    "id":      "extra",
    "service": "target"
  }
}

service (LINUX-0-syscon) is mapped to target because it identifies the producing service. id is mapped to extra since no built-in semantic fits a hex sequence number.

Critical fields

Three fields unlock core logana features. Map them correctly if your format contains them:

FieldFeatures that depend on it
timestampDate & time filters (:date-filter, -t) — without a timestamp field, date filters are silently skipped
levelError/warning navigation (e/w) and level-based coloring — both are disabled when no level field is present
targetField coloring by originating component in the structured view

Forcing a schema

:schema             — show the active schema
:schema acme     — force the acme schema for this tab

Default filter files per format

:default-filters                              — open a popup listing every format and its configured filter file
:default-filters acme ~/logs/filters/acme.json — set acme's default filter file
:default-filters acme                          — clear acme's default filter file mapping

When a tab’s format becomes acme — auto-detected on open, or via :schema acme — and the tab has no filters yet, its configured default filter file loads automatically, same effect as :load-filters. Setting or clearing a mapping never retroactively affects the tab you’re currently on — it only applies the next time a tab’s format is assigned.

tracing-subscriber fmt (Rust / Axum)

Rust applications using tracing-subscriber’s default fmt output produce lines like:

Startup (no span):

2024-02-21T10:00:00.123456Z  INFO app::server: listening on 0.0.0.0:3000

Runtime (with span):

2024-02-21T10:00:01.234Z  INFO request{method=GET uri=/api/users id="0.5"}: app::handler: processing request

Both forms are handled: span lines are parsed into a span column with name and fields; non-span lines fall through to the generic fallback.

Custom Schemas

If none of the built-in parsers match your log format, define your own schema. logana loads every .json file from the schema/ directory next to config.json and includes them in format detection automatically — no restart required after adding a file.

If a schema file fails to load (malformed JSON) or fails to compile (an invalid template/pattern), logana shows a startup warning naming the file or schema and the problem, instead of silently dropping it from format detection.

Schema directory

OSPath
Linux~/.config/logana/schema/
macOS~/Library/Application Support/logana/schema/
Windows%APPDATA%\logana\schema\

On Windows, %APPDATA% resolves to C:\Users\<username>\AppData\Roaming, e.g. C:\Users\<username>\AppData\Roaming\logana\schema.

Each file describes one format. The filename is arbitrary; the name field inside the JSON is what identifies the schema at runtime.

Schema Validation

logana publishes a JSON Schema for custom schema files. Add a $schema line to enable validation and autocomplete in VS Code and other JSON-aware editors:

{
  "$schema": "https://raw.githubusercontent.com/pauloremoli/logana/main/schema/custom-schema.schema.json",
  "name": "my-format",
  "template": "{level} {message}"
}

An unrecognized key (e.g. a typo like "templte") is rejected at load time — logana’s startup warning names the file and the bad key instead of silently ignoring it.

Schema file structure

{
  "name":        "my-format",
  "description": "Optional human-readable description",
  "template":    "{field1} <{timestamp}> {level}/{component}, {message}",
  "fields": {
    "field1": "extra"
  }
}
KeyRequiredDescription
nameyesIdentifier used by :schema <name> and shown in the status bar
descriptionnoFree-form description; not used by logana
templateone of template / patternA single-line template string, or an array describing a full multi-line record — see Multiline records
patternone of template / patternRaw regex with named capture groups
fieldsnoOverrides the automatic role for named placeholders/groups
multilinenoWhen true (and template is a plain string), folds continuation lines into the record’s message field — see Folding continuation text into message

Template syntax

A template describes the literal shape of a log line. Write the fixed characters exactly as they appear and mark variable parts with {name}:

{id} {service} <{timestamp}> {pid} {level}/{component}/{feature}, {message}

Compilation rules:

  • A placeholder adjacent to a literal delimiter (e.g. <{ts}>, {level}/) stops at that character — <{ts}> becomes <(?P<ts>[^>]+)>.
  • A placeholder followed by whitespace or another placeholder matches \S+.
  • The last placeholder always captures the rest of the line, regardless of what follows it.

Raw regex

Use pattern instead of template when the template language is not expressive enough:

{
  "name": "my-format",
  "pattern": "^(?P<ts>\\S+)\\s+(?P<level>\\w+)\\s+(?P<message>.*)$",
  "fields": { "ts": "timestamp" }
}

The regex must use named capture groups ((?P<name>...)). Group names are resolved to field roles using the same rules as template placeholders.

Field roles

Placeholder or capture group names that match a known semantic are mapped automatically:

NameRole
timestampTimestamp column
levelLevel column — normalized automatically (INF→Info, ERR→Error, WRN→Warning, etc.)
messageMessage column
targetTarget column
componentComponent field
featureFeature field
hostnameHostname field
pidPID field
threadThread field
facilityFacility field

Any name not in the table above defaults to extra — it appears in the structured fields sidebar with its original name.

Use the fields map to assign a different role to a non-standard name:

"fields": {
  "service": "target",
  "seq":     "extra",
  "thr":     "thread"
}

The value can also be "ignored" to capture a group in the regex without displaying it.

Critical fields

Three fields unlock core logana features. If your format contains them, map them correctly:

FieldFeatures that depend on it
timestampDate & time filters (:date-filter, -t on the CLI) — without a timestamp field, date filters have nothing to match against and are silently skipped
levelError/warning navigation (e/w keys to jump between errors and warnings) and level-based field coloring — without a level field both are disabled for that tab. If your level values aren’t error/warn/etc., see Level values below to map them.
targetField coloring by target — used to color-code log lines by their originating component in the structured view

If your format uses non-standard names for these fields, always map them explicitly:

"fields": {
  "sev":     "level",
  "ts":      "timestamp",
  "subsys":  "target"
}

Level values

level is normalized automatically against a built-in set of keywords (error/err, warn/warning/wrn, info/inf, …). If your format’s level values don’t match any of them — e.g. severity codes like SEV1/SEV2 — level coloring and e/w error/warning navigation won’t recognize them by default. Declare which raw values (case-insensitive) mean error and warning with levels:

{
  "name":     "sev",
  "template": "{level}: {message}",
  "levels": {
    "error":   ["SEV1"],
    "warning": ["SEV2"]
  }
}

A value can only be declared for one of error/warning — declaring the same value in both is a schema error, reported the same way as an invalid template/pattern (see the startup warning at the top of this page). Values not covered by levels still fall back to the built-in keyword matching.

Rendering

When a template-based schema matches a line, the log panel renders it using the template’s own field order and literal separators — so {level}/{component}/{feature} shows as INF/Syscon/StartupMgr, not space-joined columns. Hiding a field with :select-fields keeps this: the hidden field’s value drops and its adjacent separator collapses with it, so hiding component renders INF/StartupMgr rather than a dangling INF//StartupMgr. Only genuinely reordering columns (moving a field with J/K in :select-fields) falls back to the standard space-joined column layout, since an arbitrary new order can’t be expressed with the template’s fixed separators. This only applies to template-defined schemas; a pattern-defined schema (raw regex) always uses the column layout, since a regex has no literal skeleton to reconstruct from.

The :select-fields popup lists a template-defined schema’s fields in the template’s own order (e.g. id, target, timestamp, pid, level, component, feature, message for the acme example below) rather than the generic timestamp/level/target/extras/message grouping used for other formats — matching the order the line actually renders in by default.

Detection

Custom schemas are evaluated before all built-in parsers. When a schema matches ≥ 50% of the sampled lines, it wins the detection competition and is used for all subsequent parsing.

Run :schema to show the current tab’s active schema, or :schema <name> to force a specific custom schema for the current tab — start typing a name to see every custom and built-in schema in the autocomplete list.

A default filter file can also be configured per schema — see Default filter files per format.

Multiline records

A line that doesn’t match the schema’s template/pattern is already treated as a continuation of the previous matched line — it’s shown right below its parent and inherits the parent’s level and visibility. This covers most multiline formats (stack traces, wrapped messages) automatically, with no schema changes needed.

By default, continuation lines stay unstructured: their content isn’t part of any parsed field, so field filters can’t see it. Two independent ways to give them structure:

  • "multiline": true — fold all continuation text into the record’s message field as one blob. Simplest option; use it when you just want the continuation text searchable, not broken into individual fields.
  • An array template — describe the continuation lines’ own shape, extracting each into its own field (or a repeating group of sub-records). Use this when continuation lines carry structured key: value data you want to filter on individually.

The two can be combined: an array template structures what it recognizes, and multiline still folds any unrecognized trailing continuation lines into message.

Folding continuation text into message

Set "multiline": true to fold continuation lines into the record’s message field:

{
  "name":      "journalctl-verbose",
  "template":  "{weekday} {timestamp} {host} [{cursor}]",
  "multiline": true,
  "fields": {
    "cursor": "extra"
  }
}

Given journalctl --output=verbose output like:

Tue 2024-01-01 10:15:30.123456 UTC myhost [s=abc;i=1;b=def;t=2;x=3]
    MESSAGE=disk usage at 92%
    _PID=1234
    _COMM=diskmond

Only the first line matches this schema’s template — the two indented _KEY=VALUE lines become its continuation. With multiline enabled, the record’s message field becomes the continuation lines’ raw text (joined by their original newlines), so :filter --field message disk usage matches the record even though the header line itself carries no message text at all. If the schema’s template does capture a message field, continuation text is appended after it instead of replacing it.

This only changes what field filters and the structured fields panel see for the record — each physical line still renders as its own row in the log panel, exactly as before.

Structured continuation lines

For formats where each continuation line carries its own field — or where the record has an explicit terminator instead of just running until the next header — make template an array instead of a single string. Each element describes one line of the record, in order:

  • Element 0 is always the header (the line that starts a new record) — same rules as a plain string template.
  • A plain string or object after the header is a flat continuation line, extracting its own fields.
  • {"vec": "<name>", "template": ..., "fields": [...], "auto_fields": ...} declares a repeating group — see Repeating groups below.
  • If the last element is a plain line (not a group), it’s the record’s terminator: extraction stops once that line is seen, purely by its position as the last array element — no separate marker needed. Without one, the record runs until the next line matching the header.
{
  "name": "transaction",
  "template": [
    "### Start transaction {id}",
    "field1: {field1}",
    "field2: {field2}",
    { "template": "Object {payload}", "json": true },
    "### End transaction"
  ],
  "fields": { "id": "extra" }
}

Each flat line accepts the same shorthand as the top-level schema: a bare string (template syntax), or an object with template/pattern/fields/json for more control. A continuation field can’t be mapped to timestamp, level, target, or message — those belong to the header alone; map it to extra (the default) or any other field role instead. Set "json": true on an entry to treat its single placeholder as an embedded JSON object instead of a plain string — each key in the object becomes its own extra field, using the JSON key’s own name.

Given:

### Start transaction 42
field1: 10
field2: 3
Object { "user": "alice", "amount": 99 }
### End transaction

the record is parsed with extra fields id=42, field1=10, field2=3, user=alice, amount=99. The terminator ("### End transaction") also bounds where extraction stops — any lines between it and the next ### Start transaction (stray blank lines, unrelated output, etc.) aren’t scanned for fields.

Repeating groups

Use a {"vec": ...} entry when a record contains a variable number of nested sub-records — e.g. a batch job with any number of workers. Its template matcher opens a new item in the group (and finalizes the previous one, if any); its optional fields are matchers tried in order against subsequent lines while that item is open.

{
  "name": "batch-job",
  "template": [
    "### Job {job_id} started",
    "Owner: {owner}",
    { "vec": "workers", "template": "worker: {hostname}" },
    {
      "vec": "checkpoints",
      "template": "checkpoint: {name}, status: {status}",
      "auto_fields": false
    },
    "### Job {job_id} finished"
  ],
  "fields": { "job_id": "extra" }
}

A group with only template (no fields) is a single-line-item group — every matching line closes the previous item and opens a new one from its own captures (checkpoints above: each line is a complete record). A group with fields opens an item on template and keeps extracting fields from subsequent lines into that same item until the next line that matches template (a new item), a different group’s template, or the terminator.

auto_fields (default true) captures any continuation line inside a group’s open item that looks like key: value (or key: "value") but wasn’t matched by a declared fields entry — useful when a nested block’s field set varies too much to enumerate. Lines with no key: value shape are still ignored. Set it to false (as checkpoints does above) to only extract explicitly declared fields. workers above relies entirely on auto_fields: any key: value line following a worker: {hostname} line (e.g. cpu: 87%, status: running) is captured automatically into that worker’s item.

A group’s name becomes the dotted-index column prefix in rendering (workers.0.hostname, workers.1.hostname, …) and the group-scoped path for field filters (workers.hostname — see Field Filters).

Full example — Acme node log

Log line:

04 LINUX-0-syscon <2035-04-04T21:54:53.283856Z> 62A INF/Syscon/StartupMgr, StateChange: dirtyrfservice::instance1 state=CONNECTED

~/.config/logana/schema/acme.json:

{
  "name": "acme",
  "description": "Acme node log: id service <timestamp> pid level/component/feature, message",
  "template": "{id} {service} <{timestamp}> {pid} {level}/{component}/{feature}, {message}",
  "fields": {
    "id":      "extra",
    "service": "target"
  }
}

Parsed fields:

FieldValue
timestamp2035-04-04T21:54:53.283856Z
levelINF → Info
targetLINUX-0-syscon
componentSyscon
featureStartupMgr
messageStateChange: dirtyrfservice::instance1 state=CONNECTED
id (extra)04
pid (extra)62A

Data Locations

Runtime Files

Paths depend on the operating system:

LocationLinuxmacOSWindows
Database~/.local/share/logana/logana.db~/Library/Application Support/logana/logana.db%APPDATA%\logana\logana.db
Config file~/.config/logana/config.json~/Library/Application Support/logana/config.json%APPDATA%\logana\config.json
Themes dir~/.config/logana/themes/~/Library/Application Support/logana/themes/%APPDATA%\logana\themes\
Templates dir~/.config/logana/templates/~/Library/Application Support/logana/templates/%APPDATA%\logana\templates\
Schema dir~/.config/logana/schema/~/Library/Application Support/logana/schema/%APPDATA%\logana\schema\

On Windows, %APPDATA% resolves to C:\Users\<username>\AppData\Roaming, e.g. C:\Users\<username>\AppData\Roaming\logana\schema.

Database

The SQLite database stores:

  • Filters — include/exclude patterns and date filters, per source file
  • File context — per-file session state: scroll position, search query, wrap, sidebar visibility, marked lines, field layout, show-keys preference, and more
  • Session tabs — the ordered list of files/Docker streams open when logana last exited (used for session restore)

The database is created automatically on first run. Schema migrations run on startup — no manual setup needed.

Config File

The config file is optional. If it is absent, logana starts with all defaults. If the file exists but cannot be read, contains invalid JSON, or has unknown keys, a warning is shown in the notification area on startup. Partial configs are valid — only specified keys override defaults.

See Configuration for the full schema.

Custom Themes

Place .json files in ~/.config/logana/themes/. Files here shadow bundled themes of the same name. See Themes for the theme JSON format.

Custom Export Templates

Place .txt template files in ~/.config/logana/templates/. Files here shadow bundled templates (markdown, jira) of the same name. See Annotations & Export for the template format.

Custom Schemas

Place .json schema files in ~/.config/logana/schema/. Each file is loaded automatically and included in format detection — no restart required. See Log Formats for the schema file format.