opcodesio/log-viewer
Fast, beautiful log viewer for Laravel. Browse and manage log files, search and filter entries by level, share links, use dark mode, and preview mails. Supports multiple hosts, Horizon logs, and an API for folders, files, and entries.
Installation:
composer require opcodesio/log-viewer
php artisan log-viewer:publish
resources/views/vendor/log-viewer and config/log-viewer.php.Access:
/log-viewer in your Laravel app (e.g., https://your-app.test/log-viewer).First Use Case:
/log-viewer, filter by error level, and search for keywords like 500 or DatabaseException to quickly locate relevant logs.Log Exploration:
storage/logs/) and other supported log types (Horizon, Nginx, Redis, etc.) via the UI.debug, info, error), date range, or custom keywords./\d{3} Error/).Advanced Features:
QueryException by clicking the trace to see the SQL query and affected files.Log::channel('mail')->info($mailable) with rendered HTML previews./api/log-viewer/folders, /api/log-viewer/files, or /api/log-viewer/entries.$logs = Http::get('http://your-app.test/api/log-viewer/entries?filter[level]=error');
Configuration:
config/log-viewer.php:
'parsers' => [
'custom-json' => \Opcodes\LogViewer\Parsers\JsonParser::class,
],
config/log-viewer.php:
'frontend' => [
'defaults' => [
'itemsPerPage' => 50,
'darkMode' => true,
],
],
Multi-Host Environments:
log-viewer.php to include remote hosts:
'includes' => [
'remote' => [
'type' => 'remote',
'url' => 'https://staging-app.test',
'auth' => [
'basic' => [
'username' => 'user',
'password' => 'pass',
],
],
],
],
config/log-viewer.php to share logs across machines:
'useLocalIpInFileFolderHash' => false,
Asset Management:
php artisan log-viewer:publish --tag=assets
config/log-viewer.php:
'assets_path' => public_path('custom-log-viewer-assets'),
Permission Issues:
www-data, nginx) has read access to log files:
sudo chmod -R 755 storage/logs
sudo setfacl -R -m u:www-data:rX storage/logs
APP_URL is accessible and credentials are correct.Log Format Mismatches:
config/log-viewer.php or extend \Opcodes\LogViewer\Parsers\ParserInterface:
'parsers' => [
'json' => \App\Services\JsonLogParser::class,
],
namespace App\Services;
use Opcodes\LogViewer\Parsers\ParserInterface;
class JsonLogParser implements ParserInterface {
public function parse(string $line): array {
return json_decode($line, true) ?: ['message' => $line];
}
}
Octane Compatibility:
routes/web.php:
Route::middleware(['web', 'log-viewer'])
->group(function () {
\Opcodes\LogViewer\LogViewerServiceProvider::routes();
});
Indexing Delays:
// Poll logs every 5 seconds
setInterval(() => {
axios.get('/api/log-viewer/entries?updated_after=' + new Date().toISOString())
.then(response => updateUI(response.data));
}, 5000);
Horizon Logs:
config/log-viewer.php:
'includes' => [
'horizon' => [
'type' => 'horizon',
'path' => storage_path('logs/horizon.log'),
],
],
API Debugging:
curl -X GET http://your-app.test/api/log-viewer/folders
APP_URL is empty (fixed in v3.21.0).Log Index Corruption:
php artisan log-viewer:rebuild-indexes
Stack Trace Issues:
debugbar is installed and configured in Laravel:
composer require barryvdh/laravel-debugbar
config/log-viewer.php:
'stack_trace' => [
'base_path' => app_path(),
],
Custom Log Types:
\Opcodes\LogViewer\LogTypes\LogType to support new formats (e.g., Elasticsearch logs).namespace App\LogTypes;
use Opcodes\LogViewer\LogTypes\LogType;
class ElasticsearchLogType extends LogType {
protected $name = 'elasticsearch';
protected $parser = \App\Services\ElasticsearchParser::class;
protected $filePattern = '*.json';
}
config/log-viewer.php:
'log_types' => [
\App\LogTypes\ElasticsearchLogType::class,
],
UI Customization:
resources/views/vendor/log-viewer:
resources/views/vendor/log-viewer/layouts/app.blade.php to customize the layout.resources/js/log-viewer.js.// resources/js/log-viewer.js
Vue.component('log-viewer-custom-button', {
template: `<button @click="downloadLogs">Download Logs</button>`,
methods: {
downloadLogs() {
axios.get('/api/log-viewer/entries', { responseType: 'blob' })
.then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'logs.zip');
document.body.appendChild(link);
link.click();
});
}
}
How can I help you explore Laravel packages today?