ГлавнаяАкадемияОсновы умного дома → Практика: Простое журналирование

Практика: Простое журналирование

Урок 6 · Основы умного дома · 30 мин · theory

Введение в журналирование: Зачем и что логировать?

> ℹ️ Информация: Правильно настроенное журналирование — это ваш «черный ящик», который поможет понять, что пошло не так в ваше отсутствие. Без логов вы будете пытаться диагностировать проблему "вслепую", полагаясь лишь на догадки.

Журналирование (или логирование) — это процесс систематической записи информации о событиях, происходящих в системе. Для инженера по автоматизации это один из важнейших инструментов, который служит трем ключевым целям.
  • Отладка потоков (flows) и сценариев. Это основная и наиболее частая причина использования логов. Когда сценарий ведет себя не так, как ожидалось (например, свет не включился по датчику движения), именно логи позволяют пошагово отследить всю цепочку событий: получил ли контроллер сигнал от датчика, какое решение принял узел логики, была ли отправлена команда на реле и был ли получен ответ.
  • Анализ поведения системы и пользователей. Логи позволяют собирать статистику и отвечать на вопросы более высокого уровня:
  • * Как часто открывается входная дверь в течение дня?

    * Сколько часов в среднем работает система отопления в зимний период?

    * Есть ли закономерности в ложных срабатываниях датчика протечки?

    Анализ этих данных помогает оптимизировать сценарии, экономить ресурсы и повышать комфорт.

  • Аудит безопасности и действий. В системах, где важен контроль доступа (офисы, гостиницы, промышленные объекты), логи выполняют функцию протокола безопасности. Они безоговорочно фиксируют, кто и когда получал доступ, какие команды отправлялись на исполнительные устройства (например, открытие замка или ворот).
  • Чтобы логи были полезными и не превращались в информационный шум, необходимо придерживаться простого правила: логировать только значимые события.

    📋 Ключевые понятия: Что следует и не следует логировать

    | Критерий | Что следует логировать (Примеры) | Что НЕ следует логировать (Примеры) |

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

    | Изменения состояния | Включение/выключение света, открытие/закрытие двери (сработал геркон), изменение режима климат-контроля (с "Нагрев" на "Охлаждение"). | Постоянное значение температуры от датчика, если оно не меняется. |

    | Команды | Команда от пользователя из мобильного приложения, нажатие настенного выключателя, команда от другого системного сценария. | Внутренние, служебные сообщения между узлами в Node-RED, которые служат лишь для передачи промежуточных данных. |

    | Ошибки и исключения | Ошибка связи с Modbus-устройством, невалидные данные от датчика, сбой при отправке MQTT-сообщения, ошибка выполнения узла `function`. | Любые успешно выполненные, но рутинные операции. |

    | Частота событий | Редкие, но важные события: срабатывание датчика протечки, датчика дыма, потеря связи с контроллером. | Высокочастотные данные: показания счетчика электроэнергии каждые 100 мс, сырые данные с акселерометра. Такие потоки данных могут быстро исчерпать место на диске. |

    Важно также различать системные логи операционной системы контроллера (Debian) и прикладные логи, которые мы создаем целенаправленно для системы автоматизации.

    ---

    Инструменты для записи логов в Node-RED

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

    Узел `debug`

    Это основной инструмент для отладки в реальном времени. Он выводит полученное им сообщение `msg` на боковую панель редактора Node-RED.

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

    * Системная консоль: Выводит лог в ту же консоль, где был запущен Node-RED. На контроллере HI это можно просмотреть командой `journalctl -f -u nodered`. Этот режим полезен для отслеживания событий без входа в редактор.

    * `msg.payload`: Только полезная нагрузка сообщения.

    * Полный объект `msg`: Крайне полезно для отладки сложных потоков, так как позволяет увидеть все свойства сообщения (`topic`, `_msgid`, и любые другие, добавленные предыдущими узлами).

    > 💡 Подсказка: Узлы `debug` можно временно отключать и включать кнопкой на самом узле прямо в редакторе, не останавливая поток. Это позволяет быстро фокусироваться на нужных участках логики.

    Узел `file` для прямой записи в файл

    Для постоянного журналирования, которое должно сохраняться между перезагрузками и анализироваться позже, используется узел `file`. Он записывает `msg.payload` в указанный файл.

    * append to file (добавить в файл): Наиболее частый режим для логов. Каждое новое сообщение добавляется в конец файла.

    * overwrite file (перезаписать файл): Полностью заменяет содержимое файла. Используется редко.

    > ⚠️ Внимание: Для записи в системные папки (например, `/var/log`) у пользователя, от имени которого запущен Node-RED (обычно `nodered`), должны быть права на запись. Этот аспект мы разберем в практической части.

    Формирование читаемой строки для лога

    Просто записывать `msg.payload` (который часто является JSON-объектом или простым числом) в файл — не лучшая практика. Лог должен быть информативным. Каждая строка должна содержать как минимум временную метку и контекст события. Для этого перед узлом `file` ставят узел `function` или `template`.

    Пример с использованием узла `function`:

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

    // Код для узла function
    
    

    // 1. Получаем текущее время в стандартном формате ISO

    const timestamp = new Date().toISOString();

    // 2. Получаем топик, из которого пришло сообщение

    const topic = msg.topic;

    // 3. Получаем значение. Если это объект, преобразуем его в строку JSON.

    let payload_str;

    if (typeof msg.payload === 'object') {

    payload_str = JSON.stringify(msg.payload);

    } else {

    payload_str = msg.payload;

    }

    // 4. Формируем единую строку для записи в лог-файл

    // Формат: ВРЕМЯ | ТОПИК | ЗНАЧЕНИЕ

    msg.payload = `${timestamp} | ${topic} | ${payload_str}\n`; // \n добавляет перенос строки

    // 5. Передаем сообщение дальше на узел 'file'

    return msg;

    Кроме того, узел `file` позволяет динамически задавать имя файла через свойство `msg.filename`. Это открывает возможности для более сложной организации логов, например, записывать события от разных подсистем (освещение, безопасность, климат) в разные файлы.

    ---

    Практика: Создание простого лог-файла для датчика двери

    В этой практической части мы создадим поток, который будет записывать все события от датчика открытия/закрытия входной двери (геркона) в отдельный текстовый файл.

    > 💡 Подсказка: Для удобного форматирования дат и времени в человекочитаемый вид (например, `27.10.2023 13:45:10`) можно использовать сторонний модуль `node-red-contrib-moment`. Он предоставляет готовый узел, который преобразует `timestamp` в любой заданный формат. Для простоты в этом уроке мы будем использовать стандартный формат ISO.

    Пошаговое создание потока

    Мы создадим цепочку из трех узлов: `mqtt in` -> `function` -> `file`.

    Шаг 1: Подписка на событие от датчика

  • Добавьте на поле узел `mqtt in`.
  • Настройте его для подключения к вашему MQTT-брокеру (обычно он находится на `localhost:1883`).
  • В поле Topic укажите топик, в который геркон публикует свое состояние. На контроллерах Wiren Board, подключенный к "сухому контакту" `EXT1_IN` модуля `wb-gpio`, это будет топик `/devices/wb-gpio/controls/EXT1_IN`.
  • > 🔗 Связанный материал: Мы подробно анализировали структуру MQTT-топиков и контракты сообщений от оборудования в уроке `COURSE-01-M02-L06: Практика: Анализ контракта события`. Сообщение в этом топике будет содержать `1` (дверь закрыта/контакт замкнут) или `0` (дверь открыта/контакт разомкнут).

    Шаг 2: Форматирование строки для лога

  • Добавьте узел `function` и соедините его выход с выходом узла `mqtt in`.
  • Вставьте в него следующий код:
  • // Код для узла function "Format Door Log"
    
    

    const timestamp = new Date().toISOString();

    const topic = msg.topic;

    const value = msg.payload;

    let state_text;

    if (value == '1') {

    state_text = 'CLOSED';

    } else if (value == '0') {

    state_text = 'OPENED';

    } else {

    state_text = 'UNKNOWN_STATE';

    }

    // Формируем итоговую строку для лога.

    // Пример: 2023-10-27T12:30:05.123Z | /devices/wb-gpio/controls/EXT1_IN | State: OPENED

    msg.payload = `${timestamp} | ${topic} | State: ${state_text}\n`;

    return msg;

    Этот код не просто записывает `0` или `1`, а добавляет понятное текстовое описание `OPENED`/`CLOSED`, что делает лог значительно более читаемым для человека.

    Шаг 3: Запись в файл и настройка прав доступа

  • Добавьте узел `file` и соедините его с выходом узла `function`.
  • Настройте узел `file`:
  • * File path (Путь к файлу): `/var/log/home-automation/door_events.log`

    * Action (Действие): `append to file` (добавлять в файл)

    * Create directory...: Установите галочку.

    После развертывания (Deploy) поток готов, но, скорее всего, он не сможет записать файл, так как у него нет прав. Узел `file` покажет красный индикатор ошибки.

    Проверка и установка прав доступа в Linux

  • Подключитесь к консоли контроллера по SSH.
  • Создайте директорию для наших логов:
  •     sudo mkdir -p /var/log/home-automation

  • Измените владельца этой директории на пользователя `nodered`. Именно от его имени работает сервис Node-RED в Debian.
  •     sudo chown nodered:nodered /var/log/home-automation

  • Теперь вернитесь в Node-RED и снова инициируйте событие (откройте/закройте дверь). Ошибка должна исчезнуть.
  • Проверим содержимое файла лога прямо из консоли:
  •     cat /var/log/home-automation/door_events.log

    Вы должны увидеть строки вида:

        2023-10-27T12:30:05.123Z | /devices/wb-gpio/controls/EXT1_IN | State: OPENED

    2023-10-27T12:30:08.456Z | /devices/wb-gpio/controls/EXT1_IN | State: CLOSED

    Поздравляем! Вы создали свой первый автоматизированный журнал событий.

    ---

    Управление лог-файлами: Ротация и очистка в Linux

    Созданный нами лог-файл будет постоянно расти. На контроллере с ограниченным объемом памяти (например, eMMC на 8-16 ГБ или SD-карта) это неизбежно приведет к исчерпанию дискового пространства и отказу системы. Чтобы этого не произошло, используется механизм ротации логов.

    > ⚠️ Внимание: Неправильная конфигурация `logrotate`, особенно в сочетании с неверными правами доступа, может привести к полной остановке записи логов. Перед применением на боевой системе всегда проверяйте конфигурацию командой `sudo logrotate --force --verbose /etc/logrotate.conf`.

    Утилита `logrotate`

    В Debian (и большинстве других дистрибутивов Linux) за ротацию логов отвечает стандартная утилита `logrotate`. Она запускается по расписанию (обычно раз в день) и обрабатывает файлы согласно правилам, описанным в конфигурационных файлах.

    Основной принцип работы `logrotate`:

  • Переименовывает текущий лог-файл (например, `door_events.log` в `door_events.log.1`).
  • Создает новый пустой файл `door_events.log` для продолжения записи.
  • При следующей ротации `door_events.log.1` станет `door_events.log.2` и так далее.
  • Опционально сжимает старые файлы (например, `door_events.log.2.gz`).
  • Удаляет самые старые файлы, превысившие лимит хранения.
  • Создание конфигурации для нашего лога

    Все пользовательские конфигурации для `logrotate` следует размещать в директории `/etc/logrotate.d/`.

  • Подключитесь к консоли контроллера по SSH.
  • Создайте новый конфигурационный файл с помощью текстового редактора `nano`:
  •     sudo nano /etc/logrotate.d/home-automation

  • Вставьте в этот файл следующее содержимое:
  • /var/log/home-automation/*.log {
    

    daily

    rotate 7

    compress

    delaycompress

    missingok

    notifempty

    create 0640 nodered nodered

    size 10M

    }

    Разбор директив `logrotate`

    Давайте разберем, что означает каждая строка в нашей конфигурации.

    | Директива | Описание |

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

    | `/var/log/home-automation/*.log` | Применить правила ко всем файлам, заканчивающимся на `.log` в указанной директории. |

    | `daily` | Проверять файлы для ротации ежедневно. Альтернативы: `weekly`, `monthly`. |

    | `rotate 7` | Хранить 7 старых копий лог-файлов. После создания 8-й копии, самая старая будет удалена. |

    | `compress` | Сжимать старые лог-файлы с помощью `gzip` (они получат расширение `.gz`), чтобы экономить место. |

    | `delaycompress` | Не сжимать самый последний (вчерашний) лог. Сжатие начнется со второго по старости файла. Это удобно, если нужно часто просматривать вчерашние события. |

    | `missingok` | Не выдавать ошибку, если лог-файл не найден. |

    | `notifempty` | Не проводить ротацию, если лог-файл пуст. |

    | `create 0640 nodered nodered` | После ротации создать новый пустой файл с правами `0640` (чтение/запись для владельца, чтение для группы) и установить владельца и группу `nodered`. Это критически важная директива для Node-RED! |

    | `size 10M` | Проводить ротацию, если размер файла превысит 10 мегабайт, даже если не прошел день (`daily`). Это защита от "всплеска" событий. |

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