PSR-4 — Autoloading Standard
Concept
PSR-3 defines a LoggerInterface that standardizes how logging works in PHP. Any library that accepts a Psr\Log\LoggerInterface parameter can work with Monolog, Laravel's logger, or any other compliant logger — without coupling to a specific implementation.
The 8 log levels (from RFC 5424, lowest to highest severity):
debug: Detailed debug information. Application internals, query results, variable dumps.info: Interesting events. "User logged in", "Order placed".notice: Normal but significant events. "Database connection reestablished".warning: Exceptional occurrences that are not errors but should be investigated. Deprecated API usage, slow queries.error: Runtime errors that don't require immediate action but should be logged and monitored. Individual request failures.critical: Critical conditions. "Component unavailable", "Application component failed entirely".alert: Action must be taken immediately. "Entire website down", "Database unreachable".emergency: System unusable.
Context array: Every PSR-3 method accepts a $context array for structured data. $logger->error('User login failed', ['user_id' => 42, 'ip' => '1.2.3.4']). The {key} placeholder syntax interpolates context into messages: $logger->info('User {user} logged in', ['user' => 'alice']).
Monolog: The most popular PSR-3 implementation. Used by Laravel, Symfony, and others. Supports handlers (where to write), formatters (how to format), and processors (add context to all messages).
Code Example
<?php
declare(strict_types=1);
use Psr\Log\LoggerInterface;
// Accepting PSR-3 logger — framework-agnostic
class OrderService
{
public function __construct(
private readonly LoggerInterface $logger,
) {}
public function place(array $order): void
{
$this->logger->info('Order placement started', [
'user_id' => $order['user_id'],
'item_count' => count($order['items']),
]);
try {
$result = $this->processOrder($order);
$this->logger->info('Order placed successfully', [
'order_id' => $result['id'],
]);
} catch (\Exception $e) {
$this->logger->error('Order placement failed', [
'user_id' => $order['user_id'],
'exception' => $e::class,
'message' => $e->getMessage(),
]);
throw $e;
}
}
}
// Monolog setup (without Laravel)
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SlackWebhookHandler;
use Monolog\Formatter\JsonFormatter;
$logger = new Logger('app');
// Write to file
$fileHandler = new StreamHandler('/var/log/app.log', Logger::DEBUG);
$fileHandler->setFormatter(new JsonFormatter());
$logger->pushHandler($fileHandler);
// Write errors to Slack
$logger->pushHandler(
new SlackWebhookHandler(
webHookUrl: 'https://hooks.slack.com/...',
channel: '#alerts',
level: Logger::ERROR
)
);
// In Laravel — just use the Log facade or inject LoggerInterface
// Laravel auto-resolves Psr\Log\LoggerInterface from the container
use Illuminate\Support\Facades\Log;
Log::info('User logged in', ['user_id' => 42]);
Log::error('Something failed', ['context' => 'important']);
// Placeholder interpolation
$logger->warning('User {username} exceeded rate limit', ['username' => 'alice']);