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

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 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 / patternTemplate string with {field} placeholders
patternone of template / patternRaw regex with named capture groups
fieldsnoOverrides the automatic role for named placeholders/groups

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.

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