Заметки
Ошибка PHP в бизнес-процессе может скрыть факт запуска
Почему при ошибке в действии PHP-код бизнес-процесса может не остаться понятного следа, что нужная часть процесса запускалась.
Если действие PHP код падает до записи лога или служебной отметки,
потом сложно понять, запускался ли этот участок бизнес-процесса вообще.
Суть проблемы
В бизнес-процессе может быть действие с произвольным PHP-кодом. Если этот код завершается ошибкой, выполнение обрывается раньше, чем процесс успевает записать диагностическую информацию.
Суть
Проблема не только в самой ошибке PHP. Проблема в том, что после неё может быть непонятно, дошёл ли бизнес-процесс до этого действия.
Типовая цепочка выглядит так:
- бизнес-процесс запускается по сделке, лиду или элементу списка;
- процесс доходит до действия
PHP код; - внутри PHP возникает ошибка;
- следующие действия не выполняются;
- служебная переменная, лог или отметка о старте не записываются;
- при разборе кажется, что бизнес-процесс мог вообще не запускаться.
Поэтому старт критичного участка лучше фиксировать до рискованного кода, а не после него.
Причина
Действие PHP код выполняется как часть бизнес-процесса. Если внутри него возникает
ошибка, код после неё уже не выполняется.
Ошибка обрывает выполнение
Если сначала выполнить рискованное действие, а потом записать лог, то при ошибке лог может просто не появиться.
<?php
use Bitrix\Main\Loader;
if (!Loader::includeModule('crm')) {
throw new RuntimeException('Не удалось подключить модуль CRM');
}
$deal_id = (int)$this->GetVariable('deal_id');
updateDealData($deal_id);
$this->WriteToTrackingService('PHP-действие выполнено');
/**
* Обновляет данные сделки.
*/
function updateDealData(int $deal_id): void
{
if ($deal_id <= 0) {
throw new RuntimeException('Не передан ID сделки');
}
$deal = new CCrmDeal(false);
$fields = [
'UF_CRM_EXAMPLE_FIELD' => 'Y',
];
$is_updated = $deal->Update($deal_id, $fields);
if (!$is_updated) {
throw new RuntimeException('Не удалось обновить сделку');
}
}
Если ошибка произойдёт внутри updateDealData, строка с
WriteToTrackingService может не выполниться.
Решение
Перед рискованным кодом нужно заранее записывать техническую отметку: в лог бизнес-процесса, переменную, поле элемента или отдельный файл логов.
Фиксировать старт заранее
Минимальный порядок такой:
- получить нужные входные данные;
- записать в лог, что PHP-действие началось;
- завернуть основной код в
try/catch; - при ошибке записать текст ошибки;
- после успешного выполнения записать отдельный лог завершения.
Так потом видно три состояния: действие не запускалось, действие стартовало и упало, действие стартовало и завершилось.
Пример для PHP-кода
<?php
use Bitrix\Main\Loader;
const CRM_MODULE_ID = 'crm';
const LOG_PREFIX = '[BP PHP]';
if (!Loader::includeModule(CRM_MODULE_ID)) {
throw new RuntimeException('Не удалось подключить модуль CRM');
}
$deal_id = (int)$this->GetVariable('deal_id');
$this->WriteToTrackingService(buildProcessLogMessage('Действие PHP код запущено', [
'DEAL_ID' => $deal_id,
]));
try {
updateDealData($deal_id);
$this->WriteToTrackingService(buildProcessLogMessage('Действие PHP код завершено', [
'DEAL_ID' => $deal_id,
]));
} catch (Throwable $exception) {
$this->WriteToTrackingService(buildProcessLogMessage('Ошибка в действии PHP код', [
'DEAL_ID' => $deal_id,
'ERROR' => $exception->getMessage(),
]));
throw $exception;
}
/**
* Обновляет данные сделки.
*/
function updateDealData(int $deal_id): void
{
if ($deal_id <= 0) {
throw new RuntimeException('Не передан ID сделки');
}
$deal = new CCrmDeal(false);
$fields = [
'UF_CRM_EXAMPLE_FIELD' => 'Y',
];
$is_updated = $deal->Update($deal_id, $fields);
if (!$is_updated) {
throw new RuntimeException('Не удалось обновить сделку');
}
}
/**
* Формирует сообщение для журнала бизнес-процесса.
*/
function buildProcessLogMessage(string $message, array $context): string
{
$log_data = [
'MESSAGE' => LOG_PREFIX . ' ' . $message,
'CONTEXT' => $context,
];
return print_r($log_data, true);
} Если нужно не только увидеть ошибку в журнале, но и остановить бизнес-процесс как ошибочный, после записи лога можно повторно выбросить исключение. Это зависит от того, как должен вести себя конкретный процесс.