draw/log
Lightweight Laravel logging helper package providing a simple API to write structured log entries and streamline application debugging. Integrates with Laravel’s logger, supports common log levels, and keeps logging consistent across your services.
Installation
composer require draw/log
psr/log is installed (included by default in Laravel).First Use Case: Structured Logging
Replace basic Log::debug() with structured logs:
use Draw\Log\Logger;
// Inject via Laravel's DI or manually
$logger = new Logger();
$logger->info('User action', [
'user_id' => 123,
'action' => 'login',
'metadata' => ['ip' => $request->ip(), 'user_agent' => $request->userAgent()]
]);
Quick Win: Event-Driven Logging
Attach logging to Symfony events (e.g., KernelEvents::REQUEST):
use Draw\Log\Event\LogEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventDispatcher;
$dispatcher = new EventDispatcher();
$dispatcher->addListener(KernelEvents::REQUEST, function (RequestEvent $event) {
$logger = new Logger();
$logger->dispatch(new LogEvent('Request started', [
'method' => $event->getRequest()->getMethod(),
'path' => $event->getRequest()->getPathInfo()
]));
});
Leverage Laravel’s Service Provider
Bind the logger in AppServiceProvider:
public function register()
{
$this->app->singleton(Logger::class, function ($app) {
return new Logger($app['log']); // Extend Laravel's logger
});
}
Contextual Logging
$logger->withContext(['user_id' => auth()->id()])
->error('Payment failed', ['amount' => $request->amount]);
Event-Driven Logging
EventDispatcher.// In a controller
$logger->dispatch(new LogEvent('Order created', ['order_id' => $order->id]));
// In a subscriber
$dispatcher->addListener(LogEvent::class, function (LogEvent $event) {
// Process log entry (e.g., send to external API)
});
OrderCreated event).Handler Chaining
use Draw\Log\Handler\EnrichHandler;
$handler = new EnrichHandler(new StreamHandler(storage_path('logs/app.log')));
$handler->setEnricher(function ($record) {
$record['custom_field'] = 'value';
return $record;
});
$logger->pushHandler($handler);
Dependency Injection (DI)
draw/dependency-injection to manage loggers.// config/draw.php
'loggers' => [
'app' => [
'class' => \Draw\Log\Logger::class,
'arguments' => ['@logger'], // Laravel's logger
],
];
Laravel-Specific:
Log::getMonolog() to inject custom handlers:
use Illuminate\Log\LogManager;
LogManager::extend('custom', function () {
$monolog = new Monolog\Logger('custom');
$monolog->pushHandler(new \Draw\Log\Handler\CustomHandler());
return $monolog;
});
Log::channel('custom')->info(...) in controllers.Symfony Integration:
Log facade with Symfony’s LoggerInterface:
use Psr\Log\LoggerInterface;
public function __construct(private LoggerInterface $logger) {}
draw/log's logger via Symfony’s DI container.Testing:
LoggerInterface in PHPUnit:
$mockLogger = $this->createMock(LoggerInterface::class);
$mockLogger->expects($this->once())
->method('info')
->with('Test log', ['key' => 'value']);
$this->app->instance(LoggerInterface::class, $mockLogger);
PHP 8.5+ Requirement
Draw Ecosystem Lock-in
draw/dependency-injection and draw/user-bundle may complicate maintenance.draw/* bundles):
// Avoid this if not using draw/ecosystem
$logger = new \Draw\Log\Logger($this->app['draw.di.container']);
Monolog Handler Conflicts
SingleHandler).$logger->pushHandler(new \Draw\Log\Handler\CustomHandler());
EventDispatcher Overhead
$dispatcher->dispatch(new LogEvent($record), LogEvent::PRIORITY_LOW);
Structured Log Parsing
$logger->setFormatter(new \Monolog\Formatter\JsonFormatter());
Log Not Appearing?
$logger->getHandlers(); // Verify handlers are attached
processors are not filtering records:
$logger->pushProcessor(function ($record) {
return $record; // Ensure no silent drops
});
Circular References in Logs
var_export or custom serializers:
$logger->info('User', [
'data' => json_decode(json_encode($user), true)
]);
Performance Bottlenecks
$handler = new \Monolog\Handler\AsyncHandler(new StreamHandler('app.log'));
$logger->pushHandler($handler);
Custom Handlers
\Draw\Log\Handler\AbstractHandler to create reusable handlers:
class DatabaseHandler extends AbstractHandler
{
public function write(array $record): void
{
DB::table('logs')->insert([
'message' => $record['message'],
'context' => json_encode($record['context']),
'level' => $record['level'],
]);
}
}
Log Formatters
\Monolog\Formatter\FormatterInterface for custom output:
class CustomJsonFormatter extends JsonFormatter
{
public function format(array $record): string
{
$record['extra']['custom_field'] = 'value';
return parent::format($record);
}
}
Event Listeners
LogEvent for post-processing:
$dispatcher->addListener(LogEvent::class, function (LogEvent $event) {
if ($event->getLevel() === LogLevel::ERROR) {
// Send alert to Slack/PagerDuty
}
});
Logger Middleware
namespace App\Http\Middleware;
use Draw\Log\Logger;
use Closure;
class LogMiddleware
{
public function __construct(private
How can I help you explore Laravel packages today?