ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
logger.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdarg>
4#include <map>
5#ifdef USE_ESP32
6#include <pthread.h>
7#endif
12#include "esphome/core/log.h"
13
14#ifdef USE_ESPHOME_TASK_LOG_BUFFER
15#include "task_log_buffer.h"
16#endif
17
18#ifdef USE_ARDUINO
19#if defined(USE_ESP8266) || defined(USE_ESP32)
20#include <HardwareSerial.h>
21#endif // USE_ESP8266 || USE_ESP32
22#ifdef USE_RP2040
23#include <HardwareSerial.h>
24#include <SerialUSB.h>
25#endif // USE_RP2040
26#endif // USE_ARDUINO
27
28#ifdef USE_ESP_IDF
29#include <driver/uart.h>
30#endif // USE_ESP_IDF
31
32#ifdef USE_ZEPHYR
33#include <zephyr/kernel.h>
34struct device;
35#endif
36
37namespace esphome::logger {
38
39// Color and letter constants for log levels
40static const char *const LOG_LEVEL_COLORS[] = {
41 "", // NONE
42 ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), // ERROR
43 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW), // WARNING
44 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GREEN), // INFO
45 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_MAGENTA), // CONFIG
46 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_CYAN), // DEBUG
47 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY), // VERBOSE
48 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE), // VERY_VERBOSE
49};
50
51static const char *const LOG_LEVEL_LETTERS[] = {
52 "", // NONE
53 "E", // ERROR
54 "W", // WARNING
55 "I", // INFO
56 "C", // CONFIG
57 "D", // DEBUG
58 "V", // VERBOSE
59 "VV", // VERY_VERBOSE
60};
61
62#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
67enum UARTSelection : uint8_t {
68#ifdef USE_LIBRETINY
71#else
73#endif
75#if defined(USE_LIBRETINY) || defined(USE_ESP32_VARIANT_ESP32)
77#endif
78#ifdef USE_LOGGER_USB_CDC
80#endif
81#ifdef USE_LOGGER_USB_SERIAL_JTAG
83#endif
84#ifdef USE_ESP8266
86#endif // USE_ESP8266
87};
88#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY || USE_ZEPHYR
89
107class Logger : public Component {
108 public:
109 explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
110#ifdef USE_ESPHOME_TASK_LOG_BUFFER
111 void init_log_buffer(size_t total_buffer_size);
112#endif
113#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32) || defined(USE_ZEPHYR)
114 void loop() override;
115#endif
117 void set_baud_rate(uint32_t baud_rate);
118 uint32_t get_baud_rate() const { return baud_rate_; }
119#ifdef USE_ARDUINO
120 Stream *get_hw_serial() const { return hw_serial_; }
121#endif
122#ifdef USE_ESP_IDF
123 uart_port_t get_uart_num() const { return uart_num_; }
124#endif
125#ifdef USE_ESP32
126 void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); }
127#endif
128#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
129 void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
131 UARTSelection get_uart() const;
132#endif
133
135 void set_log_level(uint8_t level);
137 void set_log_level(const std::string &tag, uint8_t log_level);
138 uint8_t get_log_level() { return this->current_level_; }
139
140 // ========== INTERNAL METHODS ==========
141 // (In most use cases you won't need these)
143 void pre_setup();
144 void dump_config() override;
145
146 inline uint8_t level_for(const char *tag);
147
149 void add_on_log_callback(std::function<void(uint8_t, const char *, const char *, size_t)> &&callback);
150
151 // add a listener for log level changes
152 void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
153
154 float get_setup_priority() const override;
155
156 void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args); // NOLINT
157#ifdef USE_STORE_LOG_STR_IN_FLASH
158 void log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
159 va_list args); // NOLINT
160#endif
161
162 protected:
163 void process_messages_();
164 void write_msg_(const char *msg);
165
166 // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
167 // It's the caller's responsibility to initialize buffer_at (typically to 0)
168 inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
169 va_list args, char *buffer, uint16_t *buffer_at,
170 uint16_t buffer_size) {
171#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
172 this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
173#else
174 this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size);
175#endif
176 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, args);
177 this->write_footer_to_buffer_(buffer, buffer_at, buffer_size);
178
179 // Always ensure the buffer has a null terminator, even if we need to
180 // overwrite the last character of the actual content
181 if (*buffer_at >= buffer_size) {
182 buffer[buffer_size - 1] = '\0'; // Truncate and ensure null termination
183 } else {
184 buffer[*buffer_at] = '\0'; // Normal case, append null terminator
185 }
186 }
187
188 // Helper to format and send a log message to both console and callbacks
189 inline void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format,
190 va_list args) {
191 // Format to tx_buffer and prepare for output
192 this->tx_buffer_at_ = 0; // Initialize buffer position
193 this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, this->tx_buffer_, &this->tx_buffer_at_,
194 this->tx_buffer_size_);
195
196 if (this->baud_rate_ > 0) {
197 this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
198 }
199 this->log_callback_.call(level, tag, this->tx_buffer_, this->tx_buffer_at_);
200 }
201
202 // Write the body of the log message to the buffer
203 inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at,
204 uint16_t buffer_size) {
205 // Calculate available space
206 if (*buffer_at >= buffer_size)
207 return;
208 const uint16_t available = buffer_size - *buffer_at;
209
210 // Determine copy length (minimum of remaining capacity and string length)
211 const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
212
213 // Copy the data
214 if (copy_len > 0) {
215 memcpy(buffer + *buffer_at, value, copy_len);
216 *buffer_at += copy_len;
217 }
218 }
219
220 // Format string to explicit buffer with varargs
221 inline void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, ...) {
222 va_list arg;
223 va_start(arg, format);
224 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
225 va_end(arg);
226 }
227
228#ifndef USE_HOST
229 const char *get_uart_selection_();
230#endif
231
232 // Group 4-byte aligned members first
233 uint32_t baud_rate_;
234 char *tx_buffer_{nullptr};
235#ifdef USE_ARDUINO
236 Stream *hw_serial_{nullptr};
237#endif
238#if defined(USE_ZEPHYR)
239 const device *uart_dev_{nullptr};
240#endif
241#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
242 void *main_task_ = nullptr; // Only used for thread name identification
243#endif
244#ifdef USE_ESP32
245 // Task-specific recursion guards:
246 // - Main task uses a dedicated member variable for efficiency
247 // - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
248 pthread_key_t log_recursion_key_; // 4 bytes
249#endif
250#ifdef USE_ESP_IDF
251 uart_port_t uart_num_; // 4 bytes (enum defaults to int size)
252#endif
253
254 // Large objects (internally aligned)
255 std::map<std::string, uint8_t> log_levels_{};
256 CallbackManager<void(uint8_t, const char *, const char *, size_t)> log_callback_{};
258#ifdef USE_ESPHOME_TASK_LOG_BUFFER
259 std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
260#endif
261
262 // Group smaller types together at the end
263 uint16_t tx_buffer_at_{0};
264 uint16_t tx_buffer_size_{0};
265 uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
266#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR)
268#endif
269#ifdef USE_LIBRETINY
271#endif
272#ifdef USE_ESP32
274#else
275 bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
276#endif
277
278#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
279 const char *HOT get_thread_name_() {
280#ifdef USE_ZEPHYR
281 k_tid_t current_task = k_current_get();
282#else
283 TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
284#endif
285 if (current_task == main_task_) {
286 return nullptr; // Main task
287 } else {
288#if defined(USE_ESP32)
289 return pcTaskGetName(current_task);
290#elif defined(USE_LIBRETINY)
291 return pcTaskGetTaskName(current_task);
292#elif defined(USE_ZEPHYR)
293 return k_thread_name_get(current_task);
294#endif
295 }
296 }
297#endif
298
299#ifdef USE_ESP32
300 inline bool HOT check_and_set_task_log_recursion_(bool is_main_task) {
301 if (is_main_task) {
302 const bool was_recursive = main_task_recursion_guard_;
304 return was_recursive;
305 }
306
307 intptr_t current = (intptr_t) pthread_getspecific(log_recursion_key_);
308 if (current != 0)
309 return true;
310
311 pthread_setspecific(log_recursion_key_, (void *) 1);
312 return false;
313 }
314
315 inline void HOT reset_task_log_recursion_(bool is_main_task) {
316 if (is_main_task) {
318 return;
319 }
320
321 pthread_setspecific(log_recursion_key_, (void *) 0);
322 }
323#endif
324
325 inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name,
326 char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
327 // Format header
328 // uint8_t level is already bounded 0-255, just ensure it's <= 7
329 if (level > 7)
330 level = 7;
331
332 const char *color = esphome::logger::LOG_LEVEL_COLORS[level];
333 const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level];
334
335#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
336 if (thread_name != nullptr) {
337 // Non-main task with thread name
338 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
339 ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
340 return;
341 }
342#endif
343 // Main task or non ESP32/LibreTiny platform
344 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
345 }
346
347 inline void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,
348 va_list args) {
349 // Get remaining capacity in the buffer
350 if (*buffer_at >= buffer_size)
351 return;
352 const uint16_t remaining = buffer_size - *buffer_at;
353
354 const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
355
356 if (ret < 0) {
357 return; // Encoding error, do not increment buffer_at
358 }
359
360 // Update buffer_at with the formatted length (handle truncation)
361 uint16_t formatted_len = (ret >= remaining) ? remaining : ret;
362 *buffer_at += formatted_len;
363
364 // Remove all trailing newlines right after formatting
365 while (*buffer_at > 0 && buffer[*buffer_at - 1] == '\n') {
366 (*buffer_at)--;
367 }
368 }
369
370 inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
371 static constexpr uint16_t RESET_COLOR_LEN = sizeof(ESPHOME_LOG_RESET_COLOR) - 1;
372 this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
373 }
374
375#ifdef USE_ESP32
376 // Disable loop when task buffer is empty (with USB CDC check)
378 // Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
379 // concurrently. If that happens between our check and disable_loop(), the enable request
380 // will be processed on the next main loop iteration since:
381 // - disable_loop() takes effect immediately
382 // - enable_loop_soon_any_context() sets a pending flag that's checked at loop start
383#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
384 // Only disable if not using USB CDC (which needs loop for connection detection)
385 if (this->uart_ != UART_SELECTION_USB_CDC) {
386 this->disable_loop();
387 }
388#else
389 // No USB CDC support, always safe to disable
390 this->disable_loop();
391#endif
392 }
393#endif
394};
395extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
396
397class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
398 public:
399 explicit LoggerMessageTrigger(Logger *parent, uint8_t level) {
400 this->level_ = level;
401 parent->add_on_log_callback([this](uint8_t level, const char *tag, const char *message, size_t message_len) {
402 if (level <= this->level_) {
403 this->trigger(level, tag, message);
404 }
405 });
406 }
407
408 protected:
409 uint8_t level_;
410};
411
412} // namespace esphome::logger
void disable_loop()
Disable this component's loop.
Logger component for all ESPHome logging.
Definition logger.h:107
UARTSelection uart_
Definition logger.h:267
void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,...)
Definition logger.h:221
CallbackManager< void(uint8_t, const char *, const char *, size_t)> log_callback_
Definition logger.h:256
void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format, va_list args, char *buffer, uint16_t *buffer_at, uint16_t buffer_size)
Definition logger.h:168
const char *HOT get_thread_name_()
Definition logger.h:279
void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format, va_list args)
Definition logger.h:189
void dump_config() override
Definition logger.cpp:251
void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, va_list args)
Definition logger.h:347
uint32_t get_baud_rate() const
Definition logger.h:118
const char * get_uart_selection_()
uint8_t level_for(const char *tag)
Definition logger.cpp:150
bool HOT check_and_set_task_log_recursion_(bool is_main_task)
Definition logger.h:300
void loop() override
Definition logger.cpp:178
uint8_t get_log_level()
Definition logger.h:138
Stream * get_hw_serial() const
Definition logger.h:120
void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args)
Definition logger.cpp:26
CallbackManager< void(uint8_t)> level_callback_
Definition logger.h:257
void pre_setup()
Set up this component.
void add_on_log_callback(std::function< void(uint8_t, const char *, const char *, size_t)> &&callback)
Register a callback that will be called for every log message sent.
Definition logger.cpp:245
void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size)
Definition logger.h:370
float get_setup_priority() const override
Definition logger.cpp:248
UARTSelection get_uart() const
Get the UART used by the logger.
Definition logger.cpp:242
std::map< std::string, uint8_t > log_levels_
Definition logger.h:255
void disable_loop_when_buffer_empty_()
Definition logger.h:377
uart_port_t uart_num_
Definition logger.h:251
std::unique_ptr< logger::TaskLogBuffer > log_buffer_
Definition logger.h:259
pthread_key_t log_recursion_key_
Definition logger.h:248
uart_port_t get_uart_num() const
Definition logger.h:123
void init_log_buffer(size_t total_buffer_size)
Definition logger.cpp:167
void set_log_level(uint8_t level)
Set the default log level for this logger.
Definition logger.cpp:274
void set_baud_rate(uint32_t baud_rate)
Manually set the baud rate for serial, set to 0 to disable.
Definition logger.cpp:238
void set_uart_selection(UARTSelection uart_selection)
Definition logger.h:129
Logger(uint32_t baud_rate, size_t tx_buffer_size)
Definition logger.cpp:157
void add_listener(std::function< void(uint8_t)> &&callback)
Definition logger.h:152
void HOT reset_task_log_recursion_(bool is_main_task)
Definition logger.h:315
void write_msg_(const char *msg)
void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at, uint16_t buffer_size)
Definition logger.h:203
void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name, char *buffer, uint16_t *buffer_at, uint16_t buffer_size)
Definition logger.h:325
bool main_task_recursion_guard_
Definition logger.h:273
const device * uart_dev_
Definition logger.h:239
uint16_t tx_buffer_size_
Definition logger.h:264
LoggerMessageTrigger(Logger *parent, uint8_t level)
Definition logger.h:399
UARTSelection
Enum for logging UART selection.
Definition logger.h:67
@ UART_SELECTION_UART0_SWAP
Definition logger.h:85
@ UART_SELECTION_UART2
Definition logger.h:76
@ UART_SELECTION_USB_SERIAL_JTAG
Definition logger.h:82
@ UART_SELECTION_DEFAULT
Definition logger.h:69
@ UART_SELECTION_USB_CDC
Definition logger.h:79
@ UART_SELECTION_UART0
Definition logger.h:70
@ UART_SELECTION_UART1
Definition logger.h:74
Logger * global_logger
Definition logger.cpp:283
uint16_t length
Definition tt21100.cpp:0