Skip to content

Enhanced Log Viewing #182

Description

@jmsobe

Problem / Motivation

Add a standalone Log Explorer window that extends Yerd’s already-solid logging and debugging tools into one exploratory surface for file-based logs — local, offline, and tied to Yerd’s sites and services.

Proposed Solution

Proposal: Unified Log Explorer for Yerd

I have a general idea on how I might build this.


Summary

Add a standalone Log Explorer window that extends Yerd’s already-solid logging and debugging tools into one exploratory surface for file-based logs — local, offline, and tied to Yerd’s sites and services.

Yerd already gives developers strong primitives: live Dumps/Inspect for runtime telemetry, quick About → Logs for host diagnostics, Services → View logs for instance output, and a Diagnostics export for support. This proposal unifies and levels up file log access — especially per-site Laravel logs — while keeping those surfaces in place.

The new window would tail:

  • Laravel site logs (storage/logs/laravel.log and daily files) — natural next step alongside Dumps
  • Service instance logs (Redis, MySQL, Reverb, etc.) — same data as today, richer presentation
  • Host logs (GUI session log, daemon rolling log, spawn/repair trails) — same sources as About → Logs

The UX goal: search narrows, exploration reveals structure — site dropdown, facets, click-to-filter, and copy-for-debug, in the same spirit as the Dumps viewer and Mail viewer.


Building on the current system

Yerd’s logging story is already purposeful — each surface is tuned for a specific job:

Surface What it does well today How Log Explorer extends it
Dumps / Inspect Live runtime telemetry — queries, dumps, requests, log writes via the PHP extension Stays as-is. Explorer adds Open Inspect for jumping between file logs and live telemetry.
About → Logs Fast GUI + daemon tail; ideal for copy/paste when filing bugs Stays as-is. Explorer surfaces the same host logs under System, plus site logs. Tray Open Logs can open the explorer for day-to-day use.
Services → View logs Reliable live tail of instance log files Same backend; View logs opens the explorer scoped to that service with search and facets.
About → Diagnostics Complete support snapshot with ERROR extraction Unchanged — different workflow, still the right tool for support bundles.

The main additive capability: browsing storage/logs/laravel.log per site in the GUI — a natural complement to Dumps (which captures log writes at runtime) and a common ask for Laravel day-to-day debugging.


Proposed solution

