DeterministicESPAsyncWebServer v6.27.1
Zero-allocation, bounded-execution async HTTP server for ESP32
Loading...
Searching...
No Matches
syslog.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file syslog.cpp
6 * @brief RFC 5424 syslog client: line formatter + UDP send.
7 */
8
10
11#if DETWS_ENABLE_SYSLOG
12
14#include <stdio.h>
15#include <string.h>
16
17// All syslog client state, owned by one instance (internal linkage): the collector endpoint,
18// the host/app identity, the facility, the ready flag, and the format scratch (all BSS, no
19// heap), grouped so it is one named owner, unreachable from any other translation unit.
20struct SyslogCtx
21{
22 char server_ip[16] = {0}; // "255.255.255.255" + NUL
23 uint16_t port = DETWS_SYSLOG_DEFAULT_PORT;
24 char hostname[DETWS_SYSLOG_FIELD_MAX] = {0};
25 char appname[DETWS_SYSLOG_FIELD_MAX] = {0};
26 SyslogFacility facility = SyslogFacility::SYSLOG_FAC_LOCAL0;
27 bool ready = false;
28 char buf[DETWS_SYSLOG_MSG_MAX];
29};
30static SyslogCtx s_syslog;
31
32static void copy_field(char *dst, size_t cap, const char *src)
33{
34 if (!src || !src[0])
35 {
36 dst[0] = '\0';
37 return;
38 }
39 strncpy(dst, src, cap - 1);
40 dst[cap - 1] = '\0';
41}
42
43void syslog_init(const char *server_ip, uint16_t port, const char *hostname, const char *appname,
44 SyslogFacility facility)
45{
46 copy_field(s_syslog.server_ip, sizeof(s_syslog.server_ip), server_ip);
47 s_syslog.port = port;
48 copy_field(s_syslog.hostname, sizeof(s_syslog.hostname), hostname);
49 copy_field(s_syslog.appname, sizeof(s_syslog.appname), appname);
50 s_syslog.facility = facility;
51 s_syslog.ready = (s_syslog.server_ip[0] != '\0');
52}
53
54size_t syslog_format(char *out, size_t cap, SyslogFacility facility, SyslogSeverity severity, const char *hostname,
55 const char *appname, const char *msg)
56{
57 if (!out || cap == 0)
58 return 0;
59 // RFC 5424 6.2.1: PRIVAL = facility*8 + severity, range 0..191. The numeric priority is the wire
60 // encoding of the level pair, so cast to int here (the boundary). facility/severity are unsigned
61 // enums, so PRI is never negative; clamp only the top so an over-range value cast in by the caller
62 // can never emit a malformed PRI (e.g. <400>).
63 int pri = (int)facility * 8 + (int)severity;
64 if (pri > 191)
65 pri = 191;
66 const char *h = (hostname && hostname[0]) ? hostname : "-";
67 const char *a = (appname && appname[0]) ? appname : "-";
68 // <PRI>VERSION SP TIMESTAMP SP HOSTNAME SP APP-NAME SP PROCID SP MSGID SP SD SP MSG
69 // TIMESTAMP/PROCID/MSGID/STRUCTURED-DATA are NILVALUE ("-").
70 int n = snprintf(out, cap, "<%d>1 - %s %s - - - %s", pri, h, a, msg ? msg : "");
71 if (n < 0 || (size_t)n >= cap)
72 return 0;
73 return (size_t)n;
74}
75
76bool syslog_log(SyslogSeverity severity, const char *msg)
77{
78 if (!s_syslog.ready)
79 return false;
80 size_t n = syslog_format(s_syslog.buf, sizeof(s_syslog.buf), s_syslog.facility, severity, s_syslog.hostname,
81 s_syslog.appname, msg);
82 if (n == 0)
83 return false;
84 return det_udp_sendto(s_syslog.server_ip, s_syslog.port, (const uint8_t *)s_syslog.buf, n);
85}
86
87#endif // DETWS_ENABLE_SYSLOG
#define DETWS_SYSLOG_DEFAULT_PORT
Default syslog collector UDP port (RFC 5426 well-known 514; overridable at runtime via syslog_init an...
#define DETWS_SYSLOG_FIELD_MAX
Maximum syslog HOSTNAME / APP-NAME field length (including NUL).
#define DETWS_SYSLOG_MSG_MAX
Maximum formatted syslog datagram length in bytes (RFC 5424 line).
Zero-heap RFC 5424 syslog client over UDP.
bool det_udp_sendto(const char *dst_ip, uint16_t dst_port, const uint8_t *data, size_t len)
Send a UDP datagram to an arbitrary destination (outbound client).
Definition udp.cpp:196
Layer 4 (Transport) - connectionless UDP datagram service.