Reduce Logger memory usage by optimizing variable sizes (#9161)

This commit is contained in:
J. Nick Koston 2025-06-23 04:06:02 +02:00 committed by GitHub
parent dc5cbd4df8
commit 59889a6286
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 76 additions and 63 deletions

View File

@ -184,7 +184,9 @@ CONFIG_SCHEMA = cv.All(
{ {
cv.GenerateID(): cv.declare_id(Logger), cv.GenerateID(): cv.declare_id(Logger),
cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int, cv.Optional(CONF_BAUD_RATE, default=115200): cv.positive_int,
cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.validate_bytes, cv.Optional(CONF_TX_BUFFER_SIZE, default=512): cv.All(
cv.validate_bytes, cv.int_range(min=160, max=65535)
),
cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean, cv.Optional(CONF_DEASSERT_RTS_DTR, default=False): cv.boolean,
cv.SplitDefault( cv.SplitDefault(
CONF_TASK_LOG_BUFFER_SIZE, CONF_TASK_LOG_BUFFER_SIZE,

View File

@ -24,7 +24,7 @@ static const char *const TAG = "logger";
// - Messages are serialized through main loop for proper console output // - Messages are serialized through main loop for proper console output
// - Fallback to emergency console logging only if ring buffer is full // - Fallback to emergency console logging only if ring buffer is full
// - WITHOUT task log buffer: Only emergency console output, no callbacks // - WITHOUT task log buffer: Only emergency console output, no callbacks
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
if (level > this->level_for(tag)) if (level > this->level_for(tag))
return; return;
@ -46,8 +46,8 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
bool message_sent = false; bool message_sent = false;
#ifdef USE_ESPHOME_TASK_LOG_BUFFER #ifdef USE_ESPHOME_TASK_LOG_BUFFER
// For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag, message_sent =
static_cast<uint16_t>(line), current_task, format, args); this->log_buffer_->send_message_thread_safe(level, tag, static_cast<uint16_t>(line), current_task, format, args);
#endif // USE_ESPHOME_TASK_LOG_BUFFER #endif // USE_ESPHOME_TASK_LOG_BUFFER
// Emergency console logging for non-main tasks when ring buffer is full or disabled // Emergency console logging for non-main tasks when ring buffer is full or disabled
@ -58,7 +58,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
// Maximum size for console log messages (includes null terminator) // Maximum size for console log messages (includes null terminator)
static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144; static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
int buffer_at = 0; // Initialize buffer position uint16_t buffer_at = 0; // Initialize buffer position
this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at, this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
MAX_CONSOLE_LOG_MSG_SIZE); MAX_CONSOLE_LOG_MSG_SIZE);
this->write_msg_(console_buffer); this->write_msg_(console_buffer);
@ -69,7 +69,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
} }
#else #else
// Implementation for all other platforms // Implementation for all other platforms
void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT void HOT Logger::log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args) { // NOLINT
if (level > this->level_for(tag) || global_recursion_guard_) if (level > this->level_for(tag) || global_recursion_guard_)
return; return;
@ -85,7 +85,7 @@ void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *
#ifdef USE_STORE_LOG_STR_IN_FLASH #ifdef USE_STORE_LOG_STR_IN_FLASH
// Implementation for ESP8266 with flash string support. // Implementation for ESP8266 with flash string support.
// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266. // Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, void Logger::log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
va_list args) { // NOLINT va_list args) { // NOLINT
if (level > this->level_for(tag) || global_recursion_guard_) if (level > this->level_for(tag) || global_recursion_guard_)
return; return;
@ -122,7 +122,7 @@ void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStr
} }
#endif // USE_STORE_LOG_STR_IN_FLASH #endif // USE_STORE_LOG_STR_IN_FLASH
inline int Logger::level_for(const char *tag) { inline uint8_t Logger::level_for(const char *tag) {
auto it = this->log_levels_.find(tag); auto it = this->log_levels_.find(tag);
if (it != this->log_levels_.end()) if (it != this->log_levels_.end())
return it->second; return it->second;
@ -195,13 +195,13 @@ void Logger::loop() {
#endif #endif
void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; } void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; } void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; }
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
UARTSelection Logger::get_uart() const { return this->uart_; } UARTSelection Logger::get_uart() const { return this->uart_; }
#endif #endif
void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) { void Logger::add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback) {
this->log_callback_.add(std::move(callback)); this->log_callback_.add(std::move(callback));
} }
float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; } float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
@ -230,7 +230,7 @@ void Logger::dump_config() {
} }
} }
void Logger::set_log_level(int level) { void Logger::set_log_level(uint8_t level) {
if (level > ESPHOME_LOG_LEVEL) { if (level > ESPHOME_LOG_LEVEL) {
level = ESPHOME_LOG_LEVEL; level = ESPHOME_LOG_LEVEL;
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]); ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);

View File

@ -61,7 +61,7 @@ static const char *const LOG_LEVEL_LETTERS[] = {
* *
* Advanced configuration (pin selection, etc) is not supported. * Advanced configuration (pin selection, etc) is not supported.
*/ */
enum UARTSelection { enum UARTSelection : uint8_t {
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
UART_SELECTION_DEFAULT = 0, UART_SELECTION_DEFAULT = 0,
UART_SELECTION_UART0, UART_SELECTION_UART0,
@ -129,10 +129,10 @@ class Logger : public Component {
#endif #endif
/// Set the default log level for this logger. /// Set the default log level for this logger.
void set_log_level(int level); void set_log_level(uint8_t level);
/// Set the log level of the specified tag. /// Set the log level of the specified tag.
void set_log_level(const std::string &tag, int log_level); void set_log_level(const std::string &tag, uint8_t log_level);
int get_log_level() { return this->current_level_; } uint8_t get_log_level() { return this->current_level_; }
// ========== INTERNAL METHODS ========== // ========== INTERNAL METHODS ==========
// (In most use cases you won't need these) // (In most use cases you won't need these)
@ -140,19 +140,20 @@ class Logger : public Component {
void pre_setup(); void pre_setup();
void dump_config() override; void dump_config() override;
inline int level_for(const char *tag); inline uint8_t level_for(const char *tag);
/// Register a callback that will be called for every log message sent /// Register a callback that will be called for every log message sent
void add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback); void add_on_log_callback(std::function<void(uint8_t, const char *, const char *)> &&callback);
// add a listener for log level changes // add a listener for log level changes
void add_listener(std::function<void(int)> &&callback) { this->level_callback_.add(std::move(callback)); } void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
float get_setup_priority() const override; float get_setup_priority() const override;
void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args); // NOLINT
#ifdef USE_STORE_LOG_STR_IN_FLASH #ifdef USE_STORE_LOG_STR_IN_FLASH
void log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); // NOLINT void log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
va_list args); // NOLINT
#endif #endif
protected: protected:
@ -160,8 +161,9 @@ class Logger : public Component {
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
// It's the caller's responsibility to initialize buffer_at (typically to 0) // It's the caller's responsibility to initialize buffer_at (typically to 0)
inline void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format, inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
va_list args, char *buffer, int *buffer_at, int buffer_size) { va_list args, char *buffer, uint16_t *buffer_at,
uint16_t buffer_size) {
#if defined(USE_ESP32) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_LIBRETINY)
this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size); this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
#else #else
@ -180,7 +182,7 @@ class Logger : public Component {
} }
// Helper to format and send a log message to both console and callbacks // Helper to format and send a log message to both console and callbacks
inline void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format, inline void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format,
va_list args) { va_list args) {
// Format to tx_buffer and prepare for output // Format to tx_buffer and prepare for output
this->tx_buffer_at_ = 0; // Initialize buffer position this->tx_buffer_at_ = 0; // Initialize buffer position
@ -194,11 +196,12 @@ class Logger : public Component {
} }
// Write the body of the log message to the buffer // Write the body of the log message to the buffer
inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size) { inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at,
uint16_t buffer_size) {
// Calculate available space // Calculate available space
const int available = buffer_size - *buffer_at; if (*buffer_at >= buffer_size)
if (available <= 0)
return; return;
const uint16_t available = buffer_size - *buffer_at;
// Determine copy length (minimum of remaining capacity and string length) // Determine copy length (minimum of remaining capacity and string length)
const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available; const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
@ -211,7 +214,7 @@ class Logger : public Component {
} }
// Format string to explicit buffer with varargs // Format string to explicit buffer with varargs
inline void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, ...) { inline void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, ...) {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg); this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
@ -222,41 +225,50 @@ class Logger : public Component {
const char *get_uart_selection_(); const char *get_uart_selection_();
#endif #endif
// Group 4-byte aligned members first
uint32_t baud_rate_; uint32_t baud_rate_;
char *tx_buffer_{nullptr}; char *tx_buffer_{nullptr};
int tx_buffer_at_{0}; #ifdef USE_ARDUINO
int tx_buffer_size_{0}; Stream *hw_serial_{nullptr};
#endif
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
void *main_task_ = nullptr; // Only used for thread name identification
#endif
#ifdef USE_ESP32
// Task-specific recursion guards:
// - Main task uses a dedicated member variable for efficiency
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
pthread_key_t log_recursion_key_; // 4 bytes
#endif
#ifdef USE_ESP_IDF
uart_port_t uart_num_; // 4 bytes (enum defaults to int size)
#endif
// Large objects (internally aligned)
std::map<std::string, uint8_t> log_levels_{};
CallbackManager<void(uint8_t, const char *, const char *)> log_callback_{};
CallbackManager<void(uint8_t)> level_callback_{};
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#endif
// Group smaller types together at the end
uint16_t tx_buffer_at_{0};
uint16_t tx_buffer_size_{0};
uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
UARTSelection uart_{UART_SELECTION_UART0}; UARTSelection uart_{UART_SELECTION_UART0};
#endif #endif
#ifdef USE_LIBRETINY #ifdef USE_LIBRETINY
UARTSelection uart_{UART_SELECTION_DEFAULT}; UARTSelection uart_{UART_SELECTION_DEFAULT};
#endif #endif
#ifdef USE_ARDUINO
Stream *hw_serial_{nullptr};
#endif
#ifdef USE_ESP_IDF
uart_port_t uart_num_;
#endif
std::map<std::string, int> log_levels_{};
CallbackManager<void(int, const char *, const char *)> log_callback_{};
int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#endif
#ifdef USE_ESP32 #ifdef USE_ESP32
// Task-specific recursion guards:
// - Main task uses a dedicated member variable for efficiency
// - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
bool main_task_recursion_guard_{false}; bool main_task_recursion_guard_{false};
pthread_key_t log_recursion_key_;
#else #else
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
#endif #endif
CallbackManager<void(int)> level_callback_{};
#if defined(USE_ESP32) || defined(USE_LIBRETINY) #if defined(USE_ESP32) || defined(USE_LIBRETINY)
void *main_task_ = nullptr; // Only used for thread name identification
const char *HOT get_thread_name_() { const char *HOT get_thread_name_() {
TaskHandle_t current_task = xTaskGetCurrentTaskHandle(); TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
if (current_task == main_task_) { if (current_task == main_task_) {
@ -297,11 +309,10 @@ class Logger : public Component {
} }
#endif #endif
inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer, inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name,
int *buffer_at, int buffer_size) { char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
// Format header // Format header
if (level < 0) // uint8_t level is already bounded 0-255, just ensure it's <= 7
level = 0;
if (level > 7) if (level > 7)
level = 7; level = 7;
@ -320,12 +331,12 @@ class Logger : public Component {
this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line); this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
} }
inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, inline void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,
va_list args) { va_list args) {
// Get remaining capacity in the buffer // Get remaining capacity in the buffer
const int remaining = buffer_size - *buffer_at; if (*buffer_at >= buffer_size)
if (remaining <= 0)
return; return;
const uint16_t remaining = buffer_size - *buffer_at;
const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args); const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
@ -334,7 +345,7 @@ class Logger : public Component {
} }
// Update buffer_at with the formatted length (handle truncation) // Update buffer_at with the formatted length (handle truncation)
int formatted_len = (ret >= remaining) ? remaining : ret; uint16_t formatted_len = (ret >= remaining) ? remaining : ret;
*buffer_at += formatted_len; *buffer_at += formatted_len;
// Remove all trailing newlines right after formatting // Remove all trailing newlines right after formatting
@ -343,18 +354,18 @@ class Logger : public Component {
} }
} }
inline void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size) { inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR); static const uint16_t RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size); this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
} }
}; };
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
class LoggerMessageTrigger : public Trigger<int, const char *, const char *> { class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
public: public:
explicit LoggerMessageTrigger(Logger *parent, int level) { explicit LoggerMessageTrigger(Logger *parent, uint8_t level) {
this->level_ = level; this->level_ = level;
parent->add_on_log_callback([this](int level, const char *tag, const char *message) { parent->add_on_log_callback([this](uint8_t level, const char *tag, const char *message) {
if (level <= this->level_) { if (level <= this->level_) {
this->trigger(level, tag, message); this->trigger(level, tag, message);
} }
@ -362,7 +373,7 @@ class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
} }
protected: protected:
int level_; uint8_t level_;
}; };
} // namespace logger } // namespace logger

View File

@ -29,7 +29,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const char *form
if (log == nullptr) if (log == nullptr)
return; return;
log->log_vprintf_(level, tag, line, format, args); log->log_vprintf_(static_cast<uint8_t>(level), tag, line, format, args);
#endif #endif
} }
@ -41,7 +41,7 @@ void HOT esp_log_vprintf_(int level, const char *tag, int line, const __FlashStr
if (log == nullptr) if (log == nullptr)
return; return;
log->log_vprintf_(level, tag, line, format, args); log->log_vprintf_(static_cast<uint8_t>(level), tag, line, format, args);
#endif #endif
} }
#endif #endif