Files
zh_syslog/wiki_ru.md
T
2026-10-17 19:42:23 +03:00

12 KiB
Raw Blame History

Описание компонента zh_syslog

Общие сведения

zh_syslog — это лёгкий UDP-клиент syslog для ESP-IDF, отправляющий сообщения на удалённый syslog-сервер в формате RFC 5424.

Компонент оборачивает стандартный BSD-сокет UDP и предоставляет несколько функций для инициализации, отправки сообщений и освобождения ресурсов.

Основные возможности

  1. Все коды facility (0–23) — поддержка стандартных значений по RFC 5424, включая LOCAL0–LOCAL7 для локального использования.
  2. Все уровни severity (0–7) — от ZH_EMERG до ZH_DEBUG.
  3. Формат RFC 5424 — автоматический расчёт приоритета <PRI> и генерация заголовка сообщения.
  4. Отправка по UDP — сокеты не устанавливают соединения, сообщения доставляются дейтаграммами.
  5. Двухуровневая работа с памятью — короткие сообщения форматируются в стековом буфере, длинные аллоцируются в куче точным размером.
  6. Проверка входных аргументов — валидация IP-адреса, порта и диапазона кодов на этапе вызова.
  7. Потокобезопасные init/deinit — защита состояния флагом инициализации (см. раздел «Ограничения» для send).

Внутреннее устройство

Структура zh_syslog_init_config_t

Публичная конфигурационная структура, передаваемая в zh_syslog_init().

Поле Тип Описание
syslog_ip char[16] IPv4-адрес syslog-сервера в виде строки (макс. 15 символов + '\0')
syslog_port uint32_t UDP-порт сервера (диапазон 1–65535, по умолчанию 514)

Для заполнения значений по умолчанию используется макрос ZH_SYSLOG_INIT_CONFIG_DEFAULT() (IP 192.168.0.1, порт 514).

Внутренние переменные модуля

Не входят в публичный API и хранятся как static в zh_syslog.c.

Переменная Тип Описание
_syslog_server struct sockaddr_in Адрес сервера: IPv4, порт в сетевом порядке, семья AF_INET
_socket volatile int Дескриптор UDP-сокета, -1 когда клиент не инициализирован
_is_initialized volatile bool Флаг состояния: true после успешного init

Флаг _is_initialized используется как простой сторож состояния: init отказывает при уже инициализированном клиенте, deinit и send отказывают при неинициализированном.

Управление памятью

  • Короткое сообщение (помещающееся в стековый буфер char stack_buf[256]) форматируется прямо в стеке — аллокации в куче не происходит.
  • Если snprintf вернул len >= 256 (строка обрезана), память выделяется в куче ровно под len + 1 байт через heap_caps_calloc(..., MALLOC_CAP_8BIT).
  • Флаг heap_allocated отслеживает, нужно ли освобождать буфер: heap_caps_free() вызывается владением только для кучного буфера — и при успехе, и при ошибке форматирования.

API (интерфейс)

Инициализация и освобождение

Функция Описание
zh_syslog_init() Создаёт UDP-сокет и настраивает параметры соединения с сервером.
zh_syslog_deinit() Закрывает сокет и сбрасывает состояние клиента.

Отправка сообщений

Функция Описание
zh_syslog_send() Форматирует сообщение по RFC 5424 и отправляет его на настроенный сервер.

Типы и значения

Элемент Описание
zh_syslog_facility_code_t Перечисление кодов facility ZH_KERN…ZH_LOCAL7 (0–23).
zh_syslog_severity_code_t Перечисление уровней severity ZH_EMERG…ZH_DEBUG (0–7).
zh_syslog_init_config_t Структура конфигурации (IP и порт сервера).
ZH_SYSLOG_INIT_CONFIG_DEFAULT() Макрос значений конфигурации по умолчанию.

Потокобезопасность

  1. Флаг состояния _is_initialized — volatile bool, используется как сторож в init/deinit/send для защиты от повторной инициализации и от работы без инициализации.
  2. Дескриптор сокета _socket — volatile int, обновляется при init/deinit.

Мьютексы и критические секции не используются. Проверка флага и последующая работа с сокетом не образуют атомарной операции, поэтому zh_syslog_send() не является потокобезопасной — подробности в разделе «Ограничения».

Алгоритмы

Расчёт приоритета

  • Приоритет <PRI> считается по формуле RFC 5424: pri = facility * 8 + severity.
  • Значение подставляется в начало сообщения в угловых скобках: <%d>1 - ....

