0

PSR-4 — Autoloading Standard

Intermediate5 min read·php-12-008
psrframework

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):

  1. debug: Detailed debug information. Application internals, query results, variable dumps.
  2. info: Interesting events. "User logged in", "Order placed".
  3. notice: Normal but significant events. "Database connection reestablished".
  4. warning: Exceptional occurrences that are not errors but should be investigated. Deprecated API usage, slow queries.
  5. error: Runtime errors that don't require immediate action but should be logged and monitored. Individual request failures.
  6. critical: Critical conditions. "Component unavailable", "Application component failed entirely".
  7. alert: Action must be taken immediately. "Entire website down", "Database unreachable".
  8. 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
<?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']);