ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
esphome_syslog.cpp
Go to the documentation of this file.
1#include "esphome_syslog.h"
2
5#include "esphome/core/hal.h"
6#include "esphome/core/time.h"
7
8namespace esphome::syslog {
9
10// Map log levels to syslog severity using an array, indexed by ESPHome log level (1-7)
11static constexpr uint8_t LOG_LEVEL_TO_SYSLOG_SEVERITY[] PROGMEM = {
12 3, // NONE
13 3, // ERROR
14 4, // WARN
15 5, // INFO
16 6, // CONFIG
17 7, // DEBUG
18 7, // VERBOSE
19 7 // VERY_VERBOSE
20};
21
22static constexpr char SECRET_BEGIN[] = ESPHOME_LOG_SECRET_BEGIN;
23static constexpr char SECRET_END[] = ESPHOME_LOG_SECRET_END;
24static constexpr size_t SECRET_BEGIN_LEN = sizeof(SECRET_BEGIN) - 1;
25static constexpr size_t SECRET_END_LEN = sizeof(SECRET_END) - 1;
26
27static bool starts_with_marker(const char *message, size_t remaining, const char *marker, size_t marker_len) {
28 return message[0] == '\033' && remaining >= marker_len && memcmp(message, marker, marker_len) == 0;
29}
30
31static size_t append_bytes(char *buf, size_t size, size_t pos, const char *data, size_t len) {
32 if (pos >= size) {
33 return size;
34 }
35 size_t n = std::min(len, size - pos - 1);
36 memcpy(buf + pos, data, n);
37 pos += n;
38 buf[pos] = '\0';
39 return pos;
40}
41
42size_t append_redacted_message(char *buf, size_t size, size_t pos, const char *message, size_t len) {
43 if (memchr(message, '\033', len) == nullptr) {
44 return append_bytes(buf, size, pos, message, len);
45 }
46 size_t i = 0;
47 while (i < len) {
48 if (starts_with_marker(message + i, len - i, SECRET_BEGIN, SECRET_BEGIN_LEN)) {
49 pos = buf_append_str(buf, size, pos, "[redacted]");
50 i += SECRET_BEGIN_LEN;
51 while (i < len && !starts_with_marker(message + i, len - i, SECRET_END, SECRET_END_LEN)) {
52 i++;
53 }
54 if (i < len) {
55 i += SECRET_END_LEN;
56 }
57 continue;
58 }
59 size_t start = i;
60 while (i < len && !starts_with_marker(message + i, len - i, SECRET_BEGIN, SECRET_BEGIN_LEN)) {
61 i++;
62 }
63 pos = append_bytes(buf, size, pos, message + start, i - start);
64 }
65 return pos;
66}
67
70 this, [](void *self, uint8_t level, const char *tag, const char *message, size_t message_len) {
71 static_cast<Syslog *>(self)->on_log(level, tag, message, message_len);
72 });
73}
74
75void Syslog::on_log(uint8_t level, const char *tag, const char *message, size_t message_len) {
76 this->log_(level, tag, message, message_len);
77}
78
79void Syslog::log_(const int level, const char *tag, const char *message, size_t message_len) const {
80 if (level > this->log_level_)
81 return;
82 // Syslog PRI calculation: facility * 8 + severity
83 int severity = 7;
84 if ((unsigned) level <= 7) {
85 severity = progmem_read_byte(&LOG_LEVEL_TO_SYSLOG_SEVERITY[level]);
86 }
87 int pri = this->facility_ * 8 + severity;
88
89 size_t len = message_len;
90 // remove color formatting
91 if (this->strip_ && message[0] == 0x1B && len > 11) {
92 message += 7;
93 len -= 11;
94 }
95
96 // Build syslog packet on stack (508 bytes chosen as practical limit for syslog over UDP)
97 char packet[508];
98 size_t offset = 0;
99 // Write PRI - abort if this fails as packet would be malformed
100 offset = buf_append_printf(packet, sizeof(packet), 0, "<%d>", pri);
101 if (offset == 0) {
102 return; // PRI always produces at least "<0>" (3 chars), so 0 means error
103 }
104 auto now = this->time_->now();
105 if (this->format_ == SYSLOG_FORMAT_RFC5424) {
106 offset = buf_append_str(packet, sizeof(packet), offset, "1 ");
107
108 char timestamp[32];
109 size_t timestamp_len = now.is_valid() ? now.strftime(timestamp, sizeof(timestamp), "%Y-%m-%dT%H:%M:%S%z") : 0;
110 if (timestamp_len == 24) {
111 // ESPTime formats the numeric offset as +HHMM. RFC 3339 requires +HH:MM.
112 timestamp[25] = '\0';
113 timestamp[24] = timestamp[23];
114 timestamp[23] = timestamp[22];
115 timestamp[22] = ':';
116 offset = buf_append_printf(packet, sizeof(packet), offset, "%s", timestamp);
117 } else {
118 offset = buf_append_str(packet, sizeof(packet), offset, "-");
119 }
120 offset = buf_append_printf(packet, sizeof(packet), offset, " %s %s - - - ", App.get_name().c_str(), tag);
121 } else {
122 // RFC 3164 has no NILVALUE. If the clock is invalid, omit TIMESTAMP so a relay can add it.
123 if (now.is_valid()) {
124 offset += now.strftime(packet + offset, sizeof(packet) - offset, "%b %e %H:%M:%S ");
125 }
126 offset = buf_append_printf(packet, sizeof(packet), offset, "%s %s: ", App.get_name().c_str(), tag);
127 }
128 offset = append_redacted_message(packet, sizeof(packet), offset, message, len);
129 // Clamp to exclude null terminator position if buffer was filled
130 if (offset >= sizeof(packet)) {
131 offset = sizeof(packet) - 1;
132 }
133
134 if (offset > 0) {
135 this->parent_->send_packet(reinterpret_cast<const uint8_t *>(packet), offset);
136 }
137}
138
139} // namespace esphome::syslog
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
constexpr const char * c_str() const
Definition string_ref.h:78
void add_log_callback(void *instance, void(*fn)(void *, uint8_t, const char *, const char *, size_t))
Register a log callback to receive log messages.
Definition logger.h:194
time::RealTimeClock * time_
void log_(int level, const char *tag, const char *message, size_t message_len) const
void on_log(uint8_t level, const char *tag, const char *message, size_t message_len)
ESPTime now()
Get the time in the currently defined timezone.
bool send_packet(const uint8_t *data, size_t size)
Returns false if the packet could not be sent to every destination.
const LogString * message
Definition component.cpp:35
Logger * global_logger
Definition logger.cpp:276
size_t append_redacted_message(char *buf, size_t size, size_t pos, const char *message, size_t len)
Append len bytes of message to buf at pos, replacing every LOG_SECRET() region with "[redacted]".
size_t buf_append_str(char *buf, size_t size, size_t pos, const char *str)
Safely append a string to buffer, returning new position (capped at size).
Definition helpers.h:1304
const void size_t len
Definition hal.h:68
uint16_t size
Definition helpers.cpp:34
size_t size_t pos
Definition helpers.h:1255
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51
uint16_t timestamp
Definition tt21100.cpp:2