Нода `Catch`: централизованный перехват ошибок в потоке
Введение в обработку ошибок: почему Debug не всегда достаточно
В процессе разработки и отладки потоков в Node-RED, нода `Debug` является незаменимым инструментом. Она позволяет инспектировать содержимое объекта `msg`, отслеживать ход выполнения логики и быстро выявлять проблемы на этапе создания сценария. Однако, как только поток переходит из стадии разработки в производственную эксплуатацию на объекте, роль и ограничения ноды `Debug` становятся очевидными.
> 🔗 Связанный материал: Подробно о работе с нодой `Debug` и её возможностями для пошаговой отладки мы говорили в уроке COURSE-06-M04-L01 "Нода Debug и панель отладки".
Основное ограничение ноды `Debug` заключается в ее предназначении: она является инструментом для интерактивной отладки инженером, а не для автоматизированного управления ошибками в реальном времени. Рассмотрим ключевые отличия:
Именно для решения этих проблем в Node-RED существует концепция централизованной обработки ошибок. Ее суть — создать единую точку в проекте, которая будет автоматически "ловить" все сбои, независимо от того, в какой части потока они произошли. Такой подход позволяет:
- Гарантировать надежность: Ни одна критическая ошибка не останется незамеченной.
- Упростить обслуживание: Вместо поиска десятков `Debug`-узлов, инженер может анализировать единый, структурированный лог ошибок.
- Реализовать автоматические реакции: При возникновении сбоя система может не просто залогировать его, но и отправить уведомление администратору, попытаться выполнить операцию повторно или переключиться на резервное оборудование.
Основным инструментом для реализации этого подхода является нода `Catch`. Это специальная нода, которая не имеет входов, но способна перехватывать ошибки, сгенерированные другими нодами в определенной области действия. Она действует как "сеть безопасности" для ваших потоков, гарантируя, что любое исключение будет поймано и обработано согласно заданной вами логике.
---
Нода Catch: Конфигурация и область действия (Scope)
Нода `Catch` находится в палитре "common" и легко узнаваема по иконке с ладонью. В отличие от большинства нод, она не имеет входного порта, так как ее задача — не обрабатывать проходящие сообщения, а реагировать на события-ошибки внутри среды выполнения Node-RED.
Панель конфигурации
Двойной клик по ноде открывает ее окно настроек, которое содержит один ключевой параметр — Scope (Область действия). Этот параметр определяет, ошибки каких нод будет перехватывать данный экземпляр ноды `Catch`.
| Область действия (Scope) | Описание | Преимущества | Недостатки |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| All nodes on current tab (По умолчанию) | Перехватывает ошибки от абсолютно всех нод, расположенных на той же вкладке (flow), что и сама нода `Catch`. | Простота настройки, гарантирует базовую отказоустойчивость для всей логики на вкладке. | Не позволяет создать специфическую логику обработки для разных типов ошибок на одной вкладке. |
| Selected nodes | Перехватывает ошибки только от тех нод, которые вы вручную выберете. После выбора этой опции появляется кнопка, открывающая список всех нод на вкладке. | Позволяет создавать целевую обработку для критически важных или нестабильных узлов (например, HTTP-запросы, Modbus-опросы). | Требует ручной конфигурации. Если вы добавите новую ноду в поток, нужно не забыть добавить ее в список. |
| All nodes | Глобальный перехватчик. Перехватывает ошибки со всех нод во всем проекте (на всех вкладках). | Обеспечивает максимальное покрытие, "ловит" ошибки даже на тех вкладках, где забыли разместить `Catch`. | Крайне не рекомендуется, так как создает "невидимую" связь между потоками, усложняя отладку. Может привести к путанице. |
> 💡 Подсказка: Начните с правила `All nodes on current tab` для общей надежности потока и добавляйте специфичные `Catch` ноды для критически важных участков, где требуется особая логика обработки (например, Modbus-запросы или API-вызовы). Размещение одной общей ноды `Catch` на каждой вкладке является хорошей практикой и обязательным требованием для сертификации.
Опция "Catch unhandled errors in 'Done' callbacks"
Эта опция, доступная при выборе области `Selected nodes`, относится к продвинутому сценарию работы с асинхронными нодами. Некоторые ноды (например, `Exec` для выполнения команд в ОС) могут выполнять длительные операции в фоновом режиме. После завершения они могут вызвать так называемую функцию `done()`. Если в процессе этой фоновой работы произошла ошибка, и она не была обработана внутри самой ноды, эта опция позволит ноде `Catch` ее перехватить. Для большинства стандартных задач эту опцию можно оставить выключенной.
Как это работает на практике?
Представьте поток, опрашивающий Modbus-устройство:
`[Inject]` -> `[Modbus Getter]` -> `[Function]`
Если `Modbus Getter` не сможет получить ответ от устройства (например, обрыв связи), он сгенерирует ошибку. В этот момент произойдет следующее:
Теперь мы можем подключить к выходу `Catch` другую цепочку нод, которая, например, запишет информацию о сбое в базу данных MySQL и отправит уведомление по MQTT. Таким образом, мы создаем отдельный, независимый поток для управления нештатными ситуациями.
---
Структура объекта ошибки: msg.error
труктура объекта ошибки: msg.error
Ключевой момент в работе с нодой `Catch` — понимание структуры сообщения, которое она генерирует. Когда `Catch` перехватывает ошибку, она не просто передает дальше исходное сообщение, вызвавшее сбой. Вместо этого она создает совершенно новый объект `msg` со своим уникальным `_msgid`. В этот новый объект помещается исчерпывающая информация о произошедшей ошибке.
Главным свойством этого нового сообщения является `msg.error`. Это объект, который содержит в себе все детали инцидента.
> ℹ️ Информация: Оригинальное сообщение, которое обрабатывалось в момент сбоя, не теряется. Его копия сохраняется, и ссылка на него (его `_msgid`) доступна. Это позволяет при необходимости восстановить контекст, в котором произошла ошибка.
Рассмотрим типовую структуру объекта, который выходит из ноды `Catch`:
{
"_msgid": "a987b654.321fed",
"error": {
"message": "connect ECONNREFUSED 127.0.0.1:1884",
"source": {
"id": "f123e456.789abc",
"type": "mqtt out",
"name": "Отправить статус",
"count": 1
},
"stack": "Error: connect ECONNREFUSED 127.0.0.1:1884\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1187:16)"
}
}
Глубокий анализ `msg.error`
Давайте разберем каждое свойство объекта `msg.error`, чтобы понять, как его использовать для создания умной системы логирования.
- `msg.error.message` (string)
- `msg.error.source` (object)
* `source.id`: Уникальный идентификатор ноды. Самый надежный способ программно идентифицировать источник проблемы.
* `source.type`: Тип ноды (например, `function`, `http request`).
* `source.name`: Имя, которое вы задали ноде вручную. Идеально подходит для формирования понятных логов.
- `msg.error.stack` (string, опционально)
Спецификация для системы аудита (Audit Log)
При построении профессиональных систем мониторинга все ошибки должны записываться в единый журнал (Audit Log). Ниже представлена таблица обязательных полей, которые необходимо извлекать из `msg.error` для корректного ведения журнала:
| Поле в журнале | Источник в Node-RED | Описание |
| :--- | :--- | :--- |
| Timestamp | `new Date()` | Точное время возникновения ошибки. |
| Node ID | `msg.error.source.id` | Уникальный ID ноды для быстрого поиска в редакторе. |
| Node Name | `msg.error.source.name` | Человекочитаемое имя процесса/датчика. |
| Error Message| `msg.error.message` | Текст ошибки для диагностики. |
| Flow Context | `msg._msgid` | Идентификатор транзакции для связи с другими логами. |
Как использовать эту структуру?
Имея эти данные, мы можем построить очень гибкую логику. Например, в ноде `Function`, подключенной к `Catch`, мы можем подготовить данные для записи в БД или файл:
let sourceNodeName = msg.error.source.name || "Неизвестная нода";
let errorMessage = msg.error.message;
let timestamp = new Date().toISOString();
// Формируем объект для Audit Log
msg.audit = {
time: timestamp,
node: msg.error.source.id,
label: sourceNodeName,
details: errorMessage
};
// Формируем текстовую строку для лог-файла
msg.payload = `${timestamp} | ERROR | [${msg.error.source.id}] (${sourceNodeName}): ${errorMessage}`;
return msg;
Таким образом, мы извлекаем из объекта `msg.error` всю необходимую информацию согласно спецификации и используем ее для создания разных форматов сообщений для разных каналов оповещения.
Практический пример: логирование и оповещение об ошибках
рактический пример: логирование и оповещение об ошибках
Теперь давайте создадим полноценный поток, который демонстрирует централизованный перехват, логирование и оповещение. Наша задача — создать "сеть безопасности" для потока, которая будет ловить любые сбои, записывать их в файл на контроллере и отправлять сигнал тревоги по MQTT.
Сценарий: У нас есть нода `Function`, которая по какой-то причине (например, из-за ошибки в коде) генерирует исключение. Мы должны это исключение перехватить. Архитектура потока (ASCII)://========= Основной поток (может быть любым) ===========
[Inject] ----> [Function: "Проблемная логика"] ----> [Debug: "Успех"]
//========= Поток обработки ошибок на этой же вкладке ===========
[Catch] ----> [Function: "Форматировать ошибку"] --+-- (в лог) --> [File: "errors.log"]
|
+-- (в MQTT) --> [MQTT Out: "hi/system/alerts"]
Шаг 1: Создание потока, генерирующего ошибку
// Код для ноды "Проблемная логика"
// Имитируем получение данных
let temperature = "не число"; // Некорректные данные
// Пытаемся обработать
let calculatedValue = parseFloat(temperature) * 10;
if (isNaN(calculatedValue)) {
// Мы обнаружили проблему. Генерируем ошибку.
// Первый аргумент - текст ошибки.
// Второй (опциональный) - оригинальное сообщение, которое будет доступно в Catch.
node.error("Ошибка преобразования данных: значение не является числом", msg);
// Важно! После node.error() нужно остановить поток, вернув null.
return null;
}
msg.payload = calculatedValue;
return msg;
Шаг 2: Добавление ноды Catch и потока обработки
Для соответствия стандартам промышленного аудита (Audit Log), мы должны извлекать из `msg.error` строго определенный набор полей.
Спецификация полей для журнала событий:| Поле в msg.error | Описание | Обязательность |
| :--- | :--- | :--- |
| `source.id` | Уникальный ID ноды, где произошел сбой | Да |
| `source.name` | Пользовательское имя ноды | Да (или 'default') |
| `source.type` | Тип ноды (Function, MQTT и т.д.) | Да |
| `message` | Текст ошибки (человекочитаемый) | Да |
| `stack` | Стек вызовов (для отладки JS) | Нет |
> ⚠️ Внимание: Убедитесь, что у системного пользователя `nodered` есть права на запись в директорию `/var/log/hi/`. Можно настроить командой `sudo chown nodered:nodered /var/log/hi/`. Иначе возникнет рекурсивная ошибка при записи лога.
// Код для ноды "Форматировать ошибку"
// Извлекаем данные согласно спецификации Audit Log
const timestamp = new Date().toISOString();
const nodeName = msg.error.source.name || 'безымянная нода';
const nodeType = msg.error.source.type;
const nodeId = msg.error.source.id;
const errorMessage = msg.error.message;
// 1. Формируем строку для записи в файл лога (Audit String)
const logString = `${timestamp} | ERROR | ID: ${nodeId} | Node: "${nodeName}" (${nodeType}) | Message: ${errorMessage}\n`;
// 2. Формируем JSON-объект для отправки по MQTT (Контракт сообщения)
const alertPayload = {
ts: Date.now(),
level: "error",
source: {
id: nodeId,
name: nodeName,
type: nodeType
},
message: errorMessage
};
msg.logPayload = logString;
msg.payload = alertPayload;
return msg;
* Filename: `/var/log/hi/nodered_errors.log`
* Action: `Append to file`
* Payload property: `msg.logPayload`
* Broker: Выберите ваш MQTT-брокер.
* Topic: `hi/system/alerts`
* QoS: `1`
Теперь разверните проект и снова нажмите `Inject`. В результате:
- Нода `Catch` перехватит ошибку.
- В файле лога появится детализированная запись, включающая `node.id`.
- В MQTT-топик уйдет структурированный JSON для системы мониторинга.
Вы создали надежную и централизованную систему мониторинга состояния ваших потоков.
Итоги и лучшие практики
Нода `Catch` — это не просто еще один инструмент отладки, а фундаментальный компонент для построения надежных и отказоустойчивых систем автоматизации на платформе HI. В отличие от `Debug`, предназначенной для инженера на этапе разработки, `Catch` служит автоматическим стражем системы в производственной среде.
Ключевые выводы урока:
- Ошибки в Node-RED могут быть явно сгенерированы (`node.error()`) или возникать как исключения при сбое работы узлов (например, при сетевых таймаутах).
- Нода `Catch` позволяет централизованно перехватывать эти ошибки, предотвращая неконтролируемое прерывание потоков.
- Параметр `Scope` определяет, за какими нодами "наблюдает" `Catch`. Лучшей практикой является использование `All nodes on current tab` на каждой вкладке.
- Перехваченная ошибка доставляется в виде нового `msg`, где вся информация о сбое упакована в объект `msg.error`. Этот объект содержит текст ошибки, информацию о ноде-источнике и (иногда) стек вызовов.
- Комбинируя `Catch` с нодами `Function`, `File`, `MQTT Out` и другими, можно создавать гибкие системы логирования, оповещения и даже автоматического восстановления после сбоев.
Рекомендации для построения надежных потоков:
В этом уроке мы научились строить централизованный обработчик ошибок. В следующем уроке мы рассмотрим, как инкапсулировать повторяющуюся логику, включая обработку ошибок, в переиспользуемые компоненты с помощью субпотоков (subflows). Это позволит нам создавать стандартные, проверенные блоки для работы с устройствами, которые будут отказоустойчивыми "из коробки".