ГлавнаяАкадемияСценарии умного дома: режимы, состояния, приоритеты → Зачем нужен журнал событий? (Audit Trail)

Зачем нужен журнал событий? (Audit Trail)

Урок · Сценарии умного дома: режимы, состояния, приоритеты · 30 мин · theory

Введение в журнал событий (Audit Trail): Ваш черный ящик

> ℹ️ Информация: Журнал событий — это один из самых недооцененных, но критически важных компонентов любой отказоустойчивой системы умного дома. Он представляет собой не просто технический лог для разработчика, а полноценный инструмент для диагностики, анализа безопасности и даже решения споров.

Журнал событий (или Audit Trail) — это защищенная, хронологически упорядоченная запись всех значимых событий, происходящих в системе автоматизации. Подобно "черному ящику" в самолете, его основная задача — дать исчерпывающий ответ на вопрос "Что, где, когда и почему произошло?" уже после свершившегося факта.

Давайте разберем его ключевые функции:

  • Отладка и поиск неисправностей. Это наиболее очевидное применение. Представьте, что клиент жалуется: "Утром свет в гостиной не включился по сценарию 'Пробуждение'". Без журнала событий поиск причины превращается в гадание. С журналом вы можете восстановить всю цепочку:
  • Был ли в 6:30 утра запущен сценарий? Запись в журнале: "INFO: Scenario 'WakeUp' started."*

    Получил ли контроллер сигнал от датчика освещенности? Запись: "DEBUG: Light sensor 'living_room_lux' reported 50 lux."*

    Решила ли логика, что 50 люкс — это достаточно темно для включения света? Запись: "INFO: Light threshold (100 lux) not met, skipping light activation."*

    Была ли отправлена команда на реле? Отсутствие записи об отправке команды наглядно демонстрирует, на каком этапе логика приняла иное решение.*

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

  • Анализ безопасности. Журнал событий является первой линией обороны при анализе инцидентов безопасности. Кто и когда снял дом с охраны? Были ли попытки подобрать пароль к веб-интерфейсу? Открывалась ли входная дверь в 3 часа ночи, когда система находилась в режиме "Охрана"?
  • Пример записи:* `WARNING: Failed login attempt for user 'admin' from IP 8.8.8.8.`

    Пример записи:* `CRITICAL: Front door sensor triggered while security mode is 'ARMED'.`

    Наличие таких записей позволяет не только расследовать инцидент, но и проактивно настраивать систему оповещений на аномальную активность.

  • Юридическая и страховая значимость. В критических ситуациях, таких как протечка воды или пожар, журнал событий может служить неопровержимым доказательством.
  • Предположим, в системе произошла протечка, но сценарий "Антипотоп", который мы рассмотрим в одном из следующих уроков, отработал штатно: датчик зафиксировал воду, и контроллер немедленно отправил команду на шаровой кран с электроприводом для перекрытия воды. Ущерб был минимизирован. При обращении в страховую компанию вы можете предоставить выгрузку из журнала:

    * `14:32:01 CRITICAL: [Leak Sensor Bathroom] State changed to 'WET'. Triggering 'Anti-Flood' scenario.`

    * `14:32:01 INFO: [Valve MainWater] Command 'CLOSE' sent via Modbus.`

    * `14:32:02 INFO: [Valve MainWater] Status confirmed: 'CLOSED'.`

    * `14:32:02 INFO: Push notification sent to user 'owner': 'Протечка в ванной! Вода перекрыта.'`

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

    В профессиональной инсталляции отсутствие журнала событий является признаком низкого качества и незрелости системы. Это экономия на самом важном — на стабильности и прозрачности работы объекта.

    ---

    Что и как логировать: Принципы эффективного журналирования

    то и как логировать: Принципы эффективного журналирования

    Определившись с тем, почему журнал событий критически важен, необходимо понять, как именно его вести. Хаотичные записи без четкой структуры и контекста превратятся в "цифровую свалку", в которой невозможно быстро найти причину сбоя. Чтобы журнал оставался аналитическим инструментом, а не мертвым грузом на диске, придерживайтесь строгих стандартов ведения логов.

    > 💡 Подсказка: Используйте стандартизированный машинно-читаемый формат, например, JSON. Это упростит последующий парсинг, ротацию и анализ логов в автоматическом режиме с помощью таких инструментов, как ELK-стек (Elasticsearch, Logstash, Kibana), Filebeat, Fluentd или даже обычного скрипта на Python.

    Структура сообщения: Стандартный формат

    Каждая запись в журнале должна быть самодостаточной и содержать всю необходимую информацию для детального восстановления хода событий. Мы настоятельно рекомендуем использовать структурированное логирование и придерживаться следующего JSON-формата для каждого генерируемого события:

    {
    

    "ts": 1678886400000,

    "level": "INFO",

    "source_id": "flow-auto-light-012",

    "source_name": "Сценарий 'Вечерний свет'",

    "facility": "automation_logic",

    "message": "Режим 'Вечер' активирован, включение основного света в гостиной.",

    "details": {

    "target_device": "relay-livingroom-main",

    "command": "ON",

    "transition_time": 3000

    }

    }

    Разберем обязательные поля:

    Техническая деталь: Одно подобное JSON-сообщение в среднем занимает около 150–250 байт. Для масштабирования: 10 000 полезных срабатываний сценариев потребуют всего ~2-2.5 МБ дискового пространства. Это абсолютно безопасно для SD-карт контроллеров (например, Raspberry Pi) и не сокращает значительно их срок службы.

    Уровни логирования (Logging Levels)

    Понимание и грамотное применение уровней — ключ к управлению объемом журнала. Использование разных уровней позволяет моментально фильтровать события (особенно при поиске аномалий) и концентрироваться на самом важном. В production-средах, таких как Node-RED или Home Assistant, стандартной практикой является установка глобального уровня на `INFO` или `WARNING`.

    | Уровень | Описание | Пример в умном доме |

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

    | DEBUG | Детальная информация для отладки. Используется разработчиком во время пусконаладки или расследования сложного бага. В рабочем режиме отключен. | "Получен сырой payload с Modbus-модуля `node-2`: `[251, 0, 1, 0, 5]`" |

    | INFO | Информационные сообщения о штатной, ожидаемой работе системы, смене режимов. Основной рабочий уровень для аудита. | "Сценарий 'Я ушел' активирован. Команда OFF отправлена всем светильникам." |

    | WARNING | Предупреждение о потенциальной проблеме, которая пока не привела к фатальной ошибке, но требует внимания (система справилась сама / fallback). | "Ответ от датчика температуры не получен за 2 цикла. Используется последнее валидное значение (22.5°C)." |

    | ERROR | Ошибка в работе компонента или сценария, которая нарушила его логику, но не остановила работу всего контроллера. | "Не удалось отправить команду на закрытие штор: актуатор `node-6` недоступен (Timeout)." |

    | CRITICAL| Критическое событие, угрожающее безопасности или целостности системы (пожар, взлом, системный отказ). Требует интеграции с push-уведомлениями. | "Обнаружена протечка воды на кухне! Перекрытие стояка..." / "Обрыв связи с главным шлюзом!" |

    Контекст — король

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

    Вопросы для отладки:* Какая дверь? В какое время? Это легитимное открытие ключом или взлом монтировкой? Кто или что вызвало это событие?
        {
    

    "ts": 1678886400000,

    "level": "INFO",

    "source_id": "sensor-door-main",

    "facility": "security",

    "message": "Статус входной двери изменен на 'открыто'",

    "details": {"security_mode": "DISARMED", "unlocked_by": "user_id_2"}

    }

    Результат:* Мы мгновенно видим, какая дверь сработала, точное время, факт безопасности (штатное открытие, так как система в режиме `DISARMED`), и кто именно снял блокировку в объекте `details`.

    Избегание избыточности (Log Flooding)

    Последняя, но не менее важная проблема — логирование "каждого чиха" оборудования. Запись в постоянный текстовый лог показаний датчика температуры, обновляющегося каждую секунду, за сутки создаст 86 400 строк (около 15-20 МБ избыточных данных на один датчик). Это быстро заполнит диск, вызовет излишний износ Flash-памяти и сделает поиск нужной информации невозможным.

    Решение проблемы: Логируйте только значимые изменения состояния, а не бесконечный поток телеметрии.
  • Не логируйте в системный журнал с уровнем `INFO` сырые данные вроде `21.1°C`, `21.1°C`, `21.2°C`. Оставьте эти данные для time-series баз данных (как InfluxDB) и графиков в дашбордах.
  • Используйте узел фильтрации состояния (например, логику RBE (Report-by-Exception), которую мы детально изучим в следующих уроках). Настройте пропуск сообщения только если значение изменилось на фиксированную дельту (например, порог `> 0.5°C`). Это снизит информационный шум на 90%.
  • Журналируйте только факт реакции: когда температура пересекает важный порог (уставку термостата) и, что еще важнее, когда система принимает физическое решение на основе этого значения ("Логика активирована: включено реле отопительного котла").
  • Практикум: Спроектируйте лог-событие

    Задание-кейс: Представьте, что у вас есть сценарий автоматического освещения в прихожей по датчику движения. Сегодня ночью произошел сбой — свет включился на 1 минуту, хотя никого не было. Датчик дал ложное срабатывание (`false positive`). Какую запись в `audit trail` вы бы хотели там найти, чтобы утром за пару минут определить причину ночного инцидента? Чек-лист идеального лога для диагностики этого кейса:

    Практика в Node-RED: Создание универсального логгера

    рактика в Node-RED: Создание универсального логгера

    Теория важна, но давайте создадим практический инструмент, который вы сможете использовать во всех своих проектах. Мы сделаем централизованный субпоток (Subflow) для журналирования, который будет принимать события от других узлов и записывать их в стандартизированном формате.

    > 💡 На заметку: Этот урок — наш первый шаг к созданию переиспользуемых компонентов. Мы упакуем логгер в субпоток (Subflow). Это мощный механизм Node-RED, который позволяет создавать свои собственные узлы из комбинации других, инкапсулируя сложную логику внутри одного аккуратного блока.

    Наша цель — создать субпоток "Universal Logger", который:

  • Имеет один вход для получения сообщений.
  • Принимает на вход объект `msg` с информацией для логирования.
  • Автоматически обогащает его меткой времени, текущим режимом дома (`house_mode`), именем узла-источника и уровнем критичности (Severity).
  • Записывает результат в файл `/var/log/hi-system/events.log` в формате JSON Lines.
  • Шаг 1: Создание и базовая настройка Subflow

    Для начала подготовим «контейнер» для нашей логики.

  • В верхнем правом меню Node-RED выберите `☰ -> Subflows -> Create Subflow`.
  • В открывшейся вкладке свойств:
  • * Name: `Universal Logger`

    * Category: Выберите `advanced` или создайте свою категорию `custom`.

    * Color: Задайте акцентный цвет (например, темно-серый `#8B9D96`).

  • Нажмите `+ input`, чтобы добавить входной порт.
  • Внутри субпотока разместите узел `Function` и узел `File`. Соедините их: `Вход субпотока -> Function -> File`.
  • Шаг 2: Настройка узла `Function` ("Format Log Message")

    Этот узел приводит данные к единому «контракту», принятому в нашей системе. Мы добавим поле `house_mode`, которое будем брать из глобального контекста (подробнее о его инициализации мы говорили в модуле M03).

    Вставьте следующий код в узел `Function`:

    // Берем текущий режим дома из глобального контекста (contract: M03)
    

    const currentMode = global.get("house_mode") || "UNKNOWN";

    // 1. Обработка системных ошибок (перехват от узла Catch)

    if (msg.error) {

    msg.log_level = 'ERROR';

    msg.payload = msg.error.message;

    msg.details = {

    source_id: msg.error.source.id,

    source_name: msg.error.source.name,

    original_msg: JSON.stringify(msg.error.original_msg).slice(0, 500)

    };

    }

    // 2. Безопасная обработка payload

    let safePayload = msg.payload;

    if (typeof safePayload === 'object' && safePayload !== null) {

    safePayload = JSON.stringify(safePayload);

    }

    // 3. Формирование структуры лога (Audit Record)

    const logEntry = {

    ts: new Date().toISOString(),

    level: msg.log_level || 'INFO',

    facility: msg.facility || 'system',

    house_mode: currentMode, // Важно для аудита состояний

    source_name: (msg.details && msg.details.source_name) ? msg.details.source_name : 'Unknown Node',

    message: safePayload,

    details: msg.details || {}

    };

    // 4. Подготовка для узла File

    msg.payload = JSON.stringify(logEntry) + "\n";

    return msg;

    Шаг 3: Настройка узла `File` ("Write to Log")

  • Filename: `/var/log/hi-system/events.log`.
  • Action: `Append to file`.
  • Опция `Add newline (\n) to each payload` должна быть ВЫКЛЮЧЕНА, так как разделитель уже добавлен в коде.
  • Create directory if it doesn't exist? — установите галочку.
  • Шаг 4: Использование логгера в потоках

    Пример 1: Логирование инцидента безопасности (Газ)

    Вспомним сценарий из урока M07-L02. При срабатывании датчика газа мы не только перекрываем клапан, но и обязаны зафиксировать это событие в аудит-логе для последующего разбора (был ли это ложный сработок или реальная угроза).

    [Gas Sensor: Kitchen] --> [Switch: value > 500] --> [Change: Set Alarm Msg] --> [Universal Logger]
    

    В узле `Change` (Set Alarm Msg) настройте:

    Пример 2: Перехват ошибок взаимодействия

    Если сценарий перекрытия клапана не смог отправить команду (например, отвалился Zigbee-стик), узел `Catch` отправит подробности в логгер.

    // Поток обработки ошибок
    

    [Catch: All Nodes] --(msg.error)--> [Universal Logger]

    Шаг 5: Практическое мини-задание и проверка

    Ваша задача:
  • Соберите цепочку: `[Inject] -> [Function (с ошибкой)]`, и `[Catch] -> [Universal Logger]`.
  • В узле `Function` напишите: `throw new Error("Ошибка связи с клапаном газа: Timeout");`.
  • Нажмите Deploy и кликните по узлу `Inject`.
  • Ожидаемый результат:

    Проверьте файл через терминал: `tail -n 1 /var/log/hi-system/events.log`.

    Вы должны увидеть JSON с заполненным полем `house_mode`, уровнем `ERROR` и текстом про таймаут клапана.

    > ⚠️ Важно: Файлы логов имеют свойство расти. В реальной инсталляции обязательно настройте утилиту `logrotate` для файла `events.log`, чтобы он архивировался и не переполнял память контроллера.

    Хранение и ротация логов на контроллерах HI

    ранение и ротация логов на контроллерах HI

    Создать лог-файл — это только половина дела. На встроенном накопителе контроллера (eMMC, обычно объемом 8–16 ГБ, из которых существенная часть занята системными разделами ОС) пространство ограничено. Если записывать каждое изменение температуры или состояния датчиков движения, лог может прирастать на 1–5 МБ ежедневно. Без управления размером файлов диск рано или поздно переполнится, что приведет к остановке базы данных, Node-RED и всей операционной системы Debian.

    > ⚠️ Внимание: Объем памяти на контроллерах HI ограничен. Неправильно настроенная политика ротации логов — частая причина сбоев системы из-за полного заполнения диска. Всегда настраивайте `logrotate` для всех создаваемых вами лог-файлов.

    Подготовка файловой системы

    Прежде чем настраивать автоматическую очистку, логи нужно правильно разместить. Стандартным местом для хранения журналов в Linux является директория `/var/log`. Ваши кастомные логи рекомендуется складывать в отдельную поддиректорию, например, `/var/log/hi-system/`. Это изолирует ваши файлы от системных и упрощает управление правами доступа.

    Чтобы создать директорию и выдать к ней права пользователю Node-RED, подключитесь к контроллеру по SSH и выполните:

    sudo mkdir -p /var/log/hi-system
    

    sudo chown nodered:nodered /var/log/hi-system

    sudo chmod 755 /var/log/hi-system

    Ротация логов с помощью `logrotate`

    Logrotate — это стандартная системная утилита в Debian, которая позволяет автоматически управлять лог-файлами: разбивать на части, сжимать, переименовывать и удалять старые логи по заданному расписанию (утилита автоматически вызывается планировщиком `cron`).

    Основной конфигурационный файл находится в `/etc/logrotate.conf`, а индивидуальные настройки программ — в директории `/etc/logrotate.d/`. Мы создадим свой файл конфигурации для нашего кастомного журнала.

    Пример конфигурации `logrotate`

    Создайте файл `/etc/logrotate.d/hi-system` (например, открыв его через `sudo nano /etc/logrotate.d/hi-system`) со следующим содержимым:

    # Файл: /etc/logrotate.d/hi-system
    
    

    /var/log/hi-system/*.log {

    daily # Ротировать логи ежедневно

    maxsize 10M # НО если файл превысит 10 МБ до истечения дня - ротировать немедленно

    rotate 30 # Хранить 30 старых файлов (т.е. за ~30 дней)

    compress # Сжимать старые логи (используя gzip)

    delaycompress # Не сжимать самый последний лог, сжать при следующей ротации

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

    notifempty # Не ротировать пустой файл

    create 0640 nodered nodered # Создать новый пустой файл с правами 640 и владельцем 'nodered'

    su nodered nodered # Выполнять ротацию от имени пользователя nodered

    }

    Разберем ключевые директивы: `/var/log/hi-system/.log`: Правило будет применяться ко всем файлам с расширением `.log` внутри указанной папки.

    Практическое мини-задание: Тестирование ротации

    После написания конфигурации крайне важно проверить её работоспособность, не дожидаясь наступления новых суток.

  • Сгенерируйте тестовый лог, если он ещё не существует:
  •    sudo su - nodered -c "echo 'Тестовое событие системы' > /var/log/hi-system/events.log"

  • Запустите «сухой прогон» (`--debug`), чтобы удостовериться в отсутствии синтаксических ошибок в конфигурационном файле:
  •    sudo logrotate --debug /etc/logrotate.d/hi-system

    Ожидаемый результат: Команда выведет подробный план действий, покажет, что файл конфигурации прочитан, и лог `/var/log/hi-system/events.log` обнаружен, но реальной ротации не произойдет.

  • Принудительно выполните ротацию (`-f`):
  •    sudo logrotate -f /etc/logrotate.d/hi-system

  • Проверьте результат в папке хранения:
  •    ls -lah /var/log/hi-system/

    Ожидаемый результат: В выводе вы должны увидеть актуальный (и пока пустой) файл `events.log` и перемещенный `events.log.1`, в котором будет лежать ваша строка "Тестовое событие системы".

    Долгосрочное хранение и экспорт логов

    Встроенной памяти контроллера с настроенной ротацией обычно безупречно хватает для обслуживания системы и хранения логов за 1–3 последних месяца. Но для детального масштабного анализа или соответствия особым стандартам ИБ может потребоваться хранение архивов в течение года. В таком случае опираться лишь на диск eMMC рискованно, и требуется экспорт:

    Пример: Поиск 'плавающей' ошибки с помощью журнала

    ример: Поиск "плавающей" ошибки с помощью журнала

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

    Описание инцидента

    Проблема: Клиент сообщает, что комплексный сценарий "Выключить всё" (`Good-Bye`), запускаемый мастер-кнопкой у входной двери, иногда не выключает свет в ванной комнате. Ошибка имеет "плавающий" характер — проявляется случайным образом 1–2 раза в неделю. Архитектура: Мастер-кнопка работает по протоколу KNX, реле управления светом в ванной — по Modbus RTU, логика обрабатывается центральным контроллером с Node-RED.

    Шаг 1. Локализация проблемы в консоли

    Инженер техподдержки не пытается воспроизвести баг физически (это долго и неэффективно). Вместо этого он обращается к `Audit Trail`.

  • Подключиться к контроллеру по SSH.
  • Перейти в директорию с логами: `cd /var/log/hi-system/`.
  • Использовать команды утилит `grep` и `zgrep` (для сжатых архивов) для поиска всех событий, связанных с ID проблемного реле (`actuator-bath-light`).
  • # Ищем записи в текущем логе за сегодня
    

    grep "actuator-bath-light" events.log

    # Ищем записи в заархивированных логах (если проблема была вчера или раньше)

    zgrep "actuator-bath-light" events.log.*.gz | grep "Good-Bye"

    Шаг 2. Разбор JSON-событий

    Инженер просматривает отфильтрованные записи за тот день, когда клиент зафиксировал сбой, и находит следующую аномальную последовательность:

    {"ts":1678890005000, "level":"INFO", "facility":"scenarios", "source_name":"Сценарий 'Good-Bye'", "message":"Сценарий запущен", "details":{"trigger":"wall_button_exit"}}
    

    ...

    {"ts":1678890006100, "level":"INFO", "facility":"device_control", "source_name":"Сценарий 'Good-Bye'", "message":"Отправлена команда на выключение", "details":{"target_device":"actuator-livingroom-main", "command":"OFF"}}

    {"ts":1678890006250, "level":"INFO", "facility":"device_control", "source_name":"actuator-livingroom-main", "message":"Статус подтвержден: OFF", "details":{"latency_ms": 150}}

    ...

    {"ts":1678890006800, "level":"INFO", "facility":"device_control", "source_name":"Сценарий 'Good-Bye'", "message":"Отправлена команда на выключение", "details":{"target_device":"actuator-bath-light", "command":"OFF"}}

    > ⚠️ ЗДЕСЬ АНОМАЛИЯ: Отсутствует сообщение о подтверждении статуса (ACK) OFF от 'actuator-bath-light'.

    ...

    {"ts":1678890007500, "level":"INFO", "facility":"scenarios", "source_name":"Сценарий 'Good-Bye'", "message":"Сценарий завершен", "details":{"duration_ms": 2500, "errors": 0}}

    Шаг 3. Технический анализ и выводы

    Вывод из лога однозначен: проблема не в том, что контроллер "забыл" отправить команду. Лог четко показывает: `Отправлена команда на выключение` для `actuator-bath-light`.

    Однако, в отличие от других устройств (`actuator-livingroom-main`), от реле ванной не пришло встречное сообщение-подтверждение о смене статуса (Acknowledge). Сценарий, работающий по принципу "выстрелил и забыл" (Fire-and-Forget), не дождавшись подтверждения, посчитал свою работу выполненной и успешно завершился (`"errors": 0`).

    Возможные физические причины:
  • Коллизия на шине Modbus: В момент отправки команды шина была занята опросом датчиков температур, пакет управления был искажен помехой.
  • Десинхронизация состояния: Само реле "задумалось" или перезагружалось в этот микромомент.
  • Проблемы с контактом: Физическое нарушение целостности линии связи (встречается реже, так как ошибка плавающая).
  • Шаг 4. Решение: Внедрение паттерна Retry с обратной связью

    Основываясь на данных из лога, инженер принимает решение изменить не физическую проводку, а логику работы сценария "Good-Bye" в Node-RED, переведя его из асинхронного режима работы в режим с контролем исполнения (Closed-loop control).

    Вместо программирования блокирующего цикла `while` (который "повесит" поток в Node-RED), инженер реализует паттерн Retry (Повторная попытка) с помощью связки узлов `Trigger` и `Switch`:

  • Отправка и ожидание: После отправки команды `OFF`, поток переходит в узел ожидания статуса. Устанавливается таймаут ожидания `t = 3000 мс` (3 секунды).
  • Механизм повтора: Если подтверждение статуса (`payload === false`) поступает до истечения 3 секунд, сценарий продолжается штатно.
  • Обработка таймаута: Если срабатывает таймаут, счетчик ошибок инкрементируется, и команда `OFF` отправляется повторно (до `max_retries = 3` раз).
  • Эскалация: Если после 3-й попытки ответа нет, в журнал пишется `ERROR`: `"Не получено подтверждение от actuator-bath-light. Устройство оффлайн"`, а клиенту отправляется Push-уведомление в Telegram.
  • > 💡 Итог: Журнал событий не только аппаратно оправдал контроллер (доказав, что команда уходила), но и указал на необходимость внедрения механизма отказоустойчивости на уровне софта.

    Практическое мини-задание: Имитация потери пакета

    Чтобы убедиться, что новая логика Node-RED с паттерном Retry работает корректно, проведите следующий тест:

    План тестирования:
  • Откройте консоль системы и запустите мониторинг логов в реальном времени: `tail -f /var/log/hi-system/events.log | grep "bath-light"`.
  • Физически отключите сигнальный кабель от реле в ванной комнате (или временно заблокируйте топик устройства в MQTT брокере, если используется Wi-Fi реле).
  • Нажмите кнопку сценария "Good-Bye".
  • Ожидаемый результат в консоли:
  • * Вы должны увидеть первую попытку: `"message":"Отправлена команда на выключение"`.

    * Через 3 секунды появится запись уровня `WARNING`: `"message":"Таймаут ожидания ACK. Попытка 2 из 3"`.

    * Еще через 3 секунды: `"message":"Таймаут ожидания ACK. Попытка 3 из 3"`.

    * Итоговое событие уровня `ERROR`: `"message":"Сбой сценария: actuator-bath-light недоступен"`.

  • Восстановите линию связи и повторите тест — сценарий должен завершиться с первого раза без `WARNING` записей.
  • Резюме: Золотые правила ведения журнала

    езюме: Золотые правила ведения журнала

  • Логируйте не только ошибки. Для полноценного аудита необходимо фиксировать все ключевые изменения состояний: активацию и деактивацию режимов (`Ночь`, `Отпуск`), действия пользователя (нажатие кнопок, изменение уставок), запуск и завершение критических сценариев. Соблюдайте четкую градацию:
  • * `DEBUG` — для отладки и сырых MQTT-пайлоадов (отключается в production).

    * `INFO` — для штатных событий (изменение уставки климата, постановка на охрану).

    * `WARNING` — для некритичных отклонений (потеря пинга до датчика температуры, таймаут ответа API).

    * `ERROR` / `CRITICAL` — для сбоев, требующих вмешательства (протечка, отказ реле котла).

  • Используйте единый, машиночитаемый формат. JSON — ваш лучший друг. Хранение логов в виде набора структурированных пар «ключ-значение» позволит легко интегрировать их с системами мониторинга (например, Grafana Loki, ELK stack), строить дашборды и настраивать сложные автоматические алерты по определенным тегам.
  • Настройте ротацию и архивацию. Переполнение диска (особенно на SD-картах Raspberry Pi или eMMC-накопителях хабов) из-за логов — это недопустимая ошибка для профессиональной инсталляции. Настройка утилиты `logrotate` — обязательный шаг.
  • Оптимальный стандарт для локального сервера: хранить логи 7–14 дней, максимальный размер одного файла — от 10 до 50 МБ.

    Пример базовой конфигурации `/etc/logrotate.d/smarthome`:

        /var/log/smarthome/*.log {

    daily # ежедневная проверка

    rotate 7 # хранить последние 7 архивов

    size 50M # ротировать, если файл достиг 50 МБ

    compress # сжимать старые логи

    missingok # не выдавать ошибку, если файла пока нет

    notifempty # не ротировать пустые файлы

    }

  • Регулярно просматривайте журналы. Даже если система кажется абсолютно стабильной, периодический просмотр логов поможет выявить скрытые проблемы до того, как они будут замечены клиентом. Рекомендуемый подход: события уровня `ERROR` должны немедленно отправляться пуш-уведомлением в Telegram инсталлятору, а события `WARNING` следует анализировать вручную раз в 2–4 недели.
  • 🛠 Практическое мини-задание

    Задача: Представьте, что вы пишете сценарий активации режима «Ушел из дома». Спроектируйте структуру JSON-лога для двух конкретных событий, чтобы затем их было легко отфильтровать в системе аналитики:
  • Успешная постановка дома на охрану (переход в режим «Ушел из дома»).
  • Критическая ошибка: при постановке на охрану контроллер не смог запереть входную дверь из-за заклинивания электрозамка.
  • > 💡 Ожидаемый результат (сверьте свое решение):

    >

    > Событие 1 (Успех):

    >

    > {

    > "timestamp": "2023-10-27T08:15:00Z",

    > "level": "INFO",

    > "source": "mode_manager",

    > "action": "ModeChange",

    > "state": "Away",

    > "initiator": "AppleHomeKit",

    > "message": "Режим Ушел из дома успешно активирован."

    > }

    >

    >

    > Событие 2 (Сбой):

    >

    > {

    > "timestamp": "2023-10-27T08:15:02Z",

    > "level": "ERROR",

    > "source": "lock_front_door",

    > "action": "LockDevice",

    > "error_code": "E_JAMMED",

    > "message": "Сбой автоматизации: Ригель замка заблокирован. Дверь не заперта."

    > }

    >

    Что дальше

    Мы разобрали фундаментальную базу: почему без Audit Trail невозможно поддерживать и отлаживать умный дом, и какие правила необходимо соблюдать при проектировании журналов. Теперь, когда у нас есть мощный и понятный инструмент для диагностики, мы готовы перейти к архитектуре самих автоматизаций.

    В следующих уроках мы будем проектировать предметные подсистемы (управление освещением, климатом, безопасностью) с учетом режимов и приоритетов. Обязательным шагом на этапе проектирования каждого сценария будет интеграция с нашим машиночитаемым логгером. Вы на практике увидите, как своевременная отправка правильного JSON-объекта превращает журнал из абстрактной теории в незаменимого помощника инсталлятора, способного за секунды ответить на вопрос: «Почему вчера ночью включился свет в коридоре?».