この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:T
更新日時:2026-06-11 07:10:02

タイトル: ログファイルにエラーを出力する方法
SEOタイトル: PHPログ出力完全ガイド(error_log/Monolog/PSR-3/Laravel/logrotate)

この記事の要点
  • PHP 標準: error_log("msg")php.inierror_log 指定先へ出力
  • 個別ファイルへ: error_log("msg", 3, "/var/log/app.log")
  • set_error_handler / set_exception_handler全エラーを横取り
  • 本格運用は Monolog(PSR-3 準拠)。複数 handler / formatter / processor を組み合わせ
  • Laravel: Log::info() / Log::error()config/logging.php で channel 定義
  • ファイル肥大化対策に logrotate、または Monolog の RotatingFileHandler
  • 本番では Sentry / Datadog / CloudWatch へ送信、検索・アラート・トレーシングを統合

方法1: PHP 標準の error_log()

最も手軽な方法。php.ini の設定 or 直接ファイル指定でログを書きます。

// (1) php.ini の error_log 指定先へ出力
error_log("ユーザー登録失敗: id=$id");

// (2) ファイル指定(第2引数 3 = ファイル追記)
error_log("ユーザー登録失敗: id=$id\n", 3, "/var/log/app.log");

// (3) メール送信(運用ではほぼ使わない)
error_log("緊急", 1, "admin@example.com");

// 変数の中身を文字列化
error_log(print_r($data, true));
error_log(var_export($data, true));
error_log(json_encode($data, JSON_UNESCAPED_UNICODE));

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

; date 関数のタイムゾーン(ログ時刻に影響)
date.timezone = Asia/Tokyo

権限

# 書き込み権限を付与(Apache の場合)
sudo mkdir -p /var/log/php
sudo chown www-data:www-data /var/log/php
sudo chmod 750 /var/log/php

# nginx-php-fpm の場合
sudo chown www-data:www-data /var/log/php  # ディストリ依存

方法2: set_error_handler でカスタム処理

PHP のエラー・例外・致命的エラーをすべてフックして独自処理する基盤:

// すべての E_NOTICE / E_WARNING / E_DEPRECATED を捕捉
set_error_handler(function (int $errno, string $msg, string $file, int $line) {
    error_log(sprintf("[ERROR %d] %s at %s:%d", $errno, $msg, $file, $line));
    // false を返すと PHP 標準処理も継続
    return false;
});

// 未捕捉の例外をフック
set_exception_handler(function (\Throwable $e) {
    error_log(sprintf("[EXCEPTION] %s\n%s", $e->getMessage(), $e->getTraceAsString()));
    http_response_code(500);
    echo "Internal Server Error";
});

// 致命的エラー(パースエラー以外)も検知
register_shutdown_function(function () {
    $err = error_get_last();
    if ($err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
        error_log(sprintf("[FATAL] %s at %s:%d", $err['message'], $err['file'], $err['line']));
    }
});

方法3: Monolog(業界標準)

PSR-3 準拠の高機能ロガー。Symfony / Laravel が内部で使っているデファクト:

composer require monolog/monolog
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\SlackWebhookHandler;
use Monolog\Formatter\JsonFormatter;
use Monolog\Processor\WebProcessor;

$log = new Logger('app');

// 通常ログ(日次ローテート、14 日保持)
$log->pushHandler(
    (new RotatingFileHandler('/var/log/app.log', 14, Logger::INFO))
        ->setFormatter(new JsonFormatter())
);

// ERROR 以上だけ Slack 通知
$log->pushHandler(
    new SlackWebhookHandler(
        'https://hooks.slack.com/services/...',
        '#alerts',
        'AppBot',
        true,
        null,
        false,
        false,
        Logger::ERROR
    )
);

// リクエスト情報を自動付与
$log->pushProcessor(new WebProcessor());

