Files
2026-10-17 19:42:23 +03:00

215 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Описание компонента zh_syslog
## Общие сведения
**zh_syslog** — это лёгкий UDP-клиент syslog для ESP-IDF, отправляющий сообщения на удалённый syslog-сервер в формате [RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424).
Компонент оборачивает стандартный 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`, сообщение форматируется повторно и отправляется, после чего буфер освобождается.
## Примеры использования
### Базовый пример
```c
#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
```c
#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();
}
```
### Отправка длинного сообщения
```c
#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"`.
```c
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).
## Установка
```bash
cd ваш_проект/components
git clone https://github.com/aZholtikov/zh_syslog
```
```c
#include "zh_syslog.h"
```