ESPHome 2025.5.0
Loading...
Searching...
No Matches
logger.cpp
Go to the documentation of this file.
1#include "logger.h"
2#include <cinttypes>
3#ifdef USE_ESPHOME_TASK_LOG_BUFFER
4#include <memory> // For unique_ptr
5#endif
6
7#include "esphome/core/hal.h"
8#include "esphome/core/log.h"
10
11namespace esphome {
12namespace logger {
13
14static const char *const TAG = "logger";
15
16#ifdef USE_ESP32
17// Implementation for ESP32 (multi-task platform with task-specific tracking)
18// Main task always uses direct buffer access for console output and callbacks
19//
20// For non-main tasks:
21// - WITH task log buffer: Prefer sending to ring buffer for async processing
22// - Avoids allocating stack memory for console output in normal operation
23// - Prevents console corruption from concurrent writes by multiple tasks
24// - Messages are serialized through main loop for proper console output
25// - Fallback to emergency console logging only if ring buffer is full
26// - WITHOUT task log buffer: Only emergency console output, no callbacks
27void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
28 if (level > this->level_for(tag))
29 return;
30
31 TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
32 bool is_main_task = (current_task == main_task_);
33
34 // Check and set recursion guard - uses pthread TLS for per-task state
35 if (this->check_and_set_task_log_recursion_(is_main_task)) {
36 return; // Recursion detected
37 }
38
39 // Main task uses the shared buffer for efficiency
40 if (is_main_task) {
41 this->log_message_to_buffer_and_send_(level, tag, line, format, args);
42 this->reset_task_log_recursion_(is_main_task);
43 return;
44 }
45
46 bool message_sent = false;
47#ifdef USE_ESPHOME_TASK_LOG_BUFFER
48 // For non-main tasks, queue the message for callbacks - but only if we have any callbacks registered
49 message_sent = this->log_buffer_->send_message_thread_safe(static_cast<uint8_t>(level), tag,
50 static_cast<uint16_t>(line), current_task, format, args);
51#endif // USE_ESPHOME_TASK_LOG_BUFFER
52
53 // Emergency console logging for non-main tasks when ring buffer is full or disabled
54 // This is a fallback mechanism to ensure critical log messages are visible
55 // Note: This may cause interleaved/corrupted console output if multiple tasks
56 // log simultaneously, but it's better than losing important messages entirely
57 if (!message_sent && this->baud_rate_ > 0) { // If logging is enabled, write to console
58 // Maximum size for console log messages (includes null terminator)
59 static const size_t MAX_CONSOLE_LOG_MSG_SIZE = 144;
60 char console_buffer[MAX_CONSOLE_LOG_MSG_SIZE]; // MUST be stack allocated for thread safety
61 int buffer_at = 0; // Initialize buffer position
62 this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, console_buffer, &buffer_at,
63 MAX_CONSOLE_LOG_MSG_SIZE);
64 this->write_msg_(console_buffer);
65 }
66
67 // Reset the recursion guard for this task
68 this->reset_task_log_recursion_(is_main_task);
69}
70#else
71// Implementation for all other platforms
72void HOT Logger::log_vprintf_(int level, const char *tag, int line, const char *format, va_list args) { // NOLINT
73 if (level > this->level_for(tag) || global_recursion_guard_)
74 return;
75
77
78 // Format and send to both console and callbacks
79 this->log_message_to_buffer_and_send_(level, tag, line, format, args);
80
82}
83#endif // !USE_ESP32
84
85#ifdef USE_STORE_LOG_STR_IN_FLASH
86// Implementation for ESP8266 with flash string support.
87// Note: USE_STORE_LOG_STR_IN_FLASH is only defined for ESP8266.
88void Logger::log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format,
89 va_list args) { // NOLINT
90 if (level > this->level_for(tag) || global_recursion_guard_)
91 return;
92
94 this->tx_buffer_at_ = 0;
95
96 // Copy format string from progmem
97 auto *format_pgm_p = reinterpret_cast<const uint8_t *>(format);
98 char ch = '.';
99 while (this->tx_buffer_at_ < this->tx_buffer_size_ && ch != '\0') {
100 this->tx_buffer_[this->tx_buffer_at_++] = ch = (char) progmem_read_byte(format_pgm_p++);
101 }
102
103 // Buffer full from copying format
104 if (this->tx_buffer_at_ >= this->tx_buffer_size_) {
105 global_recursion_guard_ = false; // Make sure to reset the recursion guard before returning
106 return;
107 }
108
109 // Save the offset before calling format_log_to_buffer_with_terminator_
110 // since it will increment tx_buffer_at_ to the end of the formatted string
111 uint32_t msg_start = this->tx_buffer_at_;
112 this->format_log_to_buffer_with_terminator_(level, tag, line, this->tx_buffer_, args, this->tx_buffer_,
113 &this->tx_buffer_at_, this->tx_buffer_size_);
114
115 // Write to console and send callback starting at the msg_start
116 if (this->baud_rate_ > 0) {
117 this->write_msg_(this->tx_buffer_ + msg_start);
118 }
119 this->call_log_callbacks_(level, tag, this->tx_buffer_ + msg_start);
120
122}
123#endif // USE_STORE_LOG_STR_IN_FLASH
124
125inline int Logger::level_for(const char *tag) {
126 auto it = this->log_levels_.find(tag);
127 if (it != this->log_levels_.end())
128 return it->second;
129 return this->current_level_;
130}
131
132void HOT Logger::call_log_callbacks_(int level, const char *tag, const char *msg) {
133#ifdef USE_ESP32
134 // Suppress network-logging if memory constrained
135 // In some configurations (eg BLE enabled) there may be some transient
136 // memory exhaustion, and trying to log when OOM can lead to a crash. Skipping
137 // here usually allows the stack to recover instead.
138 // See issue #1234 for analysis.
139 if (xPortGetFreeHeapSize() < 2048)
140 return;
141#endif
142 this->log_callback_.call(level, tag, msg);
143}
144
145Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
146 // add 1 to buffer size for null terminator
147 this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
148#if defined(USE_ESP32) || defined(USE_LIBRETINY)
149 this->main_task_ = xTaskGetCurrentTaskHandle();
150#endif
151}
152#ifdef USE_ESPHOME_TASK_LOG_BUFFER
153void Logger::init_log_buffer(size_t total_buffer_size) {
155}
156#endif
157
158#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
160#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
161 if (this->uart_ == UART_SELECTION_USB_CDC) {
162 static bool opened = false;
163 if (opened == Serial) {
164 return;
165 }
166 if (false == opened) {
168 }
169 opened = !opened;
170 }
171#endif
172
173#ifdef USE_ESPHOME_TASK_LOG_BUFFER
174 // Process any buffered messages when available
175 if (this->log_buffer_->has_messages()) {
177 const char *text;
178 void *received_token;
179
180 // Process messages from the buffer
181 while (this->log_buffer_->borrow_message_main_loop(&message, &text, &received_token)) {
182 this->tx_buffer_at_ = 0;
183 // Use the thread name that was stored when the message was created
184 // This avoids potential crashes if the task no longer exists
185 const char *thread_name = message->thread_name[0] != '\0' ? message->thread_name : nullptr;
186 this->write_header_to_buffer_(message->level, message->tag, message->line, thread_name, this->tx_buffer_,
187 &this->tx_buffer_at_, this->tx_buffer_size_);
188 this->write_body_to_buffer_(text, message->text_length, this->tx_buffer_, &this->tx_buffer_at_,
189 this->tx_buffer_size_);
191 this->tx_buffer_[this->tx_buffer_at_] = '\0';
192 this->call_log_callbacks_(message->level, message->tag, this->tx_buffer_);
193 // At this point all the data we need from message has been transferred to the tx_buffer
194 // so we can release the message to allow other tasks to use it as soon as possible.
195 this->log_buffer_->release_message_main_loop(received_token);
196
197 // Write to console from the main loop to prevent corruption from concurrent writes
198 // This ensures all log messages appear on the console in a clean, serialized manner
199 // Note: Messages may appear slightly out of order due to async processing, but
200 // this is preferred over corrupted/interleaved console output
201 if (this->baud_rate_ > 0) {
202 this->write_msg_(this->tx_buffer_);
203 }
204 }
205 }
206#endif
207}
208#endif
209
210void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
211void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; }
212
213#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
214UARTSelection Logger::get_uart() const { return this->uart_; }
215#endif
216
217void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) {
218 this->log_callback_.add(std::move(callback));
219}
220float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
221static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
222
224 ESP_LOGCONFIG(TAG, "Logger:");
225 ESP_LOGCONFIG(TAG, " Max Level: %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
226 ESP_LOGCONFIG(TAG, " Initial Level: %s", LOG_LEVELS[this->current_level_]);
227#ifndef USE_HOST
228 ESP_LOGCONFIG(TAG, " Log Baud Rate: %" PRIu32, this->baud_rate_);
229 ESP_LOGCONFIG(TAG, " Hardware UART: %s", get_uart_selection_());
230#endif
231#ifdef USE_ESPHOME_TASK_LOG_BUFFER
232 if (this->log_buffer_) {
233 ESP_LOGCONFIG(TAG, " Task Log Buffer Size: %u", this->log_buffer_->size());
234 }
235#endif
236
237 for (auto &it : this->log_levels_) {
238 ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
239 }
240}
241
242void Logger::set_log_level(int level) {
243 if (level > ESPHOME_LOG_LEVEL) {
244 level = ESPHOME_LOG_LEVEL;
245 ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
246 }
247 this->current_level_ = level;
248 this->level_callback_.call(level);
249}
250
251Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
252
253} // namespace logger
254} // namespace esphome
Logger component for all ESPHome logging.
Definition logger.h:104
UARTSelection uart_
Definition logger.h:231
void call_log_callbacks_(int level, const char *tag, const char *msg)
Definition logger.cpp:132
int level_for(const char *tag)
Definition logger.cpp:125
void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size)
Definition logger.h:347
void dump_config() override
Definition logger.cpp:223
const char * get_uart_selection_()
void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args)
Definition logger.cpp:27
bool HOT check_and_set_task_log_recursion_(bool is_main_task)
Definition logger.h:276
void loop() override
Definition logger.cpp:159
CallbackManager< void(int, const char *, const char *)> log_callback_
Definition logger.h:243
void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format, va_list args)
Definition logger.h:184
float get_setup_priority() const override
Definition logger.cpp:220
UARTSelection get_uart() const
Get the UART used by the logger.
Definition logger.cpp:214
std::unique_ptr< logger::TaskLogBuffer > log_buffer_
Definition logger.h:246
void init_log_buffer(size_t total_buffer_size)
Definition logger.cpp:153
void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer, int *buffer_at, int buffer_size)
Definition logger.h:301
void set_baud_rate(uint32_t baud_rate)
Manually set the baud rate for serial, set to 0 to disable.
Definition logger.cpp:210
void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format, va_list args, char *buffer, int *buffer_at, int buffer_size)
Definition logger.h:164
Logger(uint32_t baud_rate, size_t tx_buffer_size)
Definition logger.cpp:145
std::map< std::string, int > log_levels_
Definition logger.h:242
void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size)
Definition logger.h:198
void HOT reset_task_log_recursion_(bool is_main_task)
Definition logger.h:291
void write_msg_(const char *msg)
CallbackManager< void(int)> level_callback_
Definition logger.h:257
void add_on_log_callback(std::function< void(int, const char *, const char *)> &&callback)
Register a callback that will be called for every log message sent.
Definition logger.cpp:217
void set_log_level(int level)
Set the default log level for this logger.
Definition logger.cpp:242
UARTSelection
Enum for logging UART selection.
Definition logger.h:64
@ UART_SELECTION_USB_CDC
Definition logger.h:76
Logger * global_logger
Definition logger.cpp:251
const float BUS
For communication buses like i2c/spi.
Definition component.cpp:16
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::unique_ptr< T > make_unique(Args &&...args)
Definition helpers.h:85
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t progmem_read_byte(const uint8_t *addr)
Definition core.cpp:57