From 28324adfb9d03ced7b1f0696d2cb0c3d1eda3f13 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 3 Oct 2025 16:03:30 -0500 Subject: [PATCH] [logger] Conditionally compile runtime tag-specific log levels for performance --- esphome/components/logger/__init__.py | 11 +++++++++-- esphome/components/logger/logger.cpp | 10 ++++++++-- esphome/components/logger/logger.h | 15 +++++++++++++-- esphome/core/defines.h | 1 + tests/components/logger/common-default_uart.yaml | 5 +++++ 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/esphome/components/logger/__init__.py b/esphome/components/logger/__init__.py index 7d1a591f0c..1d02073d27 100644 --- a/esphome/components/logger/__init__.py +++ b/esphome/components/logger/__init__.py @@ -95,6 +95,7 @@ DEFAULT = "DEFAULT" CONF_INITIAL_LEVEL = "initial_level" CONF_LOGGER_ID = "logger_id" +CONF_RUNTIME_TAG_LEVELS = "runtime_tag_levels" CONF_TASK_LOG_BUFFER_SIZE = "task_log_buffer_size" UART_SELECTION_ESP32 = { @@ -249,6 +250,7 @@ CONFIG_SCHEMA = cv.All( } ), cv.Optional(CONF_INITIAL_LEVEL): is_log_level, + cv.Optional(CONF_RUNTIME_TAG_LEVELS, default=False): cv.boolean, cv.Optional(CONF_ON_MESSAGE): automation.validate_automation( { cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger), @@ -291,8 +293,12 @@ async def to_code(config): ) cg.add(log.pre_setup()) - for tag, log_level in config[CONF_LOGS].items(): - cg.add(log.set_log_level(tag, LOG_LEVELS[log_level])) + # Enable runtime tag levels if logs are configured or explicitly enabled + logs_config = config[CONF_LOGS] + if logs_config or config[CONF_RUNTIME_TAG_LEVELS]: + cg.add_define("USE_LOGGER_RUNTIME_TAG_LEVELS") + for tag, log_level in logs_config.items(): + cg.add(log.set_log_level(tag, LOG_LEVELS[log_level])) cg.add_define("USE_LOGGER") this_severity = LOG_LEVEL_SEVERITY.index(level) @@ -443,6 +449,7 @@ async def logger_set_level_to_code(config, action_id, template_arg, args): level = LOG_LEVELS[config[CONF_LEVEL]] logger = await cg.get_variable(config[CONF_LOGGER_ID]) if tag := config.get(CONF_TAG): + cg.add_define("USE_LOGGER_RUNTIME_TAG_LEVELS") text = str(cg.statement(logger.set_log_level(tag, level))) else: text = str(cg.statement(logger.set_log_level(level))) diff --git a/esphome/components/logger/logger.cpp b/esphome/components/logger/logger.cpp index 4a69bd9853..9a9bf89fe3 100644 --- a/esphome/components/logger/logger.cpp +++ b/esphome/components/logger/logger.cpp @@ -148,9 +148,11 @@ void Logger::log_vprintf_(uint8_t level, const char *tag, int line, const __Flas #endif // USE_STORE_LOG_STR_IN_FLASH inline uint8_t Logger::level_for(const char *tag) { +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS auto it = this->log_levels_.find(tag); if (it != this->log_levels_.end()) return it->second; +#endif return this->current_level_; } @@ -220,7 +222,9 @@ void Logger::process_messages_() { } void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; } -void Logger::set_log_level(const std::string &tag, uint8_t log_level) { this->log_levels_[tag] = log_level; } +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS +void Logger::set_log_level(const char *tag, uint8_t log_level) { this->log_levels_[tag] = log_level; } +#endif #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) UARTSelection Logger::get_uart() const { return this->uart_; } @@ -271,9 +275,11 @@ void Logger::dump_config() { } #endif +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS for (auto &it : this->log_levels_) { - ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_STR_ARG(LOG_LEVELS[it.second])); + ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first, LOG_STR_ARG(LOG_LEVELS[it.second])); } +#endif } void Logger::set_log_level(uint8_t level) { diff --git a/esphome/components/logger/logger.h b/esphome/components/logger/logger.h index 7d4c14df0b..55fcfad9d0 100644 --- a/esphome/components/logger/logger.h +++ b/esphome/components/logger/logger.h @@ -36,6 +36,13 @@ struct device; namespace esphome::logger { +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS +// Comparison function for const char* keys in log_levels_ map +struct CStrCompare { + bool operator()(const char *a, const char *b) const { return strcmp(a, b) < 0; } +}; +#endif + // ANSI color code last digit (30-38 range, store only last digit to save RAM) static constexpr char LOG_LEVEL_COLOR_DIGIT[] = { '\0', // NONE @@ -133,8 +140,10 @@ class Logger : public Component { /// Set the default log level for this logger. void set_log_level(uint8_t level); +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS /// Set the log level of the specified tag. - void set_log_level(const std::string &tag, uint8_t log_level); + void set_log_level(const char *tag, uint8_t log_level); +#endif uint8_t get_log_level() { return this->current_level_; } // ========== INTERNAL METHODS ========== @@ -242,7 +251,9 @@ class Logger : public Component { #endif // Large objects (internally aligned) - std::map log_levels_{}; +#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS + std::map log_levels_{}; +#endif CallbackManager log_callback_{}; CallbackManager level_callback_{}; #ifdef USE_ESPHOME_TASK_LOG_BUFFER diff --git a/esphome/core/defines.h b/esphome/core/defines.h index 7fc42ea334..fb8c217016 100644 --- a/esphome/core/defines.h +++ b/esphome/core/defines.h @@ -48,6 +48,7 @@ #define USE_LIGHT #define USE_LOCK #define USE_LOGGER +#define USE_LOGGER_RUNTIME_TAG_LEVELS #define USE_LVGL #define USE_LVGL_ANIMIMG #define USE_LVGL_ARC diff --git a/tests/components/logger/common-default_uart.yaml b/tests/components/logger/common-default_uart.yaml index e8b56043eb..7939a5f9c5 100644 --- a/tests/components/logger/common-default_uart.yaml +++ b/tests/components/logger/common-default_uart.yaml @@ -6,11 +6,16 @@ esphome: format: "Warning: Logger level is %d" args: [id(logger_id).get_log_level()] - logger.set_level: WARN + - logger.set_level: + level: ERROR + tag: mqtt.client logger: id: logger_id level: DEBUG initial_level: INFO + logs: + mqtt.component: WARN select: - platform: logger