1
0
mirror of https://github.com/esphome/esphome.git synced 2026-02-08 08:41:59 +00:00

Merge remote-tracking branch 'origin/syslog_no_heap' into integration

This commit is contained in:
J. Nick Koston
2025-12-20 11:26:06 -10:00
3 changed files with 52 additions and 15 deletions

View File

@@ -41,31 +41,39 @@ void Syslog::log_(const int level, const char *tag, const char *message, size_t
len -= 11;
}
// Build syslog packet on stack - 508 is max UDP packet size
// Build syslog packet on stack (508 bytes chosen as practical limit for syslog over UDP)
char packet[508];
size_t offset = 0;
size_t remaining = sizeof(packet);
// Write PRI
int ret = snprintf(packet, sizeof(packet), "<%d>", pri);
if (ret > 0)
offset = ret;
// Write PRI - abort if this fails as packet would be malformed
int ret = snprintf(packet, remaining, "<%d>", pri);
if (ret <= 0 || static_cast<size_t>(ret) >= remaining) {
return;
}
offset = ret;
remaining -= ret;
// Write timestamp directly into packet (RFC 5424: use "-" if time not valid)
// Write timestamp directly into packet (RFC 5424: use "-" if time not valid or strftime fails)
auto now = this->time_->now();
if (now.is_valid()) {
offset += now.strftime(packet + offset, sizeof(packet) - offset, "%b %e %H:%M:%S");
} else {
size_t ts_written = now.is_valid() ? now.strftime(packet + offset, remaining, "%b %e %H:%M:%S") : 0;
if (ts_written > 0) {
offset += ts_written;
remaining -= ts_written;
} else if (remaining > 0) {
packet[offset++] = '-';
remaining--;
}
// Write hostname, tag, and message
ret = snprintf(packet + offset, sizeof(packet) - offset, " %s %s: %.*s", App.get_name().c_str(), tag, (int) len,
message);
if (ret > 0)
offset += ret;
ret = snprintf(packet + offset, remaining, " %s %s: %.*s", App.get_name().c_str(), tag, (int) len, message);
if (ret > 0) {
// snprintf returns chars that would be written; clamp to actual buffer space
offset += std::min(static_cast<size_t>(ret), remaining > 0 ? remaining - 1 : 0);
}
if (offset > 0) {
this->parent_->send_packet(reinterpret_cast<const uint8_t *>(packet), std::min(offset, sizeof(packet) - 1));
this->parent_->send_packet(reinterpret_cast<const uint8_t *>(packet), offset);
}
}

View File

@@ -16,6 +16,11 @@ api:
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
- service: log_short_message
then:
- lambda: |-
// Log a short message that should arrive complete (not truncated)
ESP_LOGI("shorttest", "BEGIN|SHORT_MESSAGE_CONTENT|FINISH");
logger:
level: DEBUG

View File

@@ -33,7 +33,7 @@ class ParsedSyslogMessage(TypedDict):
# Example: <134>Dec 20 14:30:45 syslog-test app: [D][app:029]: Running...
SYSLOG_PATTERN = re.compile(
r"<(\d+)>" # PRI (priority = facility * 8 + severity)
r"(\S+ +\d+ \d+:\d+:\d+|-)" # TIMESTAMP (BSD format or NILVALUE "-")
r"(\S+ +\d+ \d+:\d+:\d+|-)" # TIMESTAMP (BSD-style "%b %e %H:%M:%S", e.g. "Dec 20 14:30:45", or NILVALUE "-")
r" (\S+)" # HOSTNAME
r" (\S+):" # TAG
r" (.*)" # MESSAGE
@@ -258,3 +258,27 @@ async def test_syslog(
assert "|END" not in trunc_msg, (
"Message should be truncated before END marker"
)
# Test short message - should arrive complete (not truncated)
short_service = next(
(s for s in services if s.name == "log_short_message"), None
)
assert short_service is not None, "log_short_message service not found"
await client.execute_service(short_service, {})
try:
short_msg = await receiver.wait_for_pattern(r"shorttest.*BEGIN\|")
except TimeoutError:
pytest.fail(
f"Short test message not received. Got: {receiver.messages[-10:]}"
)
# Verify short message arrived complete with both markers
assert "BEGIN|" in short_msg, "Short message missing BEGIN marker"
assert "|FINISH" in short_msg, (
f"Short message truncated unexpectedly: {short_msg}"
)
assert "SHORT_MESSAGE_CONTENT" in short_msg, (
f"Short message content missing: {short_msg}"
)