In PSR-3, what are the eight log levels in Psr\Log\LogLevel, and how do you choose between them?
answer
- borrowed from RFC 5424 syslog
- one method per level, plus log()
- emergency, alert, critical at the top
- warning is not an error
- unknown level: Psr\Log\InvalidArgumentException
basics
~20 sPSR-3 defines eight RFC 5424 levels: emergency, alert, critical, error, warning, notice, info and debug, each with its own LoggerInterface method. Choose by urgency: from system unusable, through failures and oddities, down to routine events and diagnostics.
solid answer
~40 s`Psr\Log\LogLevel` holds eight string constants taken from the RFC 5424 syslog levels, and `LoggerInterface` has one method per level plus `log($level, $message, $context)`. From most to least severe: `emergency` (system unusable), `alert` (act now: whole site down, database unavailable), `critical` (a component unavailable, an unexpected exception), `error` (runtime errors that need no immediate action but should be monitored), `warning` (exceptional but not errors, such as deprecated API use), `notice` (normal but significant), `info` (interesting events such as a user login) and `debug` (detailed diagnostics). Calling `log()` with a level constant must behave like the matching method, and a level the implementation does not know must throw `Psr\Log\InvalidArgumentException`. PSR-3 does not define a minimum-level filter; that is the implementation's configuration.
code
php · 20 lines<?php
declare(strict_types=1);
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
function reportRateFetch(LoggerInterface $logger, string $pair, ?\Throwable $failure, bool $servedStale): void
{
if ($failure === null) {
$logger->info('Fetched rate for {pair}', ['pair' => $pair]);
return;
}
// Stale fallback kept the caller working: warning. No fallback: error.
$level = $servedStale ? LogLevel::WARNING : LogLevel::ERROR;
$logger->log($level, 'Rate fetch for {pair} failed', [
'pair' => $pair,
'exception' => $failure,
]);
}go deeper
Recite the eight levels in order, emergency down to debug, and give one example each for error, warning and info.
Explain log() versus the level methods, the InvalidArgumentException rule for unknown levels, and why filtering is outside PSR-3.
Set level conventions for a codebase or library so on-call alerts stay meaningful: what pages someone, what only feeds dashboards, what is debug noise.
Balance log volume, cost and signal across services: which levels are retained, sampled or dropped, and how level discipline is enforced in review.
## Where the levels come from **PSR-3** is the PHP-FIG logger interface, shipped in the `psr/log` package. Its levels are not invented: they are the eight severities of **RFC 5424**, the syslog protocol. `Psr\Log\LogLevel` exposes them as string constants, and `Psr\Log\LoggerInterface` has one method for each. | Constant | Method | Specification's description and example | |---|---|---| | `LogLevel::EMERGENCY` = `'emergency'` | `emergency()` | system is unusable | | `LogLevel::ALERT` = `'alert'` | `alert()` | action must be taken immediately: entire website down, database unavailable | | `LogLevel::CRITICAL` = `'critical'` | `critical()` | critical conditions: application component unavailable, unexpected exception | | `LogLevel::ERROR` = `'error'` | `error()` | runtime errors that do not need immediate action but should be logged and monitored | | `LogLevel::WARNING` = `'warning'` | `warning()` | exceptional occurrences that are not errors: deprecated APIs, poor use of an API | | `LogLevel::NOTICE` = `'notice'` | `notice()` | normal but significant events | | `LogLevel::INFO` = `'info'` | `info()` | interesting events: user logs in, SQL logs | | `LogLevel::DEBUG` = `'debug'` | `debug()` | detailed debug information | ## The ninth method `log($level, $message, array $context = [])` takes the level as its first argument. The specification sets two rules: 1. Calling `log()` with one of the eight constants **must** have the same result as calling the level-specific method. 2. Calling it with a level the specification does not define **must** throw `Psr\Log\InvalidArgumentException` if the implementation does not know that level. Users should not use custom levels unless they are sure the implementation supports them. `log()` is useful when the level is computed at runtime, for example from a configuration value or from how bad an outcome was. ## Choosing a level in practice Take a library that fetches exchange rates from a remote service and caches them: - **debug**: the raw response size and timings, useful only while investigating; - **info**: rates fetched and cached for a currency pair; - **notice**: the remote service returned a rate outside the usual range, but valid; - **warning**: the fetch failed, and the library served a stale cached rate instead; - **error**: the fetch failed and there was no cached rate to fall back on, so the call failed; - **critical**: every configured rate source is unavailable, so the component is out of action; - **alert** and **emergency**: rarely right inside a library, because a library seldom knows that the whole system is down; they belong to the application's own health checks. The common thread is **who has to act and how soon**: debug and info need no one, warning and error are for monitoring and later triage, critical and above should wake someone. ## What PSR-3 leaves to the implementation PSR-3 defines how to *write* log records, not how to route them. It does not define: - a minimum level below which records are dropped; - where records go (files, standard error, a log collector); - formatting or timestamps. Those are configured on the concrete logger the application installs. A library that depends only on `LoggerInterface` should therefore log honestly at the right level and let the application decide what to keep. ## Helpers shipped with the interface - `Psr\Log\AbstractLogger` and `Psr\Log\LoggerTrait` let an implementation write only `log()`; the eight level methods forward to it. Because traits cannot implement interfaces, a class using the trait must still declare `implements LoggerInterface`. - `Psr\Log\NullLogger` discards everything, as a fallback when no logger is provided. - `Psr\Log\LoggerAwareInterface` has a single `setLogger()` method, and `LoggerAwareTrait` implements it with a `$this->logger` property. ## Mistakes interviewers listen for - Logging every caught exception at `alert` or `emergency`, which trains people to ignore the pager. - Treating `warning` as "a small error": the specification describes it as something exceptional that is **not** an error. - Inventing a level such as `'trace'` and passing it to `log()`, which a conforming implementation that does not know it must reject with `Psr\Log\InvalidArgumentException`.
- What must a PSR-3 logger do when log() receives the level 'trace'?If the implementation does not know that level, it must throw `Psr\Log\InvalidArgumentException`. The eight `LogLevel` constants are the only levels the standard defines, and users should not pass custom levels unless they know the installed logger supports them.
- Does PSR-3 let a library set the minimum level that gets recorded?No. `LoggerInterface` only has methods for writing records. Filtering by level, choosing destinations and formatting are configured on the concrete logger by the application. A library should log at the honest level and leave filtering to whoever installs it.
saying these in an interview costs you the question
- Says PSR-3 has five levels: debug, info, warning, error and fatal.
- Describes warning as a minor error rather than a non-error exceptional event.
- Believes LoggerInterface lets callers set a minimum log level.
- Logs every caught exception at emergency so it gets noticed.
- Thinks log() silently accepts any custom level string.