ГлавнаяАкадемияNode-RED: установка, flows, msg/JSON, отладка → Паттерн: Поток для журналирования (Audit Log)

Паттерн: Поток для журналирования (Audit Log)

Урок 4 · Node-RED: установка, flows, msg/JSON, отладка · 30 мин · theory

Введение в аудиторское журналирование (Audit Logging)

В любой сложной инженерной системе, от авиалайнера до системы автоматизации здания, возможность ретроспективно проанализировать последовательность событий имеет первостепенное значение. Аудиторское журналирование (или Audit Log) — это процесс систематической, структурированной и централизованной записи всех значимых событий, происходящих в системе. Это не просто отладочные сообщения для инженера, а фундаментальный механизм, обеспечивающий подотчетность, безопасность и возможность глубокого анализа инцидентов.

> 💡 Подсказка: Думайте об аудите как о «черном ящике» вашего умного дома. Если что-то пойдет не так — например, свет включился посреди ночи или система отопления не сработала в мороз — именно эти записи помогут восстановить точную картину событий и найти первопричину.

Основная роль Audit Log заключается в ответе на три ключевых вопроса:

  • Что произошло? (Например, `система перешла в режим "Охрана"`)
  • Кто или что инициировало событие? (Например, `пользователь "Иван" через мобильное приложение` или `сценарий "SCN-SAFETY-001" по таймеру`).
  • Когда это произошло? (С точностью до миллисекунды).
  • Ключевое отличие между простыми отладочными сообщениями, которые мы выводим через узел `Debug`, и полноценным журналом аудита заключается в их назначении и структуре.

    | Характеристика | Узел `Debug` (Отладка) | Audit Log (Журнал аудита) |

    | ------------------------- | ----------------------------------------------------------- | ---------------------------------------------------------- |

    | Назначение | Временная диагностика инженером на этапе разработки | Постоянная запись событий в рабочей (production) системе |

    | Жизненный цикл | Сообщения существуют только в панели отладки, исчезают при перезагрузке | Записи хранятся долговременно (в файле, базе данных) |

    | Структура | Произвольная (может быть число, строка, объект) | Строго стандартизированная (например, JSON с полями `level`, `source`, `message`) |

    | Аудитория | Инженер/разработчик | Инженер, служба поддержки, система безопасности, иногда — конечный пользователь |

    | Централизация | Децентрализован (узлы `Debug` разбросаны по потокам) | Единый, централизованный поток для сбора всех событий |

    Таким образом, создание выделенного потока для журналирования преследует четыре основные цели:

    Этот паттерн является неотъемлемой частью концепции «Операционного слоя» (Ops Layer), которую мы рассматривали ранее. Если узел `Catch` — это сборщик ошибок, а узел `Status` — мониторинг состояния, то Audit Log — это системный летописец, протоколирующий все, что заслуживает внимания.

    ---

    Архитектура потока для журналирования

    Правильно спроектированный поток для журналирования должен быть максимально простым, надежным и универсальным. Основой архитектуры является использование стандартных узлов Node-RED и строгого контракта сообщения.

    Ключевые компоненты потока

    Для создания единой точки входа для всех аудиторских сообщений идеально подходит пара узлов `link in` / `link out`. Это позволяет нам создать "виртуальный" вход в наш поток логирования, к которому можно подключиться из любого другого потока без создания физических "проводов" через всю рабочую область.

    Типовая архитектура потока выглядит так:

    // Любой рабочий поток (e.g., управление светом)
    

    ... -> [Function: Prepare Audit Msg] -> [link out: To Audit Log]

    // --- На отдельной вкладке "Operations Layer" ---

    // Поток Audit Log

    [link in: From All Flows] -> [Function: Standardize Log] -> [Switch: By Level] --+-- (ERROR) --> [Pushover/Notify]

    |

    +-- (ALL) -----> [File: Append to audit.log]

    |

    +-- (CRITICAL) -> [MySQL: write]

    Как видно из схемы, поток состоит из трех логических частей:

  • Точка входа: Узел `link in`, принимающий сообщения.
  • Обработка: Узел `function` для стандартизации и узел `switch` для маршрутизации.
  • Вывод/Хранение: Узлы `file`, `mysql`, `http request` и т.д., отвечающие за сохранение лога.
  • Проектирование контракта `msg.audit`

    Чтобы система логирования была эффективной, все сообщения должны следовать единому формату. Для этого мы вводим контракт сообщения, резервируя в объекте `msg` специальное свойство `msg.audit`.

    > ℹ️ Информация: Мы используем `msg.audit`, а не `msg.payload`, чтобы не нарушать основной поток данных. `msg.payload` предназначен для передачи полезной нагрузки между рабочими узлами (например, показания датчика или команда для реле). `msg.audit`, в свою очередь, является метаинформацией о самом процессе, которая передается параллельно основному потоку и не влияет на него.

    Стандартная структура объекта `msg.audit` должна включать следующие поля:

    {
    

    "ts": 1678886400000,

    "level": "INFO",

    "source_id": "SCN-LIGHT-023",

    "source_name": "Гостиная: Включение основного света",

    "message": "Свет включен по команде от настенного выключателя SW-LIVINGROOM-01",

    "details": {

    "user": "auto",

    "trigger_node_id": "a1b2c3d4.e5f6g7"

    }

    }

    📋 Ключевые понятия:

    * `INFO`: Информационное сообщение о штатном событии (например, "свет включен").

    * `WARN`: Предупреждение о потенциальной проблеме, не влияющей на работу системы (например, "ответ от датчика пришел с задержкой").

    * `ERROR`: Ошибка в работе компонента, которая была обработана (например, "не удалось записать значение в Modbus-устройство, попытка №1"). Это то, что мы перехватываем узлом `Catch`.

    * `CRITICAL`: Критическая ошибка, ведущая к отказу части системы.

    Опции хранения логов

    Контроллер HI предоставляет несколько встроенных возможностей для хранения журналов аудита:

  • Запись в файл: Самый простой и быстрый способ. Используется встроенный узел `file`. Логи записываются в текстовый файл на файловую систему контроллера (например, в `/var/log/hi-controller/audit.log`). Важно настроить ротацию логов, чтобы избежать переполнения диска.
  • Запись в базу данных MySQL: Контроллер оснащен сервером MySQL. Можно создать таблицу `audit_events` и записывать каждое событие как новую строку с помощью узла `mysql`. Это дает мощные возможности для фильтрации, агрегации и анализа данных с помощью SQL-запросов.
  • Отправка во внешние системы: Для крупных инсталляций или централизованного мониторинга нескольких объектов логи можно отправлять на внешний сервер по протоколам MQTT или Syslog. Это позволяет использовать специализированные инструменты, такие как стек ELK (Elasticsearch, Logstash, Kibana) или Grafana Loki для визуализации и анализа.
  • На начальном этапе рекомендуется использовать комбинацию записи в файл (для всех событий) и отправки оповещений (для `ERROR` и `CRITICAL`).

    ---

    Практика: Создание потока Audit Log

    Создадим базовый, но полнофункциональный поток для журналирования, который будет принимать события, стандартизировать их, маршрутизировать в зависимости от уровня и сохранять в файл.

    > ⚠️ Внимание: Никогда не создавайте циклы в логировании! Убедитесь, что ваш поток Audit Log не может сам генерировать ошибки, которые он же пытается перехватить через узел `Catch` на этой же вкладке. Это может привести к бесконечному циклу, который исчерпает ресурсы контроллера. Поток аудита должен быть максимально простым и "самодостаточным".

    Пошаговая инструкция

  • Создайте новую вкладку в редакторе Node-RED и назовите ее "Operational Layer". Если она у вас уже есть, используйте её.
  • Настройте точку входа:
  • * Перетащите на поле узел `link in`.

    * В его настройках нажмите кнопку "Создать новую ссылку".

    * Назовите ссылку `Audit Input` и добавьте описание "Центральный вход для всех событий аудита".

    * Назовите сам узел `[AUDIT] Log Ingest`.

  • Создайте узел-стандартизатор:
  • * Добавьте узел `function` и соедините его с выходом `link in`.

    * Назовите узел `Standardize Audit Message`.

    * Вставьте следующий код. Этот код проверяет наличие `msg.audit`, добавляет `timestamp` и преобразует все сообщение в одну строку JSON для записи в файл.

        // Проверяем, есть ли объект msg.audit. Если нет, создаем его.

    // Это защита от неправильно сформированных вызовов.

    if (typeof msg.audit !== 'object' || msg.audit === null) {

    msg.audit = {

    level: 'WARN',

    source_id: 'unknown',

    message: 'Received a non-standard audit message.',

    details: { original_payload: msg.payload }

    };

    }

    // Убеждаемся, что ключевые поля существуют

    msg.audit.ts = msg.audit.ts || Date.now();

    msg.audit.level = msg.audit.level || 'INFO';

    msg.audit.message = msg.audit.message || 'No message provided.';

    // Преобразуем объект msg.audit в одну строку формата JSON

    // Эта строка будет записана в файл.

    // null и 2 используются для форматирования с отступом, для логов лучше писать в одну строку

    // поэтому используем JSON.stringify(msg.audit) без аргументов.

    msg.payload = JSON.stringify(msg.audit);

    // Добавляем символ новой строки для корректной записи в файл

    msg.payload += "\n";

    return msg;

  • Маршрутизация по уровню:
  • * Добавьте узел `switch` после `function`.

    * Назовите его `Route by Level`.

    * Настройте его для проверки свойства `msg.audit.level`.

    * Добавьте правило: `==` (строка) `ERROR`. Это создаст первый выход.

    * Добавьте еще одно правило `==` (строка) `CRITICAL`. Оно будет указывать на тот же первый выход.

    * Ниже оставьте правило `otherwise`, которое создаст второй выход для всех остальных уровней.

  • Настройте вывод:
  • * Запись в файл:

    * Добавьте узел `file` (из секции "storage").

    * Соедините его со вторым выходом (`otherwise`) узла `switch`, чтобы записывать ВСЕ события.

    * В настройках узла укажите:

    * Filename: `/home/node-red/logs/audit.log` (путь может отличаться, убедитесь, что у Node-RED есть права на запись в эту директорию).

    * Action: `Append to file`.

    * Add newline (\n) to each payload: Убедитесь, что эта галочка СНЯТА, так как мы уже добавили `\n` в `function`.

    * Назовите узел `Save to File`.

    * Оповещение о критических ошибках:

    * Добавьте узел для отправки уведомлений, например `pushover` или `e-mail`.

    * Соедините его с первым выходом (`ERROR`/`CRITICAL`) узла `switch`.

    * Настройте его для отправки `msg.audit.message` на ваше устройство. Назовите узел `Notify Admin`.

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

    ---

    Интеграция с рабочими потоками

    Теперь, когда у нас есть централизованный "сборщик" логов, интегрировать его в существующие рабочие потоки очень просто. Для этого используется узел `link out`.

    > 🔗 Связанный материал: Этот паттерн идеально дополняет «Операционный слой» (Ops Layer), который мы рассматривали в уроке COURSE-06-M04-L04. Централизованный аудит — ключевой элемент надежной и наблюдаемой системы.

    Основной принцип: в любой точке вашего потока, где происходит значимое событие, вы добавляете небольшой блок, который формирует `msg.audit` и отправляет его в наш логгер.

    Пример №1: Логирование действия пользователя

    Представим поток, который управляет освещением. Пользователь нажимает кнопку, и реле включается. 我们 нужно залогировать этот факт.

    // Поток управления освещением
    

    [hi-di: Wall Switch] -> [Toggle Logic] -> [Function: Prepare Audit] -> [link out: Audit]

    | ^

    | |

    +------------------------------------------+

    |

    v

    [hi-do: Relay]

    Код для узла `Function: Prepare Audit`:

    // Входящий msg.payload = true (включить)
    

    const state = msg.payload ? "ON" : "OFF";

    msg.audit = {

    // ts будет добавлено автоматически в потоке аудита

    level: "INFO",

    source_id: "SCN-LIGHT-023",

    source_name: "Гостиная: Основной свет",

    message: `Свет переключен в состояние ${state} по сигналу от настенного выключателя`,

    details: {

    relay_id: "RL-01"

    }

    };

    // Возвращаем оригинальный msg, чтобы он пошел дальше к узлу link out.

    // Узел link out передаст копию msg в поток аудита, не прерывая основной поток.

    return msg;

    Узел `link out` настраивается на отправку в наш `Audit Input`. Важно: он не прерывает поток, а отправляет копию сообщения "в сторону". Основной `msg` с `payload` без изменений пойдет дальше, если это необходимо.

    Пример №2: Логирование ошибки от `Catch`

    Это наиболее важный сценарий использования. Мы уже создали централизованный узел `Catch` для перехвата всех ошибок. Теперь направим эти ошибки в наш новый Audit Log.

    Поток обработки ошибок (часть "Operational Layer"):

    [Catch: All nodes on all flows] -> [Function: Format Error for Audit] -> [link out: Audit Input]
    

    Код для узла `Function: Format Error for Audit`:

    // msg.error содержит информацию об ошибке
    

    const errorInfo = msg.error;

    msg.audit = {

    level: "ERROR",

    source_id: errorInfo.source.id, // ID узла, вызвавшего ошибку

    source_name: errorInfo.source.name || 'N/A', // Имя узла

    message: "Произошла ошибка в потоке: " + errorInfo.message,

    details: {

    error_type: errorInfo.source.type,

    original_msg: msg // Сохраняем все исходное сообщение для анализа

    }

    };

    // Этот поток только для логирования, поэтому просто возвращаем msg

    return msg;

    Таким образом, любая ошибка, пойманная узлом `Catch` в любом месте проекта, будет автоматически отформатирована и отправлена в единый журнал аудита, а также вызовет отправку уведомления администратору.

    ---

    Итоги и лучшие практики

    Мы рассмотрели паттерн Audit Log — один из самых важных для создания профессиональных и надежных систем автоматизации на базе Node-RED. Он выводит наши проекты из категории "хобби" в категорию инженерных решений.

    Краткий обзор

    Лучшие практики

  • Всегда используйте единую точку входа. Не создавайте несколько потоков логирования. Один центральный `Audit Input` — залог порядка.
  • Используйте стандартизированный формат. Определите для себя структуру `msg.audit` и придерживайтесь ее во всем проекте. Это окупится сторицей при анализе логов.
  • Принцип разумной достаточности. Не логируйте всё подряд. Фокусируйтесь на событиях, имеющих реальное значение:
  • * Действия пользователя (нажатие кнопок, изменение уставок).

    * Смена ключевых состояний системы (режим "охрана", "день/ночь").

    * Запуск и завершение важных сценариев.

    * Все ошибки и предупреждения (`WARN`, `ERROR`).

    * События старта и остановки контроллера.

  • Надежность потока аудита. Этот поток — критическая часть системы. Он должен быть максимально простым, быстрым и отказоустойчивым. Избегайте в нем сложной логики и внешних зависимостей, которые могут сами стать источником сбоя.
  • Что дальше?

    В следующих уроках мы перейдем к более сложным темам, таким как управление состоянием (Finite State Machines) и создание пользовательских панелей управления (Dashboard). Наличие надежного механизма журналирования, который мы создали сегодня, станет прочным фундаментом для этих продвинутых практик, позволяя нам отслеживать и отлаживать сложную логику состояний и взаимодействия с пользователем.