Installation: Add the package via Composer in a Laravel project (though primarily designed for Symfony):
composer require symfony/monolog-bridge
Note: Laravel already includes Monolog, so this package extends its functionality.
First Use Case:
Leverage Symfony’s Monolog processors or handlers. For example, add a MemoryUsageProcessor to log memory consumption:
use Symfony\Bridge\Monolog\Processor\MemoryUsageProcessor;
$log = app('log')->getMonolog();
$log->pushProcessor(new MemoryUsageProcessor());
Where to Look First:
MemoryUsageProcessor, UidProcessor).
Symfony Processors DocsFingersCrossedHandler, SwiftMailerHandler).
Symfony Handlers DocsErrorLogger for exceptions:
use Symfony\Bridge\Monolog\Logger;
use Symfony\Component\ErrorHandler\Bridge\Monolog\ErrorLogger;
$errorLogger = new ErrorLogger($log);
set_exception_handler([$errorLogger, 'handleException']);
Structured Logging with Processors: Add context to logs dynamically (e.g., request IDs, memory usage):
// In a service provider or boot method
app('log')->getMonolog()->pushProcessor(function ($record) {
return ['user_id' => auth()->id()];
});
Conditional Logging with Handlers:
Use Symfony’s FingersCrossedHandler to log errors only after retries:
use Symfony\Bridge\Monolog\Handler\FingersCrossedHandler;
$handler = new FingersCrossedHandler(
new \Monolog\Handler\StreamHandler(storage_path('logs/errors.log'), \Monolog\Logger::ERROR),
3 // Max retries before logging
);
app('log')->getMonolog()->pushHandler($handler);
Console-Specific Logging:
Filter logs for CLI commands using ConsoleHandler:
use Symfony\Bridge\Monolog\Handler\ConsoleHandler;
$handler = new ConsoleHandler(\Monolog\Logger::DEBUG);
$handler->setFormatter(new \Monolog\Formatter\LineFormatter(
"[%datetime%] %channel%.%level_name%: %message%\n",
null,
true,
true
));
app('log')->getMonolog()->pushHandler($handler);
HTTP Request/Response Logging:
Log HTTP client interactions (e.g., Guzzle) with HttpCodeActivationStrategy:
use Symfony\Bridge\Monolog\Handler\FingersCrossedHandler;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Bridge\Monolog\Processor\HttpClientProcessor;
$client = new Psr18Client();
$log = app('log')->getMonolog();
$log->pushProcessor(new HttpClientProcessor($client));
Centralized Configuration:
Define Monolog handlers/processors in Laravel’s config/logging.php:
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['single', 'symfony'],
],
'symfony' => [
'driver' => 'monolog',
'level' => 'debug',
'handlers' => [
Symfony\Bridge\Monolog\Handler\ConsoleHandler::class,
Symfony\Bridge\Monolog\Handler\FingersCrossedHandler::class,
],
'processors' => [
Symfony\Bridge\Monolog\Processor\MemoryUsageProcessor::class,
],
],
],
Laravel Service Provider Integration: Bind Symfony’s Monolog components in a service provider:
public function register()
{
$this->app->singleton('symfony.logger', function ($app) {
return new \Symfony\Bridge\Monolog\Logger('symfony');
});
}
Exception Handling:
Replace Laravel’s default exception handler with Symfony’s ErrorLogger:
use Symfony\Component\ErrorHandler\Bridge\Monolog\ErrorLogger;
$errorLogger = new ErrorLogger(app('log')->getMonolog());
set_exception_handler([$errorLogger, 'handleException']);
Avoid DI Conflicts: Manually instantiate Symfony components instead of relying on Symfony’s DI container:
// Instead of:
// $logger = $container->get('logger');
// Use:
$logger = new \Symfony\Bridge\Monolog\Logger('app');
$logger->pushHandler(new \Monolog\Handler\StreamHandler('php://stderr'));
Leverage Laravel’s Log Facade:
Extend Laravel’s Log facade to support Symfony’s Monolog methods:
if (!class_exists('SymfonyLog')) {
class SymfonyLog extends \Illuminate\Support\Facades\Log
{
public static function getMonolog()
{
return self::getMonologInstance();
}
}
}
Testing: Mock Symfony’s Monolog components in tests:
$monolog = Mockery::mock(\Symfony\Bridge\Monolog\Logger::class);
$monolog->shouldReceive('error')->once();
$this->app->instance('symfony.logger', $monolog);
Namespace Collisions:
Symfony’s Logger class may conflict with Laravel’s Log facade. Use fully qualified names:
use Symfony\Bridge\Monolog\Logger as SymfonyLogger;
$logger = new SymfonyLogger('app');
DI Container Incompatibility:
Symfony’s DependencyInjection container won’t work with Laravel’s container. Avoid:
// ❌ Avoid this:
$container = new \Symfony\Component\DependencyInjection\Container();
Instead, manually instantiate services.
Handler Propagation:
Symfony’s ConsoleHandler may interfere with Laravel’s CLI output. Set interactive_only to true:
$handler = new \Symfony\Bridge\Monolog\Handler\ConsoleHandler();
$handler->setInteractiveOnly(true); // Prevents non-interactive logs
Circular References:
Logging objects with circular references (e.g., Eloquent models) may cause infinite loops. Use Symfony’s IgnoreHandler:
use Symfony\Bridge\Monolog\Handler\IgnoreHandler;
$handler = new IgnoreHandler(new \Monolog\Handler\StreamHandler('php://stderr'));
$handler->ignoreExceptions(true);
Performance Overhead: Processors and handlers add latency. Benchmark critical paths (e.g., request logging):
// Example: Disable processors in production for high-traffic endpoints
if (app()->environment('production')) {
app('log')->getMonolog()->popProcessor(0); // Remove first processor
}
Log Level Mismatches: Ensure log levels match between Laravel and Symfony:
// Laravel levels: debug, info, notice, warning, error, critical, alert, emergency
// Symfony levels: same as above (compatible)
app('log')->debug('This will appear in Symfony logs if level is DEBUG');
Handler Not Triggering: Verify handlers are pushed to the correct logger:
$log = app('log')->getMonolog();
$log->pushHandler($handler); // Ensure this is the right logger instance
Console Output Issues: If logs don’t appear in CLI, check:
interactive_only is set correctly.Memory Leaks:
Processors that capture runtime data (e.g., MemoryUsageProcessor) may leak memory. Clean up:
// Remove processors when no longer needed
app('log')->getMonolog()->popProcessor(0);
Environment-Specific Handlers:
Use Laravel’s config/logging.php to define environment-specific handlers:
'channels' => [
'symfony' => [
'driver' => 'monolog',
'handlers' => app()->environment('local')
? [\Symfony\Bridge\Monolog\Handler\ConsoleHandler::class]
: [\Monolog\Handler\StreamHandler::class],
],
],
**
How can I help you explore Laravel packages today?