Формат сообщения

Шаблон генерируемого сообщения:

<PRI>1 - HOSTNAME APP-NAME - - - MESSAGE

Поля PROCID, MSGID и STRUCTURED-DATA не заполняются и заменяются на - согласно RFC 5424.

Выбор буфера под размер сообщения

  • Сообщение форматируется в стековый буфер stack_buf[256] через snprintf, который возвращает полную длину строки.
  • Если возвращённая длина меньше размера буфера — сокету передаётся стековый буфер.
  • Если длина >= 256 — выделяется кучный буфер точного размера len + 1, сообщение форматируется повторно и отправляется, после чего буфер освобождается.

Примеры использования

Базовый пример

#include "zh_syslog.h"

void app_main(void)
{
    // Сеть (Wi-Fi/Ethernet) должна быть поднята до init
    zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
    strcpy(config.syslog_ip, "192.168.1.100");
    config.syslog_port = 514;

    if (zh_syslog_init(&config) == ESP_OK)
    {
        // Отправка сообщения с приоритетом USER/INFO
        zh_syslog_send(ZH_USER, ZH_INFO, "esp32_node", "my_app", "Hello syslog");
    }

    // Освобождение ресурсов
    zh_syslog_deinit();
}

Работа с разными уровнями и facility

#include "zh_syslog.h"

void log_events(void)
{
    zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
    strcpy(config.syslog_ip, "10.0.0.5");
    zh_syslog_init(&config);

    // Разные facility и severity для разных событий
    zh_syslog_send(ZH_KERN,     ZH_ERR,     "dev1", "fw",   "kernel error");
    zh_syslog_send(ZH_LOCAL0,   ZH_WARNING, "dev1", "fw",   "sensor timeout");
    zh_syslog_send(ZH_DAEMON,   ZH_DEBUG,   "dev1", "fw",   "loop tick");

    zh_syslog_deinit();
}

Отправка длинного сообщения

#include "zh_syslog.h"

void send_long_message(void)
{
    zh_syslog_init_config_t config = ZH_SYSLOG_INIT_CONFIG_DEFAULT();
    strcpy(config.syslog_ip, "10.0.0.5");
    zh_syslog_init(&config);

    // Сообщение длиннее стекового буфера уходит через кучу
    char big[512];
    memset(big, 'A', sizeof(big) - 1);
    big[sizeof(big) - 1] = '\0';
    zh_syslog_send(ZH_USER, ZH_INFO, "dev1", "app", big);

    zh_syslog_deinit();
}

Логирование

Компонент использует ESP-IDF логирование с тегом "zh_syslog".

esp_log_level_set("zh_syslog", ESP_LOG_ERROR);

Ошибки логируются с указанием файла, строки и имени кода ошибки (esp_err_to_name).

Ошибки

Код Описание
ESP_OK Успех
ESP_ERR_INVALID_ARG Неверный аргумент: NULL-указатель, порт вне диапазона, некорректный IP или код вне диапазона
ESP_ERR_INVALID_STATE Клиент уже инициализирован (init) либо ещё не инициализирован (deinit/send)
ESP_ERR_NO_MEM Не удалось выделить память под длинное сообщение
ESP_FAIL Ошибка создания сокета, форматирования или отправки UDP

Ограничения

  1. zh_syslog_send() не потокобезопасна — проверка флага и работа с сокетом не атомарны; при конкурентном вызове из нескольких задач нужна внешняя синхронизация.
  2. Только IPv4 — адрес сервера задаётся строкой и разбирается через inet_pton(AF_INET, ...), IPv6 не поддерживается.
  3. Без гарантии доставки — UDP не подтверждает доставку, потерю сообщений компонент не отслеживает.
  4. Длина IP ограничена 15 символами — поле syslog_ip[16] вмещает только IPv4-строку.
  5. Требуется активная сеть — вызывать zh_syslog_init() можно только после установления сетевого соединения.
  6. Стек 256 байт на сообщение — сообщения длиннее буфера приводят к аллокации в куче на каждую отправку.

Требования для сборки

  • ESP-IDF версии 5.0 и выше.
  • FreeRTOS (входит в состав ESP-IDF).
  • lwIP (стек сокетов, входит в состав ESP-IDF).

Установка

cd ваш_проект/components
git clone https://github.com/aZholtikov/zh_syslog
#include "zh_syslog.h"