syslog: implement rate limiting

enabling a lot of logging messages, e.g., by setting verbose as the
default level, can make the system unresponsive if syslogging is
enabled. to protect against this, a rate limiting algorithm is
implemented in the syslogger.
This commit is contained in:
Bernhard Kirchen
2025-06-27 22:01:13 +02:00
committed by Thomas Basler
parent 5ba3446ad4
commit 20003a0f92
2 changed files with 53 additions and 0 deletions

View File

@@ -20,6 +20,7 @@ private:
return _address != INADDR_NONE; return _address != INADDR_NONE;
} }
static uint8_t calculatePrival(uint8_t facility, char errorCode); static uint8_t calculatePrival(uint8_t facility, char errorCode);
bool consumeToken();
Task _loopTask; Task _loopTask;
std::mutex _mutex; std::mutex _mutex;
@@ -30,6 +31,14 @@ private:
String _header; String _header;
uint16_t _port; uint16_t _port;
bool _enabled; bool _enabled;
static constexpr uint32_t RATE_LIMIT_WINDOW_MS = 1000;
static constexpr size_t RATE_LIMIT_MAX_TOKENS = 100;
size_t _available_tokens = RATE_LIMIT_MAX_TOKENS;
uint32_t _last_token_refill_millis = 0;
size_t _rate_limited_packets = 0;
uint32_t _last_rate_limit_warning_millis = 0;
static constexpr uint32_t RATE_LIMIT_WARNING_INTERVAL_MS = 1000;
}; };
extern SyslogLogger Syslog; extern SyslogLogger Syslog;

View File

@@ -8,6 +8,7 @@
#include "defaults.h" #include "defaults.h"
#include <ESPmDNS.h> #include <ESPmDNS.h>
#include <HardwareSerial.h> #include <HardwareSerial.h>
#include <algorithm>
#undef TAG #undef TAG
static const char* TAG = "syslog"; static const char* TAG = "syslog";
@@ -65,6 +66,27 @@ void SyslogLogger::write(const uint8_t* buffer, size_t size)
return; return;
} }
// Check rate limiting using token bucket
if (!consumeToken()) {
if (_rate_limited_packets == 0) {
_last_rate_limit_warning_millis = millis();
}
++_rate_limited_packets;
return;
}
if (_rate_limited_packets > 0) {
uint32_t elapsed = (millis() - _last_rate_limit_warning_millis);
if (elapsed > RATE_LIMIT_WARNING_INTERVAL_MS) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "Rate limited %d packets in the last %dms",
_rate_limited_packets, elapsed);
Serial.println(buffer);
_rate_limited_packets = 0;
_last_rate_limit_warning_millis = millis();
}
}
String header = "<"; String header = "<";
header += String(calculatePrival(1, buffer[0])); header += String(calculatePrival(1, buffer[0]));
@@ -82,6 +104,26 @@ void SyslogLogger::write(const uint8_t* buffer, size_t size)
_udp.endPacket(); _udp.endPacket();
} }
bool SyslogLogger::consumeToken()
{
uint32_t now = millis();
uint32_t elapsed = now - _last_token_refill_millis;
size_t new_tokens = RATE_LIMIT_MAX_TOKENS * elapsed / RATE_LIMIT_WINDOW_MS;
if (new_tokens > 0) {
_available_tokens = std::min(_available_tokens + new_tokens, RATE_LIMIT_MAX_TOKENS);
_last_token_refill_millis = now;
}
if (_available_tokens > 0) {
--_available_tokens;
return true;
}
return false;
}
void SyslogLogger::disable() void SyslogLogger::disable()
{ {
ESP_LOGI(TAG, "Disable"); ESP_LOGI(TAG, "Disable");
@@ -103,6 +145,8 @@ void SyslogLogger::enable()
std::lock_guard<std::mutex> lock(_mutex); std::lock_guard<std::mutex> lock(_mutex);
_enabled = true; _enabled = true;
_available_tokens = RATE_LIMIT_MAX_TOKENS;
_last_token_refill_millis = millis();
} }
bool SyslogLogger::resolveAndStart() bool SyslogLogger::resolveAndStart()