Журналы, ошибки и метрики

Три канала, по которым платформа рассказывает о том, что произошло: журнал ошибок (что сломалось), аварийное завершение (как правильно прервать модуль) и Log API (сколько что стоило). Визуальная панель со статистикой текущего запроса описана отдельно — в разделе «Отладчик».

Журнал ошибок

Ошибки времени выполнения и фатальные ошибки PHP перехватывает units/melbis.php и передаёт в функцию MELBIS_halt — она выводит страницу с описанием ошибки и останавливает выполнение скрипта (см. «Корневые скрипты»).

Запись в файл включается наличием файла error.save в корне проекта. Пока его нет, сообщение никуда не попадает. Когда он есть, ошибки дописываются в core/log/melbis/front.log с подробным контекстом: URL, IP, User-Agent, данные POST и сессии.

Туда же с типом Cron Error попадают задачи планировщика, которые не ответили или вернули код не из диапазона 2xx, — независимо от того, задан ли задаче собственный журнал (см. «Планировщик задач»).

Все журналы платформы собраны в одной папке core/log: в подпапке melbis лежат front.log (ошибки витрины) и back.log (ошибки бэк-офиса), в cron — журналы задач планировщика. В стандартной установке туда же смонтированы журналы apache и nginx, так что вся история одного запроса читается в одном месте.

Папка лежит внутри корня сайта. Доступ к ней должен быть закрыт правилом .htaccess — иначе журналы вместе со всем, что в них попало, доступны всему интернету. Полный .htaccess — в разделе «Корневые скрипты».

Платформа журналы не чистит — ни по размеру, ни по возрасту. В стандартной установке всю папку core/log обслуживает системный logrotate: посуточная нарезка, сжатие, хранение неделю. На нестандартном развёртывании ротацию нужно настроить самостоятельно.

Отдельно от журнала ошибок настраивается журнал действий пользователей — параметр MELBIS_USER_LOG в config.json (см. «Конфигурация»).


Аварийное завершение модуля

Stop() снимает служебную блокировку компиляции кеша текущего модуля. Halt($mFile, $mError, $mInfo = '') делает то же самое и дополнительно отправляет сообщение в систему отчётов об ошибках.

if ( !$answer )
{
    MELBIS()->Halt(__FILE__, 'Payment gateway is unreachable', $order_id);

    return MELBIS()->TplFinal($tpl, 'error');
}

Нужны там, где модуль прерывает работу нештатно, — при обрыве связи с внешним сервисом, при неверных входных данных в обработчике формы. Без снятия блокировки следующий запрос к этому модулю будет считать, что его кеш всё ещё компилируется другим процессом, и ждать напрасно. Ни один из методов не прерывает выполнение PHP сам по себе — выйти из функции модуля нужно самостоятельно. Живой пример со Stop() — подмена страницы на 404 в разделе «Служебные методы».


Программный сбор статистики (Log API)

Помимо визуальной панели, платформа предоставляет программный доступ к статистике работы модулей. Это удобно для автоматизированного мониторинга: отправки алертов при деградации производительности, записи метрик в БД для последующего анализа, или выявления проблемных модулей в условиях реального трафика.

LogOn() / LogOff() — включить и выключить сбор статистики. В отличие от визуального отладчика, Log API не выводит ничего на страницу и может использоваться в продакшне:

// В корневом скрипте, до Run():
MELBIS()->LogOn();

MELBIS()->Run($entry_point, $entry_param);
MELBIS()->Publish();

// После Run() статистика собрана — можно анализировать

LogUnitsList() — возвращает массив всех модулей, задействованных при генерации страницы:

$modules = MELBIS()->LogUnitsList();
// ['melbis_base_page' => true, 'melbis_cataloge' => true, ...]

LogUnitStat($unitName) — возвращает накопленную статистику по модулю. Ключи массива:

Ключ Описание
run_cnt Количество реальных выполнений модуля
smart_cnt Количество Smart-запусков
trick_cnt Количество отдач Trick-кеша
pause_cnt Количество отдач кеша из состояния паузы
valid_cnt Количество отдач валидного кеша
lazy_cnt Количество Lazy-подстановок
request Суммарное количество вызовов
total_time Суммарное время выполнения модуля (сек)
query_count Количество SQL-запросов за один вызов
query_count_total Суммарное количество SQL-запросов
query_sum_time Суммарное время SQL-запросов
query_max_time Время самого долгого SQL-запроса
query_max_query Текст самого долгого SQL-запроса
query_max_params Параметры самого долгого SQL-запроса

LogUnitProp($unitName) — возвращает конфигурацию модуля: таблицы зависимостей, настройки кеша (Base, Trick, Smart), флаги entry_point, lazy_load, список подключённых библиотек и зарегистрированных колбэков.

Пример: алерт при замедлении модуля

MELBIS()->LogOn();

MELBIS()->Run($entry_point, $entry_param);
MELBIS()->Publish();

// Проверить каждый модуль
foreach ( MELBIS()->LogUnitsList() as $mod => $tmp )
{
    $stat = MELBIS()->LogUnitStat($mod);

    // Пропустить модули, результат которых взят из кеша
    if ( ($stat['run_cnt'] ?? 0) == 0 ) continue;

    // Алерт если модуль выполнялся дольше 1 секунды
    if ( ($stat['total_time'] ?? 0) > 1 )
    {
        $params = json_encode($stat['query_max_params'] ?? []);
        error_log("[SLOW MODULE] {$mod}{$stat['total_time']}s, "
                . "SQL: {$stat['query_max_query']}, params: {$params}");
    }
}

Пример: запись статистики в БД для долгосрочного анализа

MELBIS()->LogOn();
MELBIS()->Run($entry_point, $entry_param);
MELBIS()->Publish();

foreach ( MELBIS()->LogUnitsList() as $mod => $tmp )
{
    $stat = MELBIS()->LogUnitStat($mod);
    if ( ($stat['run_cnt'] ?? 0) == 0 ) continue;

    MELBIS()->SqlQuery(__LINE__,
        "INSERT INTO {DBNICK}_log
            SET name = :NAME, date_time = NOW(),
                runtime = :RUNTIME, sql_time = :SQLTIME,
                sql_count = :SQLCNT, sql_max = :SQLMAX,
                sql_text = :SQLTEXT",
        [
            'name'    => $mod,
            'runtime' => $stat['total_time'],
            'sqltime' => $stat['query_sum_time'] ?? 0,
            'sqlcnt'  => $stat['query_count_total'] ?? 0,
            'sqlmax'  => $stat['query_max_time'] ?? 0,
            'sqltext' => $stat['query_max_query'] ?? '',
        ]
    );
}

Накапливая такие данные за несколько дней, можно выявить модули с систематически высоким потреблением ресурсов, найти корреляцию между нагрузкой и деградацией производительности, и принять взвешенное решение об оптимизации.