Introduction: Why Logging is the Foundation of a Reliable PHP Application
Logging is the process of recording events occurring in an application into a special journal (log file). For a PHP developer, proper logging is not just a "nice-to-have," but a necessity. Without it, debugging production errors turns into guesswork, and performance monitoring becomes impossible. Poorly configured logging can lead to sensitive data leaks or, conversely, to the loss of critically important information.
In this article, we will break down the best practices for logging in PHP: from choosing a library and log levels to structuring messages and security. You will learn how to turn scattered echo and var_dump statements into a professional monitoring system.
1. Choosing a Library: PSR-3 and Monolog
In the modern PHP community, the de facto standard is the Psr\\Log\\LoggerInterface (PSR-3) interface. It defines 8 methods for different logging levels: emergency, alert, critical, error, warning, notice, info, debug.
The most popular implementation of PSR-3 is the Monolog library. It is flexible, powerful, and supported by virtually all modern frameworks (Laravel, Symfony, Yii2).
Installing Monolog via Composer
composer require monolog/monologBasic Usage Example
<?phprequire 'vendor/autoload.php';
use Monolog\\Logger;use Monolog\\Handler\\StreamHandler;
// Create a logger with a channel name (e.g., 'my_app')$log = new Logger('my_app');// Add a handler: write to a file, level - INFO and above$log->pushHandler(new StreamHandler(__DIR__ . '/app.log', Logger::INFO));
// Log messages$log->info('User logged in', ['user_id' => 123]);$log->warning('Incorrect password', ['attempts' => 3]);$log->error('Database connection error', ['exception' => $e->getMessage()]);?>Note: the second argument is the context. You can pass any additional data into it: user ID, IP, stack trace, etc. Never concatenate data into the message string — use the context.
2. Logging Levels and Filtering
Using the correct levels is the foundation of a readable log. The general rule: the higher the level, the fewer messages. Here is a quick cheat sheet:
- DEBUG (100) — Debugging information for development. Usually disabled in production.
- INFO (200) — Important events: user registered, order created.
- NOTICE (250) — Normal but significant events.
- WARNING (300) — An abnormal situation, but the application continues working (e.g., a failed login attempt).
- ERROR (400) — An error requiring intervention (failed to save file).
- CRITICAL (500) — A critical error (database is unavailable).
- ALERT (550) — Immediate action is required.
- EMERGENCY (600) — The system is inoperable.
Filtering in Different Environments
In production, you should not write DEBUG messages — they create unnecessary disk load and make it difficult to find important records. Use different handlers for different environments:
<?phpuse Monolog\\Logger;use Monolog\\Handler\\StreamHandler;use Monolog\\Handler\\FingersCrossedHandler;
$log = new Logger('app');
if (getenv('APP_ENV') === 'production') { // In production, write only WARNING and above $log->pushHandler(new StreamHandler('/var/log/app/error.log', Logger::WARNING)); // FingersCrossedHandler: buffers everything, but writes only on error $log->pushHandler(new FingersCrossedHandler( new StreamHandle