Installation Require the package via Composer:
composer require alxishin/logs-bundle
Enable the bundle in config/bundles.php:
return [
Alxishin\LogsBundle\LogsBundle::class => ['all' => true],
];
Publish Configuration Publish the default config file:
php artisan vendor:publish --tag=logs-bundle-config
This creates config/packages/alxishin_logs.yaml.
First Use Case
Log a message to a channel (e.g., mailer) in a controller or service:
use Psr\Log\LoggerInterface;
public function sendMail(LoggerInterface $logger)
{
$logger->channel('mailer')->info('Sending email to [email protected]');
}
Verify logs appear in storage/logs/mailer.prod.log (or console in dev).
Leverage Symfony’s when@prod/when@dev syntax to define environment-specific handlers:
# config/packages/alxishin_logs.yaml
monolog:
channels: ['mailer']
handlers:
main:
type: rotating_file
max_files: 10
level: error
handler: nested
formatter: log_formatter
when@prod:
monolog:
handlers:
mailer:
type: rotating_file
max_files: 40
path: '%kernel.logs_dir%/mailer.prod.log'
channels: [mailer]
formatter: log_formatter
when@dev:
monolog:
handlers:
console:
type: console
process_psr_3_messages: false
channels: ['!event', '!doctrine', '!console']
Define channels in your config to route logs to specific handlers:
channels:
mailer:
level: debug
handlers: [mailer]
api:
level: info
handlers: [api_handler]
Use channels in your code:
$logger->channel('api')->info('API request processed');
Extend the default formatter to include additional context (e.g., request IDs):
// app/Formatter/CustomFormatter.php
use Monolog\Formatter\LineFormatter;
class CustomFormatter extends LineFormatter
{
public function __construct()
{
parent::__construct(
"[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n",
['date_format' => 'Y-m-d H:i:s']
);
}
}
Register the formatter in your config:
formatters:
log_formatter:
class: App\Formatter\CustomFormatter
Combine multiple handlers for a single channel:
handlers:
mailer:
type: fingers_crossed
action_level: error
handler: mailer_file
mailer_file:
type: rotating_file
max_files: 40
path: '%kernel.logs_dir%/mailer.prod.log'
channels: [mailer]
Undefined Handlers
The nested handler in the example is not a standard Monolog handler. Replace it with a valid handler (e.g., rotating_file, stream, or fingers_crossed).
Missing Formatter
The log_formatter must be defined in your config. If omitted, logs may appear unformatted or broken. Always validate the formatter exists:
php artisan config:dump | grep log_formatter
Channel Mismatch Logs sent to undefined channels will be ignored. Explicitly define all channels in your config:
channels:
mailer:
level: debug
handlers: [mailer]
Environment Conflicts
Ensure when@prod and when@dev configs do not override each other unintentionally. Test in staging before production.
PSR-3 Compliance
The process_psr_3_messages: false setting in the console handler may break PSR-3 compliance if other log processors rely on normalized messages. Enable it if needed:
console:
type: console
process_psr_3_messages: true
Validate Configuration Use Artisan to dump the Monolog config:
php artisan config:dump | grep monolog
Check Log Directories
Ensure kernel.logs_dir has write permissions:
ls -la storage/logs/
Log Level Overrides Temporarily override log levels for debugging:
config(['monolog.handlers.mailer.level' => 'debug']);
Handler Debugging Inspect active handlers:
$handlers = $logger->getHandlers();
dd($handlers);
Custom Handlers Extend Monolog’s handler system by creating a custom handler class and registering it in your config:
handlers:
custom_handler:
type: custom
class: App\Handlers\CustomHandler
Dynamic Channel Assignment Dynamically assign channels based on runtime conditions:
$channel = auth()->check() ? 'authenticated' : 'guest';
$logger->channel($channel)->info('User action');
Log Rotation Monitoring Add a command to monitor log file sizes:
// app/Console/Commands/CheckLogRotation.php
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
class CheckLogRotation extends Command
{
protected $signature = 'logs:check';
public function handle()
{
$logDir = storage_path('logs');
$process = new Process(['du', '-sh', $logDir]);
$process->run();
$this->info($process->getOutput());
}
}
Formatter Inheritance Extend the default formatter to add custom fields:
class ExtendedFormatter extends LineFormatter
{
public function format(array $record)
{
$record['extra']['custom_field'] = 'value';
return parent::format($record);
}
}
Log Rotation Under Load
Test log rotation with high write volumes (e.g., 10K+ messages/sec) to avoid file handle leaks. Consider using stream_handler for syslog or a dedicated log shipper.
Async Logging For high-throughput apps, pair with queue-based logging:
use Illuminate\Support\Facades\Log;
Log::channel('mailer')->queue(function ($level, $message, array $context) {
// Async processing logic
});
Log File Retention Implement log retention policies (e.g., delete logs older than 30 days) to prevent disk space issues:
// app/Console/Commands/CleanLogs.php
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
class CleanLogs extends Command
{
protected $signature = 'logs:clean';
public function handle()
{
$logDir = storage_path('logs');
$files = File::files($logDir);
foreach ($files as $file) {
if (now()->diffInDays(File::lastModifiedTime($file)) > 30) {
unlink($file);
}
}
}
}
How can I help you explore Laravel packages today?