error.log рассылка
12 августа 2014 PHP 13025 просмотров
Не все ошибки может отследить разработчик, многие появляются в процессе использования ПО, в зависимости от окружения и т.д. Для меня очень важно отслеживать такие возможные баги, особенно на этапе запуска проекта. Я сильно не ухищрялся, просто периодически просматривал содержимое error.log файлов. Но проектов, которые приходится поддерживать, становится всё больше, на многих из них развернуто по несколько серверов. В итоге для меня эта операция стала довольно трудоемкой. Естественно, это дело нужно автоматизировать.

В интернете есть серьезные решения для Enterprise-проектов, но мне хотелось чего-то простого, что с легкостью можно было бы установить на любой сервер. Итак, я описал необходимые мне задачи:

1. Скрипт должен запускаться с какой-то периодичностью
2. Должен выбирать ошибки за последний период, фильтровать их
3. Отправлять на email
4. Очищать error.log файл

С первым пунктом легко справится CRON. Скрипт нашей рассылки будет обычным php-файлом, который будет вызываться планировщиком задач. Я его запускаю каждый час, чтобы быть всегда готовым быстро что-то исправить:
0 * * * * php /var/www/alm/alm.php
Потом на практике я увидел, что везде формат error.log файла разный. Это зависит или от версии Apache, или от PHP, или от сборки. Поэтому парсить строки для фильтрации было трудно. Заметил я только одно общее место, которого мне и было достаточно, а фильтрация мне была нужна для того, чтобы настроить отправляемые мне типы ошибок. Так вот, во всех PHP-ошибках будет присутствовать "PHP Warning", "PHP Notice" и т.д. Я и сделал фильтр по этим ключам. Пример конфига (config.php):
<?php
return array(
    // ApacheLM will send log to this email
    'email'           => 'alexander.plutov@gmail.com',
    // Full path to the error log file
    'errorLogFile'    => '/var/log/apache2/error.log',
    // Fetch only these error types. Comment or remove if you don't need to get some types in mail.
    'errorTypes'      => array('Warning', 'Notice', 'Fatal')
);
Сам мэйлер alm.php после себя очищает файл error.log. Не знаю, правильно это или нет, но меня напрягают эти здоровые файлы. Отправка реализована при помощи функции mail. Пока с ней проблем не было, но не исключаю, что, возможно, когда-нибудь заменю эту часть на работу с SMTP.
<?php
// Don't log possible errors in this script
error_reporting(0);

$conf = include_once 'config.php';

$records = getLogRecords($conf);
if (count($records)) {
    sendEmail($conf, $records);
}
resetErrorLog($conf);

function getLogRecords($conf) {
    $lines = array_reverse(file($conf['errorLogFile']));

    $recordsMatch = array();

    foreach ($lines as $line) {
        $line = trim($line);
        if (matchLine($conf, $line) && $line) {
            $recordsMatch[] = $line;
        }
    }

    return $recordsMatch;
}

function matchLine($conf, $line) {
    foreach ($conf['errorTypes'] as $errorType) {
        if (false !== mb_strpos($line, 'PHP ' . $errorType)) {
            return true;
        }
    }

    return false;
}

function sendEmail($conf, $records) {
    $subject = count($records) . ' errors at ' . date('Y-m-d H:i:s');
    $body    = '';
    foreach ($records as $record) {
        $body .= $record . "\n\n";
    }
    mail($conf['email'], $subject, $body);
}

function resetErrorLog($conf) {
    file_put_contents($conf['errorLogFile'], '');
}
Когда я запустил это на всех серверах, то мне в день приходило по ~50 писем, это был хороший стимул для баг фиксинга. Теперь приходит 2-3 письма в день.