Logs · Linux + macOS

Find the messages around a failure.

lens-logs reads recent local logs once, lets you narrow them by time, service, process, severity or text, and emits plain text for people or schema-versioned JSON for scripts.

Quick start

Run the live command as your normal user; Lens reads the system in front of you.

shell
# Read the logs available to your current account
lens-logs --once
lens-logs --plain

# Linux: journalctl time expressions are accepted
lens-logs --since "1 hour ago"

# macOS: use a `log show --last` duration
lens-logs --since 1h

When stdout is a terminal, the command opens a navigable message list. Use the arrow keys and press Enter to inspect a message. On macOS, the latest minute appears first while the previous hour loads in the background. Pass --once for one readable snapshot, choose --plain or --json explicitly, or redirect output to get plain text automatically.

Lens reads the logs available to your current account and does not send them anywhere.

Use the interactive log view

The list is a working view of one collected time range, not an endless log stream. The highlighted message stays selected while a background collection finishes, so a slower refresh does not move you to an unrelated entry.

Up / Down or j / kMove through messages. The list scrolls around the selected row when space allows.
EnterOpen the selected message with its complete text, source, timestamp, inferred severity, service context and repeat count.
EscReturn from message detail or close an overlay.
/Open a search card over the current view and match message text without discarding the screen behind it.
rCollect the selected time range again. This is a new snapshot, so messages may enter or leave the result.
!Open the local diagnostic shell beside the log view on wide terminals, or as a bordered overlay on compact terminals.
?Show the keys available in the current view.
qQuit Lens.

On macOS, the initial interactive result uses a short one-minute window so the screen opens promptly. The previous hour then replaces it when collection completes. A loading label means that work is still in progress; zero entries is shown only after a source successfully returned no messages.

Common investigations

A Linux service failed

Show recent error-like messages attributed to a systemd unit. Service matching is case-insensitive and accepts a partial unit name, so ssh can match sshd.service.

shell
lens-services --service mosquitto
lens-logs --since "30 minutes ago" --service mosquitto --severity error

A process name or PID appears in messages

--process searches the message text. It does not resolve a PID to a process after that process has exited.

shell
lens-logs --process nginx
lens-logs --process 4242 --limit 25

An application writes its own log file

Add one or more UTF-8 text files. Lens reads the newest 1,000 lines by default and combines them with the native platform log before filtering. Use --limit 0 to read the complete files.

shell
lens-logs --log-file /var/log/my-app.log --filter timeout
lens-logs --log-file ./api.log --log-file ./worker.log --json

Read the plain-text output

plain text
2026-08-03T00:00:01Z  write failed: No space left on device  ×12
2026-08-03T00:00:02Z  mosquitto.service entered failed state  ×3
  • The first field is the source timestamp. Explicit file inputs may have no timestamp because Lens does not assume a file-specific format.
  • ×12 means 12 adjacent records had exactly the same message. Only adjacent duplicates are folded; their count is preserved.
  • No output can mean that the filters matched nothing. Check --json and inspect collection_warnings to distinguish that from an unavailable source.

Filters and options

--since VALUELimit the native platform log by time. Use journalctl syntax on Linux and durations such as 1h or 30m on macOS.
--service NAMEKeep logs attributed to a matching systemd unit. Linux only.
--process TEXTCase-insensitive substring match against the complete message, useful for a process name or PID.
--severity VALUEKeep the inferred label error or warning. Messages without either label are excluded.
--filter TEXTCase-insensitive substring match against the message after the other filters.
--log-file PATHAdd a local text file. Repeat the option to add more than one file.
--limit NUMBERCollect and return at most this many rows. Default: 1000. Use 0 for every available row.
--plainPrint one human-readable snapshot and exit.
--jsonPrint one complete schema-v2 SystemSnapshot and exit.

Run lens-logs --help for the authoritative option list installed with your version.

Sources and current limits

Linux

  • Runs journalctl --no-pager --output=short-iso -n 1000 by default and passes --since through to journalctl. --limit 0 removes the record limit.
  • Infers a unit from the journal's short text representation; --service depends on that inferred value.
  • Journal visibility is exactly what the invoking user can read.

macOS

  • Runs /usr/bin/log show --style syslog --last VALUE --info --debug. The default time window is 1h.
  • Keeps the newest 1,000 returned messages by default; --limit 0 keeps every message in the selected time range.
  • Unified-log entries do not carry a normalized service unit, so use --process or --filter instead of --service.

Severity and repeat detection

Severity is currently a text heuristic: messages containing error or failed become error; messages containing warn become warning. It is not the native journal priority or macOS log level. Repeat folding compares adjacent message text exactly.

Choose the range deliberately for large investigations.

The default is the newest 1,000 messages. Combine --since with --limit 0 when you need every retained message in a known time range. Use journalctl or log stream when you need a continuously updating stream.

JSON for scripts

--json returns the shared system snapshot, not a bare array of log lines. That lets a consumer connect a log to services, processes, findings and other system entities. Read schema_version before consuming fields.

json
{
  "schema_version": "2",
  "generated_at": "2026-08-03T00:00:00Z",
  "log_sources": [
    { "id": "systemd-journal", "kind": "journal" }
  ],
  "logs": [{
    "timestamp": "2026-08-03T00:00:01Z",
    "source": "systemd-journal",
    "unit": "mosquitto.service",
    "priority": "error",
    "message": "write failed: No space left on device",
    "repeated": 12
  }],
  "relationships": [ ... ],
  "collection_warnings": []
}

For example, extract only the log array with lens-logs --json | jq '.logs', or fail a check when a source was unavailable with jq -e '.collection_warnings | length == 0'.

When data is missing

Lens does not elevate privileges. A missing command, inaccessible journal, unreadable file or platform privacy restriction becomes an entry in collection_warnings; collection continues for other sources.

shell
lens-logs --json | jq '{logs: (.logs | length), warnings: .collection_warnings}'

If you expected messages but got none

  1. Remove --service, --process, --severity and --filter to check the unfiltered snapshot.
  2. Use the correct --since syntax for the operating system.
  3. Inspect collection_warnings with the command above.
  4. On Linux, compare with journalctl -n 20 as the same user. On macOS, compare with log show --last 10m.
  5. For a file, confirm that the same user can read it and that it is UTF-8 text.