A standalone auxiliary window (logs label, #/logs-window) — same lifecycle pattern as the Mail viewer and Dumps window:

  • Frameless chrome + TitleBar
  • Hidden on close, revealed on demand
  • show_logs_window Tauri command (like show_mails_window / show_dumps_window)

Design reference

The HTML mockup is the source of truth for layout and interactions until ported to Vue. It uses fixture data only (no daemon).

Preview: open log-explorer-mockup.html in a browser. Toggle light/dark bottom-right.

Window layout

Image

Toolbar actions (icon + visible label + tooltip)

Control Purpose
Live Live tail indicator (poll-based, ~1s)
Pin on top setAlwaysOnTop — same as Dumps window
Reveal file Show active log file in Finder
Open Inspect Jump to Dumps/Inspect window
Clear filters Reset search, facets, and chip filters

Icons: Lucide (lucide-vue-next in app; mockup uses Lucide 0.460 CDN).

Site selector (primary scope)

Site dropdown in the toolbar (existing Select component):

Group Options Behavior
All All sources Unified stream across sites + services + host
Sites blog.test, shop.test, … Scope to that site; Laravel sites with log files listed first
System Yerd daemon, Yerd GUI, Services Host and service logs

When one site is selected, show a Log file sub-select (laravel.log, laravel-YYYY-MM-DD.log, …). Default: laravel.log if present, else most recently modified file in storage/logs/.

Deep links: #/logs-window?site=blog&file=laravel
Entry preselect: Sites ⋯ → View logs opens with that site already selected.

Facets and filtering

Sidebar facet groups (counts update with visible window):

  • Level — error, warning, info, debug
  • Channel — local, stack, yerdd, redis, …
  • Site — only when “All sources” is selected
  • Exception — extracted class name when parseable

Toolbar:

  • Text search across message, stack, exception
  • regex and invert toggles
  • Presets — e.g. “Last 15 min errors”, “Database exceptions”, “Warnings only”

Click-to-filter: clicking a level, site, or exception chip on a row adds that value as a filter.

Log entries

  • Parsed Laravel lines: timestamp, level, channel, message
  • Multi-line stack traces collapsed by default, expand on row click
  • Unparseable lines still shown as raw text (never drop data)

Copy for debugging (errors only)

Each ERROR row gets a Copy button that copies a formatted block:

  • Optional “Help me debug this:” prefix (toolbar toggle debug prefix, on by default)
  • Site/source, log file, time, level, channel, exception, request ID (if present)
  • Full message + stack trace
  • Recent context — up to 2 prior lines from the same site before the error

Toast: “Copied to clipboard”.

Relationship to existing UI

Existing Approach
Dumps / Inspect Keep — runtime telemetry; explorer links to it
About → Diagnostics Keep — support export unchanged
About → Logs modal Keep — add Open Log Explorer as a sibling action
Job progress logs Keep — inline in wizards
Services → View logs Enhance — same logs, opens explorer scoped to service
Tray → Open Logs Enhance — opens explorer (All sources) for everyday debugging

Technical approach

Fits entirely within the current Yerd stack (Tauri v2 + Vue 3 + yerdd IPC). Extends patterns already proven in Mail, Dumps, and About logs — no new platform dependencies for v1.

Prior art in the repo

  • Window shell: mail_window.rs, DumpsWindowView.vue, MailsViewerView.vue
  • Live incremental poll: DumpsWindowView (listDumps + read offset), usePoll
  • File tail: logging.rs tail_file_bounded, service_logs in services.rs
  • Site discovery: laravel_detect.rs, Sites IPC with document_root + is_laravel

New daemon IPC (yerd-ipc + bin/yerdd/src/logs.rs)

list_log_sources  →  site + service sources with path, size, modified time
tail_log          →  { source_id, offset, max_bytes, max_lines }
                     →  entries[], offset, truncated

Source ID examples:

  • site:blog:laravel{document_root}/storage/logs/laravel.log
  • service:redis / service:reverb:blog → existing instance_log_path
  • host:gui / host:daemon → Tauri-side tail (merge in list_log_sources)

Laravel parser (Rust, best-effort):

[2026-07-21 22:15:03] local.ERROR: message …

Regex header + continuation lines until next [timestamp] → stack attachment.

Reuse / extract shared tail helper from logging.rs alongside existing service_logs read logic.

New GUI pieces

Path Role
apps/yerd-gui/src/views/LogsWindowView.vue Main view (port from mockup)
apps/yerd-gui/src/composables/useLogTail.ts Incremental tail poll (byte offset)
apps/yerd-gui/src/composables/useLogScope.ts Site dropdown + file sub-select + URL hash
apps/yerd-gui/src/lib/logFilter.ts, logFacets.ts Client filtering
apps/yerd-gui/src-tauri/src/log_window.rs show_logs_window, host log tail merge
bin/yerdd/src/logs.rs Discovery, tail, parse

Modify: router.ts, tauri.conf.json, tray.rs, SitesView.vue, ServicesView.vue, ipc/client.ts, ipc/types.ts, wire_stability.rs, docs/guide/desktop-app.md

Design constraints (aligned with today)

  • Poll-based tail — same discipline as About, Services, and Dumps
  • Byte-bounded reads (256KB–1MB) — same safety model as tail_file_bounded
  • Log rotation — reset read offset when file shrinks or identity changes
  • request_id cross-link to Dumps — when the log line includes it in context (Phase 3)

Implementation phases

Phase 0 — Mockup sign-off (done in HTML; port to Vue next)

  • Static HTML mockup with interactions (log-explorer-mockup.html)
  • feat/log-explorer branch from main
  • Port to LogsWindowView.vue + logFixtures.ts (no IPC)
  • Draft PR for visual review before daemon work

Gate: layout, site dropdown, facets, copy-for-debug approved before Phase 1.

Phase 1 — Unified foundation (MVP)

  • list_log_sources + tail_log IPC + wire stability tests
  • logs window in tauri.conf.json + show_logs_window
  • Live tail for site + service + host logs
  • Site dropdown + log file sub-select
  • Text search, facet sidebar, collapsible stacks
  • Copy error with context + debug prefix toggle
  • Entry points: tray Open Logs, Sites ⋯ View logs, Services ⋯ View logs
  • About → “Open Log Explorer” (alongside existing Logs modal)

Phase 2 — Exploratory debugging

  • Laravel parser hardening + unit fixtures
  • Regex + invert filters
  • Built-in presets
  • Click-to-filter polish

Phase 3 — Observability polish (optional / follow-up)

  • Timeline histogram (deferred from v1 — mockup had placeholder bars; cut unless wanted)
  • Saved searches in gui-settings.json + shareable URL hash state
  • Dumps cross-link by request_id
  • Repeated-error de-emphasis / spike highlighting

Entry points

From Action
Tray → Open Logs show_logs_window() — All sources
Sites card ⋯ View logs — site pre-selected
Services card ⋯ View logs — opens explorer scoped to service
About Open Log Explorer (alongside existing Logs button)
Dumps settings Open Log Explorer beside Show Dumps
Tray panel Activity Open Logs (if tray-panel PR lands)

Optional later: ⌘⇧L / Ctrl+Shift+L to open explorer.


Test plan

  • Open from tray: window appears, All sources, live tail updates when laravel.log appended
  • Site dropdown: switch to blog.test — only that site’s entries; log file sub-select appears
  • Services: View logs opens explorer scoped to redis (or chosen service)
  • Facets: filter by ERROR — list narrows; counts update
  • Click exception chip — adds filter
  • Expand/collapse stack trace on multi-line error
  • Copy on error: clipboard contains message, stack, context; debug prefix respects toggle
  • Pin on top: window stays above others
  • Reveal file: opens Finder at log path
  • Large log file: tail stays bounded; no UI freeze
  • Log rotation: new file / truncated file recovers without a stale read offset
  • Daemon down: host logs still readable; site logs show graceful empty/error state
  • cargo test + wire_stability for new IPC shapes
  • npm run typecheck + npm run test for filter/facet/copy helpers
  • About → Logs, Diagnostics, and Dumps/Inspect still work as before

Out of scope (this proposal)

  • Replacing Dumps/Inspect or Diagnostics
  • Remote / cloud log shipping
  • True tail -f subprocess or WebSocket streaming
  • Full-text index across all historical logs on disk
  • Timeline histogram in v1 (unless explicitly pulled into Phase 1)

Success criteria

MVP is done when:

  1. One window live-tails site + service + host logs using the same reliable tail model as today
  2. Site dropdown switches context in one click
  3. Laravel errors show level/channel facets and collapsible stacks
  4. Copy produces a paste-ready debug block with optional prefix
  5. Tray Open Logs opens the explorer for everyday use; About → Logs remains for quick host copy
  6. Dumps/Inspect and Diagnostics behave exactly as today

ngle feature branch with commits grouped by phase if preferred.


Alternatives Considered

No response

Which part of Yerd?

None

Additional Context

Proposal: Unified Log Explorer for Yerd

Status: Design / pitch — ready for implementation
Branch: feat/log-explorer (from main)
Interactive mockup: log-explorer-mockup.html — open in a browser, no build required
Related: Builds on Dumps/Inspect, About logs, and Services logs — extends the existing toolkit


Summary

Add a standalone Log Explorer window that extends Yerd’s already-solid logging and debugging tools into one exploratory surface for file-based logs — local, offline, and tied to Yerd’s sites and services.

Yerd already gives developers strong primitives: live Dumps/Inspect for runtime telemetry, quick About → Logs for host diagnostics, Services → View logs for instance output, and a Diagnostics export for support. This proposal unifies and levels up file log access — especially per-site Laravel logs — while keeping those surfaces in place.

The new window would tail:

  • Laravel site logs (storage/logs/laravel.log and daily files) — natural next step alongside Dumps
  • Service instance logs (Redis, MySQL, Reverb, etc.) — same data as today, richer presentation
  • Host logs (GUI session log, daemon rolling log, spawn/repair trails) — same sources as About → Logs

The UX goal: search narrows, exploration reveals structure — site dropdown, facets, click-to-filter, and copy-for-debug, in the same spirit as the Dumps viewer and Mail viewer.


Building on the current system

Yerd’s logging story is already purposeful — each surface is tuned for a specific job:

Surface What it does well today How Log Explorer extends it
Dumps / Inspect Live runtime telemetry — queries, dumps, requests, log writes via the PHP extension Stays as-is. Explorer adds Open Inspect for jumping between file logs and live telemetry.
About → Logs Fast GUI + daemon tail; ideal for copy/paste when filing bugs Stays as-is. Explorer surfaces the same host logs under System, plus site logs. Tray Open Logs can open the explorer for day-to-day use.
Services → View logs Reliable live tail of instance log files Same backend; View logs opens the explorer scoped to that service with search and facets.
About → Diagnostics Complete support snapshot with ERROR extraction Unchanged — different workflow, still the right tool for support bundles.

The main additive capability: browsing storage/logs/laravel.log per site in the GUI — a natural complement to Dumps (which captures log writes at runtime) and a common ask for Laravel day-to-day debugging.


Proposed solution

A standalone auxiliary window (logs label, #/logs-window) — same lifecycle pattern as the Mail viewer and Dumps window:

  • Frameless chrome + TitleBar
  • Hidden on close, revealed on demand
  • show_logs_window Tauri command (like show_mails_window / show_dumps_window)

Design reference

The HTML mockup is the source of truth for layout and interactions until ported to Vue. It uses fixture data only (no daemon).

Preview: open log-explorer-mockup.html in a browser. Toggle light/dark bottom-right.

Window layout

┌─────────────────────────────────────────────────────────────┐
│ TitleBar — Yerd Log Explorer                                │
├──────────┬──────────────────────────────────────────────────┤
│ Facets   │ [Site ▾] [Log file ▾]  Search  regex  invert     │
│ Level    │  debug prefix  Presets…                        │
│ Channel  ├──────────────────────────────────────────────────┤
│ Site*    │  Log entries (newest first)                      │
│ Exception│  — collapsible stack traces                      │
│          │  — copy button on errors                         │
└──────────┴──────────────────────────────────────────────────┘
* Site facet group hidden when a single site is selected in the dropdown

Toolbar actions (icon + visible label + tooltip)

Control Purpose
Live Live tail indicator (poll-based, ~1s)
Pin on top setAlwaysOnTop — same as Dumps window
Reveal file Show active log file in Finder
Open Inspect Jump to Dumps/Inspect window
Clear filters Reset search, facets, and chip filters

Icons: Lucide (lucide-vue-next in app; mockup uses Lucide 0.460 CDN).

Site selector (primary scope)

Site dropdown in the toolbar (existing Select component):

Group Options Behavior
All All sources Unified stream across sites + services + host
Sites blog.test, shop.test, … Scope to that site; Laravel sites with log files listed first
System Yerd daemon, Yerd GUI, Services Host and service logs

When one site is selected, show a Log file sub-select (laravel.log, laravel-YYYY-MM-DD.log, …). Default: laravel.log if present, else most recently modified file in storage/logs/.

Deep links: #/logs-window?site=blog&file=laravel
Entry preselect: Sites ⋯ → View logs opens with that site already selected.

Facets and filtering

Sidebar facet groups (counts update with visible window):

  • Level — error, warning, info, debug
  • Channel — local, stack, yerdd, redis, …
  • Site — only when “All sources” is selected
  • Exception — extracted class name when parseable

Toolbar:

  • Text search across message, stack, exception
  • regex and invert toggles
  • Presets — e.g. “Last 15 min errors”, “Database exceptions”, “Warnings only”

Click-to-filter: clicking a level, site, or exception chip on a row adds that value as a filter.

Log entries

  • Parsed Laravel lines: timestamp, level, channel, message
  • Multi-line stack traces collapsed by default, expand on row click
  • Unparseable lines still shown as raw text (never drop data)

Copy for debugging (errors only)

Each ERROR row gets a Copy button that copies a formatted block:

  • Optional “Help me debug this:” prefix (toolbar toggle debug prefix, on by default)
  • Site/source, log file, time, level, channel, exception, request ID (if present)
  • Full message + stack trace
  • Recent context — up to 2 prior lines from the same site before the error

Toast: “Copied to clipboard”.

Relationship to existing UI

Existing Approach
Dumps / Inspect Keep — runtime telemetry; explorer links to it
About → Diagnostics Keep — support export unchanged
About → Logs modal Keep — add Open Log Explorer as a sibling action
Job progress logs Keep — inline in wizards
Services → View logs Enhance — same logs, opens explorer scoped to service
Tray → Open Logs Enhance — opens explorer (All sources) for everyday debugging

Technical approach

Fits entirely within the current Yerd stack (Tauri v2 + Vue 3 + yerdd IPC). Extends patterns already proven in Mail, Dumps, and About logs — no new platform dependencies for v1.

`

New daemon IPC (yerd-ipc + bin/yerdd/src/logs.rs)

list_log_sources  →  site + service sources with path, size, modified time
tail_log          →  { source_id,maxbytes, max_lines }
                     →  entries[], truncated

Source ID examples:

  • site:blog:laravel{document_root}/storage/logs/laravel.log
  • service:redis / service:reverb:blog → existing instance_log_path
  • host:gui / host:daemon → Tauri-side tail (merge in list_log_sources)

Laravel parser (Rust, best-effort):

[2026-07-21 22:15:03] local.ERROR: message …

Regex header + continuation lines until next [timestamp] → stack attachment.

Reuse / extract shared tail helper from logging.rs alongside existing service_logs read logic.

New GUI pieces

Path Role
apps/yerd-gui/src/views/LogsWindowView.vue Main view (port from mockup)
apps/yerd-gui/src/composables/useLogTail.ts Cursor-based poll
apps/yerd-gui/src/composables/useLogScope.ts Site dropdown + file sub-select + URL hash
apps/yerd-gui/src/lib/logFilter.ts, logFacets.ts Client filtering
apps/yerd-gui/src-tauri/src/log_window.rs show_logs_window, host log tail merge
bin/yerdd/src/logs.rs Discovery, tail, parse

Modify: router.ts, tauri.conf.json, tray.rs, SitesView.vue, ServicesView.vue, ipc/client.ts, ipc/types.ts, wire_stability.rs, docs/guide/desktop-app.md

Design constraints (aligned with today)

  • Poll-based tail — same discipline as About, Services, and Dumps
  • Byte-bounded reads (256KB–1MB) — same safety model as tail_file_bounded
  • Log rotation — reset cursor when file shrinks or identity changes
  • request_id cross-link to Dumps — when the log line includes it in context (Phase 3)

Implementation phases

Phase 0 — Mockup sign-off (done in HTML; port to Vue next)

  • Static HTML mockup with interactions (log-explorer-mockup.html)
  • feat/log-explorer branch from main
  • Port to LogsWindowView.vue + logFixtures.ts (no IPC)
  • Draft PR for visual review before daemon work

Gate: layout, site dropdown, facets, copy-for-debug approved before Phase 1.

Phase 1 — Unified foundation (MVP)

  • list_log_sources + tail_log IPC + wire stability tests
  • logs window in tauri.conf.json + show_logs_window
  • Live tail for site + service + host logs
  • Site dropdown + log file sub-select
  • Text search, facet sidebar, collapsible stacks
  • Copy error with context + debug prefix toggle
  • Entry points: tray Open Logs, Sites ⋯ View logs, Services ⋯ View logs
  • About → “Open Log Explorer” (alongside existing Logs modal)

Phase 2 — Exploratory debugging

  • Laravel parser hardening + unit fixtures
  • Regex + invert filters
  • Built-in presets
  • Click-to-filter polish

Phase 3 — Observability polish (optional / follow-up)

  • Timeline histogram (deferred from v1 — mockup had placeholder bars; cut unless wanted)
  • Saved searches in gui-settings.json + shareable URL hash state
  • Dumps cross-link by request_id
  • Repeated-error de-emphasis / spike highlighting

Entry points

From Action
Tray → Open Logs show_logs_window() — All sources
Sites card ⋯ View logs — site pre-selected
Services card ⋯ View logs — opens explorer scoped to service
About Open Log Explorer (alongside existing Logs button)
Dumps settings Open Log Explorer beside Show Dumps
Tray panel Activity Open Logs (if tray-panel PR lands)

Optional later: ⌘⇧L / Ctrl+Shift+L to open explorer.


Test plan

  • Open from tray: window appears, All sources, live tail updates when laravel.log appended
  • Site dropdown: switch to blog.test — only that site’s entries; log file sub-select appears
  • Services: View logs opens explorer scoped to redis (or chosen service)
  • Facets: filter by ERROR — list narrows; counts update
  • Click exception chip — adds filter
  • Expand/collapse stack trace on multi-line error
  • Copy on error: clipboard contains message, stack, context; debug prefix respects toggle
  • Pin on top: window stays above others
  • Reveal file: opens Finder at log path
  • Large log file: tail stays bounded; no UI freeze
  • Log rotation: new file / truncated file recovers without stale cursor
  • Daemon down: host logs still readable; site logs show graceful empty/error state
  • cargo test + wire_stability for new IPC shapes
  • npm run typecheck + npm run test for filter/facet/copy helpers
  • About → Logs, Diagnostics, and Dumps/Inspect still work as before

Out of scope (this proposal)

  • Replacing Dumps/Inspect or Diagnostics
  • Remote / cloud log shipping
  • True tail -f subprocess or WebSocket streaming
  • Full-text index across all historical logs on disk
  • Timeline histogram in v1 (unless explicitly pulled into Phase 1)

Success criteria

MVP is done when:

  1. One window live-tails site + service + host logs using the same reliable tail model as today
  2. Site dropdown switches context in one click
  3. Laravel errors show level/channel facets and collapsible stacks
  4. Copy produces a paste-ready debug block with optional prefix
  5. Tray Open Logs opens the explorer for everyday use; About → Logs remains for quick host copy
  6. Dumps/Inspect and Diagnostics behave exactly as today

Suggested PR strategy

  1. Draft PR 1 — Vue mockup port (fixtures only) for UX sign-off
  2. PR 2 — IPC + daemon logs.rs + wire tests
  3. PR 3 — Wire mockup to live data + entry points + docs

Or a single feature branch with commits grouped by phase if preferred.


Open questions for implementer

  1. Timeline histogram — include in v1 or defer to Phase 3? (Mockup has static placeholder; product lean is defer.)
  2. Non-Laravel sites — expose storage/logs/*.log when directory exists, or Laravel-only for v1?
  3. Keyboard shortcut — ship ⌘⇧L in v1 or follow-up?
  4. Tray panel — coordinate with feat/tray-panel for Activity → Open Logs link?

Attachments

File Description
log-explorer-mockup.html Interactive static prototype
Plan (internal) enhanced_log_explorer — full technical plan with IPC shapes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions