[logger] Conditionally compile log level change listener

This commit is contained in:
J. Nick Koston
2025-11-28 14:28:02 -06:00
parent d6ca01775e
commit ed0751246a
7 changed files with 64 additions and 10 deletions

View File

@@ -406,6 +406,8 @@ async def to_code(config):
conf,
)
CORE.add_job(final_step)
def validate_printf(value):
# https://stackoverflow.com/questions/30011379/how-can-i-parse-a-c-format-string-in-python
@@ -506,3 +508,23 @@ FILTER_SOURCE_FILES = filter_source_files_from_platform(
},
}
)
# Key for CORE.data to track if level listeners are requested
LOGGER_LEVEL_LISTENERS_KEY = "logger_level_listeners"
def request_logger_level_listeners() -> None:
"""Request that logger level listeners be compiled in.
Components that need to be notified about log level changes should call this
function during their code generation. This enables the add_level_listener()
method and compiles in the listener vector.
"""
CORE.data[LOGGER_LEVEL_LISTENERS_KEY] = True
@coroutine_with_priority(CoroPriority.FINAL)
async def final_step():
"""Final code generation step to configure optional logger features."""
if CORE.data.get(LOGGER_LEVEL_LISTENERS_KEY, False):
cg.add_define("USE_LOGGER_LEVEL_LISTENERS")

View File

@@ -288,7 +288,10 @@ void Logger::set_log_level(uint8_t level) {
ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_STR_ARG(LOG_LEVELS[ESPHOME_LOG_LEVEL]));
}
this->current_level_ = level;
this->level_callback_.call(level);
#ifdef USE_LOGGER_LEVEL_LISTENERS
for (auto *listener : this->level_listeners_)
listener->on_log_level_change(level);
#endif
}
Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)

View File

@@ -58,6 +58,18 @@ class LogListener {
virtual void on_log(uint8_t level, const char *tag, const char *message, size_t message_len) = 0;
};
#ifdef USE_LOGGER_LEVEL_LISTENERS
/** Interface for receiving log level changes without std::function overhead.
*
* Components can implement this interface instead of using lambdas with std::function
* to reduce flash usage from std::function type erasure machinery.
*/
class LoggerLevelListener {
public:
virtual void on_log_level_change(uint8_t level) = 0;
};
#endif
#ifdef USE_LOGGER_RUNTIME_TAG_LEVELS
// Comparison function for const char* keys in log_levels_ map
struct CStrCompare {
@@ -193,8 +205,10 @@ class Logger : public Component {
/// Register a log listener to receive log messages
void add_log_listener(LogListener *listener) { this->log_listeners_.push_back(listener); }
// add a listener for log level changes
void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
#ifdef USE_LOGGER_LEVEL_LISTENERS
/// Register a listener for log level changes
void add_level_listener(LoggerLevelListener *listener) { this->level_listeners_.push_back(listener); }
#endif
float get_setup_priority() const override;
@@ -325,7 +339,9 @@ class Logger : public Component {
std::map<const char *, uint8_t, CStrCompare> log_levels_{};
#endif
std::vector<LogListener *> log_listeners_; // Log message listeners (API, MQTT, syslog, etc.)
CallbackManager<void(uint8_t)> level_callback_{};
#ifdef USE_LOGGER_LEVEL_LISTENERS
std::vector<LoggerLevelListener *> level_listeners_; // Log level change listeners
#endif
#ifdef USE_ESPHOME_TASK_LOG_BUFFER
std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
#endif

View File

@@ -5,7 +5,13 @@ from esphome.const import CONF_LEVEL, CONF_LOGGER, ENTITY_CATEGORY_CONFIG, ICON_
from esphome.core import CORE
from esphome.cpp_helpers import register_component, register_parented
from .. import CONF_LOGGER_ID, LOG_LEVELS, Logger, logger_ns
from .. import (
CONF_LOGGER_ID,
LOG_LEVELS,
Logger,
logger_ns,
request_logger_level_listeners,
)
CODEOWNERS = ["@clydebarrow"]
@@ -21,6 +27,7 @@ CONFIG_SCHEMA = select.select_schema(
async def to_code(config):
request_logger_level_listeners()
parent = await cg.get_variable(config[CONF_LOGGER_ID])
levels = list(LOG_LEVELS)
index = levels.index(CORE.data[CONF_LOGGER][CONF_LEVEL])

View File

@@ -2,7 +2,7 @@
namespace esphome::logger {
void LoggerLevelSelect::publish_state(int level) {
void LoggerLevelSelect::on_log_level_change(uint8_t level) {
auto index = level_to_index(level);
if (!this->has_index(index))
return;
@@ -10,8 +10,8 @@ void LoggerLevelSelect::publish_state(int level) {
}
void LoggerLevelSelect::setup() {
this->parent_->add_listener([this](int level) { this->publish_state(level); });
this->publish_state(this->parent_->get_log_level());
this->parent_->add_level_listener(this);
this->on_log_level_change(this->parent_->get_log_level());
}
void LoggerLevelSelect::control(size_t index) { this->parent_->set_log_level(index_to_level(index)); }

View File

@@ -5,12 +5,17 @@
#include "esphome/components/logger/logger.h"
namespace esphome::logger {
class LoggerLevelSelect : public Component, public select::Select, public Parented<Logger> {
class LoggerLevelSelect final : public Component,
public select::Select,
public Parented<Logger>,
public LoggerLevelListener {
public:
void publish_state(int level);
void setup() override;
void control(size_t index) override;
// LoggerLevelListener interface
void on_log_level_change(uint8_t level) override;
protected:
// Convert log level to option index (skip CONFIG at level 4)
static uint8_t level_to_index(uint8_t level) { return (level > ESPHOME_LOG_LEVEL_CONFIG) ? level - 1 : level; }

View File

@@ -51,6 +51,7 @@
#define USE_LIGHT
#define USE_LOCK
#define USE_LOGGER
#define USE_LOGGER_LEVEL_LISTENERS
#define USE_LOGGER_RUNTIME_TAG_LEVELS
#define USE_LVGL
#define USE_LVGL_ANIMIMG