タイトル: エラーログ出力関数
SEOタイトル: PHP エラーログ出力 (error_log / Monolog / Laravel Log) 完全ガイド
| この記事の要点 |
|
error_log 関数の基本
PHP 標準で提供される最も基本のログ関数。引数の渡し方で出力先が変わります。
// 1) 第 2 引数省略 / 0: php.ini の error_log 設定先へ
error_log("Something happened");
// 2) 第 2 引数 = 3: 指定パスにファイル追記 (★最頻出)
error_log("[" . date('c') . "] user_id=$id failed\n", 3, '/var/log/app/error.log');
// 3) 第 2 引数 = 1: メール送信
error_log("Critical error", 1, "admin@example.com");
// 4) 第 2 引数 = 4: SAPI モジュールに送る (mod_php なら Apache のエラーログ)
error_log("Send to SAPI", 4);
| 第 2 引数 | 出力先 | 備考 |
|---|---|---|
0 (省略時既定) | php.ini の error_log / SAPI のエラーログ | 本番では既定で運用 |
1 | メール送信 | 第 4 引数で追加ヘッダ。SMTP 設定が必要 |
3 | 第 3 引数で指定したファイルに追記 | ★ 任意のパスへログを出す定番 |
4 | SAPI ロガー | PHP 7.4+。Apache/Nginx のエラーログへ |
php.ini の設定
; php.ini
log_errors = On ; エラーをログに記録
error_log = /var/log/php/error.log ; 既定の出力先
error_reporting = E_ALL ; 何を記録するか
display_errors = Off ; ★ 本番では Off
display_startup_errors = Off
log_errors_max_len = 1024 ; 1 件あたりの最大バイト
; ログローテーション (logrotate 例)
; /etc/logrotate.d/php
; /var/log/php/error.log {
; daily
; rotate 30
; compress
; missingok
; notifempty
; postrotate
; kill -USR1 `cat /run/php-fpm.pid` || true
; endscript
; }
error_log で出力した内容を確認
# リアルタイム監視
tail -f /var/log/php/error.log
# 直近 100 行
tail -n 100 /var/log/php/error.log
# キーワードでフィルタ
grep -i "PDOException" /var/log/php/error.log
# Apache / nginx のエラーログ (error_log で第 2 引数 4 / 0 のときの行き先候補)
tail -f /var/log/apache2/error.log
tail -f /var/log/nginx/error.log
# PHP-FPM のエラーログ
tail -f /var/log/php-fpm/error.log
関連関数
| 関数 | 用途 |
|---|---|
error_log() | ログメッセージを出力 |
error_reporting() | 記録するエラーレベルを実行時に変更 |
set_error_handler() | 独自のエラーハンドラを設定 |
set_exception_handler() | 未捕捉例外のハンドラ |
register_shutdown_function() | 致命的エラー時に動かす後処理 |
debug_backtrace() | スタックトレースを取得 |
trigger_error() | 独自のエラーを発生させる (E_USER_NOTICE/WARNING/ERROR) |
syslog() | OS の syslog に送信 (openlog() とセット) |
独自エラーハンドラと例外ハンドラ
// すべての PHP エラーを error_log に送る
set_error_handler(function ($severity, $message, $file, $line) {
if (!(error_reporting() & $severity)) {
return false; // 抑制レベルなら無視
}
error_log("[ERROR] $message at $file:$line");
return true;
});
// 未捕捉例外
set_exception_handler(function (Throwable $e) {
error_log(sprintf(
"[EXCEPTION] %s: %s\n%s",
get_class($e),
$e->getMessage(),
$e->getTraceAsString()
));
http_response_code(500);
echo 'Internal Server Error';
});
// 致命的エラー (Out of memory 等) の最終手段
register_shutdown_function(function () {
$err = error_get_last();
if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true)) {
error_log("[FATAL] {$err['message']} at {$err['file']}:{$err['line']}");
}
});
syslog 出力
openlog("myapp", LOG_PID | LOG_PERROR, LOG_LOCAL0);
syslog(LOG_WARNING, "Suspicious login from " . $_SERVER['REMOTE_ADDR']);
syslog(LOG_ERR, "Database connection failed");
closelog();
// /var/log/syslog や /var/log/messages に流れる
// rsyslog で /var/log/myapp.log にルーティング可能
Monolog (PSR-3 準拠の定番ロガー)
本格運用ではほぼ Monolog が使われます。多彩な出力先 (file / stream / syslog / Slack / Mail / Sentry / Elasticsearch) と整形ハンドラを持ちます。
composer require monolog/monologuse Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\SlackWebhookHandler;
use Monolog\Formatter\LineFormatter;
$logger = new Logger('app');
// 日次ローテーション
$rotate = new RotatingFileHandler('/var/log/app/app.log', 30, Logger::DEBUG);
$rotate->setFormatter(new LineFormatter(
"[%datetime%] %level_name%: %message% %context% %extra%\n",
'Y-m-d H:i:s'
));
$logger->pushHandler($rotate);
// エラーは Slack にも
$logger->pushHandler(new SlackWebhookHandler(
'https://hooks.slack.com/services/...',
'#alerts',
'AppBot',
true,
null,
false,
false,
Logger::ERROR
));
// 使い方
$logger->info('user logged in', ['user_id' => 42]);
$logger->warning('slow query', ['ms' => 1200, 'sql' => $sql]);
$logger->error('payment failed', ['order_id' => $orderId, 'reason' => $e->getMessage()]);
PSR-3 LoggerInterface
PHP-FIG が定めた標準ロガーインタフェース。Monolog も Laravel Log もこれに準拠しているため、フレームワーク非依存のライブラリは LoggerInterface を依存性注入で受け取るのが定番。
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
class PaymentService {
public function __construct(private LoggerInterface $logger = new NullLogger()) {}
public function charge(int $amount): void {
$this->logger->info('charging', ['amount' => $amount]);
try {
// ...
} catch (\Throwable $e) {
$this->logger->error('charge failed', ['exception' => $e]);
throw $e;
}
}
}
// 8 つのレベル (debug, info, notice, warning, error, critical, alert, emergency)
$logger->debug('verbose');
$logger->info('normal');
$logger->notice('something noticeable');
$logger->warning('not an error, but...');
$logger->error('error happened');
$logger->critical('critical state');
$logger->alert('alert!');
$logger->emergency('system is down');
Laravel の Log ファサード
use Illuminate\Support\Facades\Log;
Log::debug('debug message');
Log::info('user signed in', ['id' => $user->id]);
Log::warning('disk almost full');
Log::error('DB connection lost', ['exception' => $e]);
Log::critical('out of memory');
// 特定チャンネルに直接
Log::channel('slack')->error('payment failed');
Log::channel('daily')->info('cron finished');
// 複数チャンネルへ同時に
Log::stack(['daily', 'slack'])->error('Critical: ...');
// 文脈付き (リクエスト ID 等を全ログに付与)
Log::withContext([
'request_id' => request()->header('X-Request-Id'),
'user_id' => auth()->id(),
]);
Log::info('order created'); // 上記 context が自動で乗る
Laravel config/logging.php の例
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'Laravel Log',
'emoji' => ':boom:',
'level' => 'critical',
],
'syslog' => [
'driver' => 'syslog',
'level' => 'debug',
],
'stderr' => [
'driver' => 'monolog',
'handler' => Monolog\Handler\StreamHandler::class,
'with' => ['stream' => 'php://stderr'],
],
],
];
本番運用の注意
- 個人情報 / 機密情報を出さない: メアド、パスワード、クレカ番号、秘密鍵
- ローテーション必須:
RotatingFileHandler/logrotate/ Laraveldailyドライバ - ログレベルを分ける: 本番は INFO 以上、開発は DEBUG
- 監視を入れる: ERROR 以上を Slack 通知 / Sentry / CloudWatch Logs Insights
- 構造化ログを推奨:
$logger->info('message', $context)の context で集計が楽に - display_errors = Off: 本番ではブラウザに出さない
FAQ
Q: error_log がどこに書かれているか分からない
A: php -i | grep error_log または phpinfo() で確認。空欄なら SAPI 既定 (Apache/Nginx のエラーログ) に流れます。
Q: error_log でファイルにパーミッションエラー
A: Web プロセス (apache / www-data / php-fpm) に対象ディレクトリの書き込み権限が必要。chown www-data:www-data /var/log/app。
Q: Monolog と Laravel Log どちらを使うべき?
A: Laravel プロジェクトなら Log ファサードで十分 (内部 Monolog)。素の PHP やライブラリでは PSR-3 ベースで Monolog を直接注入。