12 KiB
Описание компонента zh_syslog
Общие сведения
zh_syslog — это лёгкий UDP-клиент syslog для ESP-IDF, отправляющий сообщения на удалённый syslog-сервер в формате RFC 5424.
Компонент оборачивает стандартный BSD-сокет UDP и предоставляет несколько функций для инициализации, отправки сообщений и освобождения ресурсов.
Основные возможности
- Все коды facility (0–23) — поддержка стандартных значений по RFC 5424, включая
LOCAL0–LOCAL7для локального использования. - Все уровни severity (0–7) — от
ZH_EMERGдоZH_DEBUG. - Формат RFC 5424 — автоматический расчёт приоритета
<PRI>и генерация заголовка сообщения. - Отправка по UDP — сокеты не устанавливают соединения, сообщения доставляются дейтаграммами.
- Двухуровневая работа с памятью — короткие сообщения форматируются в стековом буфере, длинные аллоцируются в куче точным размером.
- Проверка входных аргументов — валидация IP-адреса, порта и диапазона кодов на этапе вызова.
- Потокобезопасные
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() |
Макрос значений конфигурации по умолчанию. |
Потокобезопасность
- Флаг состояния
_is_initialized—volatile bool, используется как сторож вinit/deinit/sendдля защиты от повторной инициализации и от работы без инициализации. - Дескриптор сокета
_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 |
Ограничения
zh_syslog_send()не потокобезопасна — проверка флага и работа с сокетом не атомарны; при конкурентном вызове из нескольких задач нужна внешняя синхронизация.- Только IPv4 — адрес сервера задаётся строкой и разбирается через
inet_pton(AF_INET, ...), IPv6 не поддерживается. - Без гарантии доставки — UDP не подтверждает доставку, потерю сообщений компонент не отслеживает.
- Длина IP ограничена 15 символами — поле
syslog_ip[16]вмещает только IPv4-строку. - Требуется активная сеть — вызывать
zh_syslog_init()можно только после установления сетевого соединения. - Стек 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"