soloterm / vtail
Vendor-aware tail for Laravel logs with stack trace formatting and vendor frame collapsing
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/soloterm/vtail
Requires
- php: ^8.1
- ext-mbstring: *
- ext-pcntl: *
- ext-posix: *
- soloterm/grapheme: ^1
- soloterm/screen: @dev
- symfony/console: ^6.0|^7.0
Requires (Dev)
- phpunit/phpunit: ^10.5|^11
This package is not auto-updated.
Last update: 2025-12-17 20:34:17 UTC
README
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.
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
| Key | Action |
|---|---|
v |
Toggle vendor frame visibility |
w |
Toggle line wrapping |
t |
Truncate (clear) the log file |
f / Space |
Toggle follow mode |
j / Down |
Scroll down one line |
k / Up |
Scroll up one line |
g |
Jump to top |
G |
Jump to bottom (enables follow) |
PgUp |
Page up |
PgDn |
Page down |
q / Ctrl-C |
Quit |
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
| Component | File | Responsibility |
|---|---|---|
| Application | src/Application.php |
Event loop, state management, rendering |
| LogFormatter | src/Formatting/LogFormatter.php |
Parse logs, detect vendors, apply styling |
| LineCollection | src/Formatting/LineCollection.php |
Filter lines, collapse vendor groups |
| Line | src/Formatting/Line.php |
Data class for formatted lines |
| AnsiAware | src/Formatting/AnsiAware.php |
ANSI-safe string operations |
| Terminal | src/Terminal/Terminal.php |
TTY control, raw mode, cursor |
| KeyPressListener | src/Input/KeyPressListener.php |
Hotkey bindings |
Vendor Detection
A stack frame is considered "vendor" if:
- The path contains
/vendor/(except whenBoundMethod.phpis calling app code) - 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
