Заметки
Ошибка PHP в бизнес-процессе может скрыть факт запуска
Почему после ошибки в PHP-действии бизнес-процесса может быть непонятно, запускался ли нужный участок кода.
Если действие PHP код падает до записи лога, потом может быть непонятно:
бизнес-процесс дошёл до этого места или нет.
Проблема
В бизнес-процессе есть действие PHP код. Внутри него выполняется логика:
обновление CRM, проверка данных, запись в поля, отправка запроса или другой код.
Если этот код падает раньше логирования, следа о запуске может не остаться.
Проблема не в том, что возникла ошибка. Проблема в том, что после неё сложно понять, дошёл ли процесс до нужного действия.
Обычно цепочка такая:
- бизнес-процесс запускается по сделке, лиду или элементу списка;
- процесс доходит до действия
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-действие дошло до этой точки.
Решение
Перед рискованным кодом нужно записать техническую отметку: в журнал бизнес-процесса, переменную, поле элемента или отдельный лог.
Порядок лучше сделать таким:
- получить входные данные;
- записать в лог, что 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);
}
В примере ошибка сначала записывается в журнал, а потом снова выбрасывается через
throw $exception. Так бизнес-процесс остаётся ошибочным, но в журнале уже есть
понятная причина.
Если процесс должен продолжать работу после ошибки, исключение можно не пробрасывать дальше. Но отметку о старте всё равно лучше писать до основного кода.