On a live site, nobody is looking at the screen when something fails. Logs are how you find out afterwards.
The settings
In php.ini (or at runtime with ini_set):
| Setting | Meaning |
|---|---|
log_errors | On writes errors to a log |
error_log | The file path (or leave empty for the server’s log) |
display_errors | Off in production |
error_reporting | E_ALL |
log_errors = On
error_log = /var/log/php/app-errors.log
display_errors = Off
error_reporting = E_ALL
With Apache, PHP errors normally go to the Apache error log if error_log is not set.
Write your own entries: error_log()
<?php
$log = __DIR__ . "/app.log";
error_log("User 42 failed to log in\n", 3, $log);
error_log("Payment gateway timeout\n", 3, $log);
echo file_get_contents($log);
User 42 failed to log in
Payment gateway timeout
The second argument 3 means “append to the file named in the third argument”. With no extra arguments, error_log("text") writes to the configured log.
Add context
A log line without a time or details is hard to use. Add them.
<?php
function logError(string $message, array $context = []): void
{
$line = sprintf(
"[%s] %s %s",
date("Y-m-d H:i:s"),
str_replace(["\r", "\n"], " ", $message),
$context ? json_encode($context) : ""
);
file_put_contents("events.log", $line . PHP_EOL, FILE_APPEND | LOCK_EX);
}
logError("Login failed", ["user_id" => 42, "ip" => "203.0.113.5"]);
echo substr(file_get_contents("events.log"), 22);
Login failed {"user_id":42,"ip":"203.0.113.5"}
(substr(..., 22) skips the timestamp so the output stays the same every time.)
What to log
- Exceptions, with the message, file, line and trace.
- Failed logins and other security events.
- Slow or failed calls to other services.
- Unexpected input.
What never to log
- Passwords, even wrong ones.
- Credit card numbers, API keys, tokens.
- Full personal data. Log an ID instead.
- Raw user input without cleaning new lines (log injection).
Log levels
Libraries such as Monolog (installed with Composer) add levels: debug, info, warning, error, critical. You can then keep a quiet log in production and a detailed one in development. See Logging.
Look at the log
On a server, tail -f shows new lines as they arrive:
tail -f /var/log/php/app-errors.log
Keep logs under control
Logs grow forever. Use log rotation (the logrotate tool, or a library that writes one file per day), and delete old files.