Skip to content

Repository files navigation

vtail

Latest Version on PackagistTotal DownloadsLicense

Vendor-aware tail for Laravel logs with stack trace formatting and vendor frame collapsing.

vtail is an interactive TUI for real-time log monitoring that intelligently collapses vendor frames in stack traces, making it easier to focus on your application code.

vtail screenshot

Features

  • Vendor Frame Collapsing - Hide vendor frames in stack traces with a single keypress, collapsing them into a count like #… (5 vendor frames)
  • Smart Stack Trace Formatting - Stack traces are visually grouped with borders and dimmed decorations
  • Line Wrapping Toggle - Switch between wrapped and truncated long lines
  • Follow Mode - Auto-scroll to new log entries as they arrive
  • Keyboard Navigation - Vim-style navigation (j/k/g/G) plus page up/down
  • ANSI & Unicode Aware - Proper handling of colored output and wide characters (CJK, emoji)

Vendor Frame Collapsing

Press v to toggle vendor frame visibility. Consecutive vendor frames collapse into a single line showing the count:

Vendor frames shown (default)

[2024-01-15 10:23:45] production.ERROR: User not found
{"exception":"[object] (App\\Exceptions\\UserNotFoundException(code: 0):
User not found at /app/Services/UserService.php:128)
╭─Trace────────────────────────────────────────────────────────────────────╮
│ #00 /app/Http/Controllers/UserController.php(42): store() │
│ #01 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(693) │
│ #02 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(670) │
│ #03 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(636) │
│ #04 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(183) │
│ #05 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(119) │
│ #06 /app/Services/UserService.php(128): createUser() │
╰══════════════════════════════════════════════════════════════════════════╯

Vendor frames hidden (press v)

[2024-01-15 10:23:45] production.ERROR: User not found
{"exception":"[object] (App\\Exceptions\\UserNotFoundException(code: 0):
User not found at /app/Services/UserService.php:128)
╭─Trace────────────────────────────────────────────────────────────────────╮
│ #00 /app/Http/Controllers/UserController.php(42): store() │
│ #… (5 vendor frames) │
│ #06 /app/Services/UserService.php(128): createUser() │
╰══════════════════════════════════════════════════════════════════════════╯

Your application code stands out while framework internals are summarized.

Installation

composer global require soloterm/vtail

Or install locally in your project:

composer require --dev soloterm/vtail

Usage

vtail [options] <file>
Options:
-h, --help Show help message
-n <lines> Initial tail count (default: 100)
--no-vendor Start with vendor frames hidden
--no-wrap Start with line wrapping disabled
Examples:
vtail storage/logs/laravel.log
vtail --no-vendor laravel.log
vtail -n 50 /var/log/app.log

Hotkeys

KeyAction
vToggle vendor frame visibility
wToggle line wrapping
tTruncate (clear) the log file
f / SpaceToggle follow mode
j / DownScroll down one line
k / UpScroll up one line
gJump to top
GJump to bottom (enables follow)
PgUpPage up
PgDnPage down
q / Ctrl-CQuit

Architecture

 vtail Data Flow
============================================================
┌─────────────────┐
│ laravel.log │
│ (Log File) │
└────────┬────────┘
│
│ tail -f -n <count>
▼
┌─────────────────┐
│ Tail Process │ Subprocess via proc_open()
│ (Non-blocking) │ with async pipe reading
└────────┬────────┘
│
│ Raw log lines (strings)
▼
┌─────────────────────────────────────────────────────────┐
│ LogFormatter │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • Detect stack frames (#N pattern) │ │
│ │ • Detect exception headers │ │
│ │ • Identify vendor frames (/vendor/ path) │ │
│ │ • Group consecutive vendor frames │ │
│ │ • Apply ANSI styling (borders, dim text) │ │
│ │ • Wrap/truncate to terminal width │ │
│ └───────────────────────────────────────────────────┘ │
└────────┬────────────────────────────────────────────────┘
│
│ Line objects with metadata
▼
┌─────────────────────────────────────────────────────────┐
│ LineCollection │
│ ┌───────────────────────────────────────────────────┐ │
│ │ • Store formatted Line objects │ │
│ │ • Apply hideVendor filter │ │
│ │ • Collapse vendor groups → "#... (N frames)" │ │
│ │ • Calculate scroll position adjustments │ │
│ └───────────────────────────────────────────────────┘ │
└────────┬────────────────────────────────────────────────┘
│
│ Display-ready strings
▼
┌─────────────────────────────────────────────────────────┐
│ Application │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Event Loop (25ms / 40 FPS) │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ 1. Collect output from tail subprocess │ │ │
│ │ │ 2. Process lines if new data or dirty flag │ │ │
│ │ │ 3. Render viewport to terminal │ │ │
│ │ │ 4. Wait for user input (25ms timeout) │ │ │
│ │ │ 5. Handle hotkey → update state → repeat │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└────────┬────────────────────────────────────────────────┘
│
│ ANSI escape sequences
▼
┌─────────────────────────────────────────────────────────┐
│ Terminal │
│ ┌───────────────────────────────────────────────────┐ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ STATUS BAR │ │ │
│ │ │ laravel.log | Lines: 247 | VENDOR: hidden │ │ │
│ │ ├─────────────────────────────────────────────┤ │ │
│ │ │ CONTENT AREA │ │ │
│ │ │ ╭─Trace──────────────────────────────────╮ │ │ │
│ │ │ │ #01 /app/Http/Controllers/Api.php(42) │ │ │ │
│ │ │ │ #… (5 vendor frames) │ │ │ │
│ │ │ │ #07 /app/Models/User.php(128) │ │ │ │
│ │ │ ╰────────────────────────────────────────╯ │ │ │
│ │ ├─────────────────────────────────────────────┤ │ │
│ │ │ HOTKEY BAR │ │ │
│ │ │ v vendor w wrap t truncate f follow │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
▲
│ Keyboard input
│
┌──────┴──────┐
│ User │
└─────────────┘

Component Overview

ComponentFileResponsibility
Applicationsrc/Application.phpEvent loop, state management, rendering
LogFormattersrc/Formatting/LogFormatter.phpParse logs, detect vendors, apply styling
LineCollectionsrc/Formatting/LineCollection.phpFilter lines, collapse vendor groups
Linesrc/Formatting/Line.phpData class for formatted lines
AnsiAwaresrc/Formatting/AnsiAware.phpANSI-safe string operations
Terminalsrc/Terminal/Terminal.phpTTY control, raw mode, cursor
KeyPressListenersrc/Input/KeyPressListener.phpHotkey bindings

Vendor Detection

A stack frame is considered "vendor" if:

  1. The path contains /vendor/ (except when BoundMethod.php is calling app code)
  2. The frame is {main} (root of execution)

Consecutive vendor frames are grouped and can be collapsed into a single line showing the count.

Requirements

  • PHP 8.1+
  • Extensions: pcntl, posix, mbstring
  • Unix-like OS (Linux, macOS)

License

MIT

Related Projects

vtail is part of the SoloTerm project:

  • Solo - All-in-one Laravel command for local development
  • Screen - Pure PHP terminal renderer
  • Dumps - Laravel command to intercept dumps
  • Grapheme - Unicode grapheme width calculator
  • Notify - PHP package for desktop notifications via OSC escape sequences
  • Notify Laravel - Laravel integration for soloterm/notify
  • TNotify - Standalone, cross-platform CLI for desktop notifications

About

No description, website, or topics provided.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages