mirror of
https://github.com/esphome/esphome.git
synced 2025-02-12 16:08:19 +00:00
[logger] Add runtime level select (#8222)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
0cd3af2fcd
commit
ff7d232ee6
@ -242,6 +242,7 @@ esphome/components/lightwaverf/* @max246
|
||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||
esphome/components/lock/* @esphome/core
|
||||
esphome/components/logger/* @esphome/core
|
||||
esphome/components/logger/select/* @clydebarrow
|
||||
esphome/components/ltr390/* @latonita @sjtrny
|
||||
esphome/components/ltr501/* @latonita
|
||||
esphome/components/ltr_als_ps/* @latonita
|
||||
|
@ -35,7 +35,7 @@ from esphome.const import (
|
||||
PLATFORM_RP2040,
|
||||
PLATFORM_RTL87XX,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, Lambda, coroutine_with_priority
|
||||
from esphome.core import CORE, Lambda, coroutine_with_priority
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
logger_ns = cg.esphome_ns.namespace("logger")
|
||||
@ -77,6 +77,9 @@ USB_SERIAL_JTAG = "USB_SERIAL_JTAG"
|
||||
USB_CDC = "USB_CDC"
|
||||
DEFAULT = "DEFAULT"
|
||||
|
||||
CONF_INITIAL_LEVEL = "initial_level"
|
||||
CONF_LOGGER_ID = "logger_id"
|
||||
|
||||
UART_SELECTION_ESP32 = {
|
||||
VARIANT_ESP32: [UART0, UART1, UART2],
|
||||
VARIANT_ESP32S2: [UART0, UART1, USB_CDC],
|
||||
@ -154,11 +157,11 @@ def uart_selection(value):
|
||||
|
||||
|
||||
def validate_local_no_higher_than_global(value):
|
||||
global_level = value.get(CONF_LEVEL, "DEBUG")
|
||||
global_level = LOG_LEVEL_SEVERITY.index(value[CONF_LEVEL])
|
||||
for tag, level in value.get(CONF_LOGS, {}).items():
|
||||
if LOG_LEVEL_SEVERITY.index(level) > LOG_LEVEL_SEVERITY.index(global_level):
|
||||
raise EsphomeError(
|
||||
f"The local log level {level} for {tag} must be less severe than the global log level {global_level}."
|
||||
if LOG_LEVEL_SEVERITY.index(level) > global_level:
|
||||
raise cv.Invalid(
|
||||
f"The configured log level for {tag} ({level}) must be no more severe than the global log level {value[CONF_LEVEL]}."
|
||||
)
|
||||
return value
|
||||
|
||||
@ -209,6 +212,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
cv.string: is_log_level,
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_INITIAL_LEVEL): is_log_level,
|
||||
cv.Optional(CONF_ON_MESSAGE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(LoggerMessageTrigger),
|
||||
@ -227,7 +231,14 @@ CONFIG_SCHEMA = cv.All(
|
||||
@coroutine_with_priority(90.0)
|
||||
async def to_code(config):
|
||||
baud_rate = config[CONF_BAUD_RATE]
|
||||
log = cg.new_Pvariable(config[CONF_ID], baud_rate, config[CONF_TX_BUFFER_SIZE])
|
||||
level = config[CONF_LEVEL]
|
||||
initial_level = LOG_LEVELS[config.get(CONF_INITIAL_LEVEL, level)]
|
||||
log = cg.new_Pvariable(
|
||||
config[CONF_ID],
|
||||
baud_rate,
|
||||
config[CONF_TX_BUFFER_SIZE],
|
||||
)
|
||||
cg.add(log.set_log_level(initial_level))
|
||||
if CONF_HARDWARE_UART in config:
|
||||
cg.add(
|
||||
log.set_uart_selection(
|
||||
@ -239,7 +250,6 @@ async def to_code(config):
|
||||
for tag, level in config[CONF_LOGS].items():
|
||||
cg.add(log.set_log_level(tag, LOG_LEVELS[level]))
|
||||
|
||||
level = config[CONF_LEVEL]
|
||||
cg.add_define("USE_LOGGER")
|
||||
this_severity = LOG_LEVEL_SEVERITY.index(level)
|
||||
cg.add_build_flag(f"-DESPHOME_LOG_LEVEL={LOG_LEVELS[level]}")
|
||||
@ -367,3 +377,27 @@ async def logger_log_action_to_code(config, action_id, template_arg, args):
|
||||
|
||||
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"logger.set_level",
|
||||
LambdaAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger),
|
||||
cv.Required(CONF_LEVEL): is_log_level,
|
||||
cv.Optional(CONF_TAG): cv.string,
|
||||
},
|
||||
key=CONF_LEVEL,
|
||||
),
|
||||
)
|
||||
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):
|
||||
text = str(cg.statement(logger.set_log_level(tag, level)))
|
||||
else:
|
||||
text = str(cg.statement(logger.set_log_level(level)))
|
||||
|
||||
lambda_ = await cg.process_lambda(Lambda(text), args, return_type=cg.void)
|
||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
|
@ -105,12 +105,9 @@ int HOT Logger::level_for(const char *tag) {
|
||||
// Uses std::vector<> for low memory footprint, though the vector
|
||||
// could be sorted to minimize lookup times. This feature isn't used that
|
||||
// much anyway so it doesn't matter too much.
|
||||
for (auto &it : this->log_levels_) {
|
||||
if (it.tag == tag) {
|
||||
return it.level;
|
||||
}
|
||||
}
|
||||
return ESPHOME_LOG_LEVEL;
|
||||
if (this->log_levels_.count(tag) != 0)
|
||||
return this->log_levels_[tag];
|
||||
return this->current_level_;
|
||||
}
|
||||
|
||||
void HOT Logger::log_message_(int level, const char *tag, int offset) {
|
||||
@ -167,9 +164,7 @@ void Logger::loop() {
|
||||
#endif
|
||||
|
||||
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_.push_back(LogLevelOverride{tag, log_level});
|
||||
}
|
||||
void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; }
|
||||
|
||||
#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
|
||||
UARTSelection Logger::get_uart() const { return this->uart_; }
|
||||
@ -183,18 +178,28 @@ const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DE
|
||||
|
||||
void Logger::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Logger:");
|
||||
ESP_LOGCONFIG(TAG, " Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
ESP_LOGCONFIG(TAG, " Max Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
|
||||
ESP_LOGCONFIG(TAG, " Initial Level: %s", LOG_LEVELS[this->current_level_]);
|
||||
#ifndef USE_HOST
|
||||
ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
|
||||
ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_());
|
||||
#endif
|
||||
|
||||
for (auto &it : this->log_levels_) {
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.tag.c_str(), LOG_LEVELS[it.level]);
|
||||
ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
|
||||
}
|
||||
}
|
||||
void Logger::write_footer_() { this->write_to_buffer_(ESPHOME_LOG_RESET_COLOR, strlen(ESPHOME_LOG_RESET_COLOR)); }
|
||||
|
||||
void Logger::set_log_level(int level) {
|
||||
if (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]);
|
||||
}
|
||||
this->current_level_ = level;
|
||||
this->level_callback_.call(level);
|
||||
}
|
||||
|
||||
Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace logger
|
||||
|
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdarg>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef USE_ARDUINO
|
||||
#if defined(USE_ESP8266) || defined(USE_ESP32)
|
||||
@ -74,8 +75,11 @@ class Logger : public Component {
|
||||
UARTSelection get_uart() const;
|
||||
#endif
|
||||
|
||||
/// Set the default log level for this logger.
|
||||
void set_log_level(int level);
|
||||
/// Set the log level of the specified tag.
|
||||
void set_log_level(const std::string &tag, int log_level);
|
||||
int get_log_level() { return this->current_level_; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
@ -88,6 +92,9 @@ class Logger : public Component {
|
||||
/// 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);
|
||||
|
||||
// add a listener for log level changes
|
||||
void add_listener(std::function<void(int)> &&callback) { this->level_callback_.add(std::move(callback)); }
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT
|
||||
@ -159,17 +166,14 @@ class Logger : public Component {
|
||||
#ifdef USE_ESP_IDF
|
||||
uart_port_t uart_num_;
|
||||
#endif
|
||||
struct LogLevelOverride {
|
||||
std::string tag;
|
||||
int level;
|
||||
};
|
||||
std::vector<LogLevelOverride> log_levels_;
|
||||
std::map<std::string, int> log_levels_{};
|
||||
CallbackManager<void(int, const char *, const char *)> log_callback_{};
|
||||
int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
|
||||
/// Prevents recursive log calls, if true a log message is already being processed.
|
||||
bool recursion_guard_ = false;
|
||||
void *main_task_ = nullptr;
|
||||
CallbackManager<void(int)> level_callback_{};
|
||||
};
|
||||
|
||||
extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
|
||||
|
29
esphome/components/logger/select/__init__.py
Normal file
29
esphome/components/logger/select/__init__.py
Normal file
@ -0,0 +1,29 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import select
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_LEVEL, CONF_LOGGER, ENTITY_CATEGORY_CONFIG, ICON_BUG
|
||||
from esphome.core import CORE
|
||||
from esphome.cpp_helpers import register_component, register_parented
|
||||
|
||||
from .. import CONF_LOGGER_ID, LOG_LEVEL_SEVERITY, Logger, logger_ns
|
||||
|
||||
CODEOWNERS = ["@clydebarrow"]
|
||||
|
||||
LoggerLevelSelect = logger_ns.class_("LoggerLevelSelect", select.Select, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = select.select_schema(
|
||||
LoggerLevelSelect, icon=ICON_BUG, entity_category=ENTITY_CATEGORY_CONFIG
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(CONF_LOGGER_ID): cv.use_id(Logger),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
levels = LOG_LEVEL_SEVERITY
|
||||
index = levels.index(CORE.config[CONF_LOGGER][CONF_LEVEL])
|
||||
levels = levels[: index + 1]
|
||||
var = await select.new_select(config, options=levels)
|
||||
await register_parented(var, config[CONF_LOGGER_ID])
|
||||
await register_component(var, config)
|
27
esphome/components/logger/select/logger_level_select.cpp
Normal file
27
esphome/components/logger/select/logger_level_select.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "logger_level_select.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace logger {
|
||||
|
||||
void LoggerLevelSelect::publish_state(int level) {
|
||||
auto value = this->at(level);
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
Select::publish_state(value.value());
|
||||
}
|
||||
|
||||
void LoggerLevelSelect::setup() {
|
||||
this->parent_->add_listener([this](int level) { this->publish_state(level); });
|
||||
this->publish_state(this->parent_->get_log_level());
|
||||
}
|
||||
|
||||
void LoggerLevelSelect::control(const std::string &value) {
|
||||
auto level = this->index_of(value);
|
||||
if (!level)
|
||||
return;
|
||||
this->parent_->set_log_level(level.value());
|
||||
}
|
||||
|
||||
} // namespace logger
|
||||
} // namespace esphome
|
15
esphome/components/logger/select/logger_level_select.h
Normal file
15
esphome/components/logger/select/logger_level_select.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/select/select.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/logger/logger.h"
|
||||
namespace esphome {
|
||||
namespace logger {
|
||||
class LoggerLevelSelect : public Component, public select::Select, public Parented<Logger> {
|
||||
public:
|
||||
void publish_state(int level);
|
||||
void setup() override;
|
||||
void control(const std::string &value) override;
|
||||
};
|
||||
} // namespace logger
|
||||
} // namespace esphome
|
@ -14,6 +14,9 @@
|
||||
#define ESPHOME_PROJECT_VERSION_30 "v2"
|
||||
#define ESPHOME_VARIANT "ESP32"
|
||||
|
||||
// logger
|
||||
#define ESPHOME_LOG_LEVEL ESPHOME_LOG_LEVEL_VERY_VERBOSE
|
||||
|
||||
// Feature flags
|
||||
#define USE_ALARM_CONTROL_PANEL
|
||||
#define USE_AUDIO_FLAC_SUPPORT
|
||||
|
@ -1,7 +1,17 @@
|
||||
esphome:
|
||||
on_boot:
|
||||
then:
|
||||
- logger.log: Hello world
|
||||
- logger.log:
|
||||
level: warn
|
||||
format: "Warning: Logger level is %d"
|
||||
args: [id(logger_id).get_log_level()]
|
||||
- logger.set_level: WARN
|
||||
|
||||
logger:
|
||||
id: logger_id
|
||||
level: DEBUG
|
||||
initial_level: INFO
|
||||
|
||||
select:
|
||||
- platform: logger
|
||||
name: Logger Level
|
||||
|
Loading…
x
Reference in New Issue
Block a user