// 利用
$log->info('ユーザー登録', ['user_id' => 123]);
$log->warning('API レート制限間近', ['remaining' => 5]);
$log->error('決済エラー', ['exception' => $e]);
$log->critical('DB 接続不可');

PSR-3 ログレベル

レベル意味使い分け
debugデバッグ詳細開発時のみ
info通常情報正常動作の記録(ログイン等)
notice注目に値する異常ではないが記録すべき
warning警告異常だが処理は継続可能
errorエラー処理失敗、即対応不要
critical致命的サービス品質悪化、要調査
alert緊急即対応必要
emergencyシステム停止システム使用不可

Laravel での Log facade

use Illuminate\Support\Facades\Log;

Log::info('注文作成', ['order_id' => $order->id, 'user_id' => $user->id]);
Log::warning('在庫低下', ['product' => $product->sku, 'stock' => 3]);
Log::error('決済失敗', ['exception' => $e]);

// channel 指定
Log::channel('payments')->info('Stripe success', ['amount' => 1500]);

// 一時的にスタック
Log::stack(['single', 'slack'])->error('障害');

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' => env('LOG_LEVEL', 'debug'),
            'days' => 14,
        ],
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => 'error',
        ],
        'payments' => [
            'driver' => 'daily',
            'path' => storage_path('logs/payments.log'),
            'days' => 30,
        ],
    ],
];

ログローテーション

logrotate(Linux 標準)

# /etc/logrotate.d/app
/var/log/app.log {
    daily
    rotate 14
    compress
    delaycompress
    missingok
    notifempty
    create 0640 www-data www-data
    sharedscripts
    postrotate
        # PHP-FPM のログ再オープン
        systemctl reload php8.3-fpm > /dev/null 2>&1 || true
    endscript
}

Monolog の RotatingFileHandler

アプリ内でローテーション完結したい場合(コンテナ環境で便利):

// 日次ローテート、過去 14 日保持
$handler = new RotatingFileHandler('/var/log/app.log', 14);

// ファイル名は app-YYYY-MM-DD.log で自動生成

外部サービス連携

サービス用途連携方法
Sentry例外/エラー追跡sentry/sentry-laravel / handler 追加
DatadogAPM + ログ統合DD_LOGS_INJECTION=true + tail エージェント
CloudWatch LogsAWS ログ集約FluentBit / CloudWatch Agent
Papertrail / LogglySaaS ログ管理syslog 転送
Elasticsearch + KibanaOSS ログ検索Filebeat → Elasticsearch
// Sentry の例(Laravel)
// composer require sentry/sentry-laravel
// .env: SENTRY_LARAVEL_DSN=https://...@sentry.io/...

// config/logging.php に channel 追加
'sentry' => [
    'driver' => 'sentry',
    'level' => 'error',
],

// 利用は通常通り
Log::channel('sentry')->error('決済失敗', ['user' => $user->id]);

本番運用のチェックリスト

  • display_errors=Off / log_errors=On(ユーザに stack trace を見せない)
  • ✅ ログファイルは Web から見えない場所 (/var/log/ 等) に
  • ✅ logrotate or RotatingFileHandler で無制限肥大化を防ぐ
  • 個人情報 / トークン / クレカ番号をログに残さない(マスキング)
  • ✅ Sentry / Datadog でアラート設定(CRITICAL ≧ 即 Slack 通知)
  • ✅ ログレベルを環境で切替(dev=debug, prod=info)
  • 構造化ログ(JSON)で機械検索しやすく

FAQ

Q: echoprint_r でデバッグするのは?
A: 開発時はOK、本番では絶対NG。HTMLに出力される or fastcgi バッファ汚染。必ず error_log / Logger を経由。

Q: ログが大量で重い
A: ① ログレベルを上げる(prod=warning 以上)② 非同期出力(Monolog の BufferHandler + キュー)③ 外部サービスへ送信。

Q: スタックトレースをログに残したい
A: $logger->error('msg', ['exception' => $e]) で Monolog が自動展開。または $e->getTraceAsString()