ESPHome 2025.10.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)
20#include <HardwareSerial.h>
21#endif // USE_ESP8266
22#ifdef USE_RP2040
23#include <HardwareSerial.h>
24#include <SerialUSB.h>
25#endif // USE_RP2040
26#endif // USE_ARDUINO
27
28#ifdef USE_ESP32
29#include <driver/uart.h>
30#endif // USE_ESP32
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_ESPHOME_TASK_LOG_BUFFER) || (defined(USE_ZEPHYR) && defined(USE_LOGGER_USB_CDC))
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#if defined(USE_ARDUINO) && !defined(USE_ESP32)
120 Stream *get_hw_serial() const { return hw_serial_; }
121#endif
122#ifdef USE_ESP32
123 uart_port_t get_uart_num() const { return uart_num_; }
124 void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); }
125#endif
126#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
127 void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
129 UARTSelection get_uart() const;
130#endif
131
133 void set_log_level(uint8_t level);
135 void set_log_level(const std::string &tag, uint8_t log_level);
136 uint8_t get_log_level() { return this->current_level_; }
137
138 // ========== INTERNAL METHODS ==========
139 // (In most use cases you won't need these)
141 void pre_setup();
142 void dump_config() override;
143
144 inline uint8_t level_for(const char *tag);
145
147 void add_on_log_callback(std::function<void(uint8_t, const char *, const char *, size_t)> &&callback);
148
149 // add a listener for log level changes
150 void add_listener(std::function<void(uint8_t)> &&callback) { this->level_callback_.add(std::move(callback)); }
151
152 float get_setup_priority() const override;
153
154 void log_vprintf_(uint8_t level, const char *tag, int line, const char *format, va_list args); // NOLINT
155#ifdef USE_STORE_LOG_STR_IN_FLASH
156 void log_vprintf_(uint8_t level, const char *tag, int line, const __FlashStringHelper *format,
157 va_list args); // NOLINT
158#endif
159
160 protected:
161 void process_messages_();
162 void write_msg_(const char *msg);
163
164 // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
165 // It's the caller's responsibility to initialize buffer_at (typically to 0)
166 inline void HOT format_log_to_buffer_with_terminator_(uint8_t level, const char *tag, int line, const char *format,
167 va_list args, char *buffer, uint16_t *buffer_at,
168 uint16_t buffer_size) {
169#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
170 this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
171#else
172 this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size);
173#endif
174 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, args);
175 this->write_footer_to_buffer_(buffer, buffer_at, buffer_size);
176
177 // Always ensure the buffer has a null terminator, even if we need to
178 // overwrite the last character of the actual content
179 if (*buffer_at >= buffer_size) {
180 buffer[buffer_size - 1] = '\0'; // Truncate and ensure null termination
181 } else {
182 buffer[*buffer_at] = '\0'; // Normal case, append null terminator
183 }
184 }
185
186 // Helper to format and send a log message to both console and callbacks
187 inline void HOT log_message_to_buffer_and_send_(uint8_t level, const char *tag, int line, const char *format,
188 va_list args) {
189 // Format to tx_buffer and prepare for output
190 this->tx_buffer_at_ = 0; // Initialize buffer position
191 this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, this->tx_buffer_, &this->tx_buffer_at_,
192 this->tx_buffer_size_);
193
194 if (this->baud_rate_ > 0) {
195 this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
196 }
197 this->log_callback_.call(level, tag, this->tx_buffer_, this->tx_buffer_at_);
198 }
199
200 // Write the body of the log message to the buffer
201 inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, uint16_t *buffer_at,
202 uint16_t buffer_size) {
203 // Calculate available space
204 if (*buffer_at >= buffer_size)
205 return;
206 const uint16_t available = buffer_size - *buffer_at;
207
208 // Determine copy length (minimum of remaining capacity and string length)
209 const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
210
211 // Copy the data
212 if (copy_len > 0) {
213 memcpy(buffer + *buffer_at, value, copy_len);
214 *buffer_at += copy_len;
215 }
216 }
217
218 // Format string to explicit buffer with varargs
219 inline void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format, ...) {
220 va_list arg;
221 va_start(arg, format);
222 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
223 va_end(arg);
224 }
225
226#ifndef USE_HOST
227 const LogString *get_uart_selection_();
228#endif
229
230 // Group 4-byte aligned members first
231 uint32_t baud_rate_;
232 char *tx_buffer_{nullptr};
233#if defined(USE_ARDUINO) && !defined(USE_ESP32)
234 Stream *hw_serial_{nullptr};
235#endif
236#if defined(USE_ZEPHYR)
237 const device *uart_dev_{nullptr};
238#endif
239#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
240 void *main_task_ = nullptr; // Only used for thread name identification
241#endif
242#ifdef USE_ESP32
243 // Task-specific recursion guards:
244 // - Main task uses a dedicated member variable for efficiency
245 // - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
246 pthread_key_t log_recursion_key_; // 4 bytes
247 uart_port_t uart_num_; // 4 bytes (enum defaults to int size)
248#endif
249
250 // Large objects (internally aligned)
251 std::map<std::string, uint8_t> log_levels_{};
252 CallbackManager<void(uint8_t, const char *, const char *, size_t)> log_callback_{};
254#ifdef USE_ESPHOME_TASK_LOG_BUFFER
255 std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
256#endif
257
258 // Group smaller types together at the end
259 uint16_t tx_buffer_at_{0};
260 uint16_t tx_buffer_size_{0};
261 uint8_t current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
262#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_ZEPHYR)
264#endif
265#ifdef USE_LIBRETINY
267#endif
268#ifdef USE_ESP32
270#else
271 bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
272#endif
273
274#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
275 const char *HOT get_thread_name_() {
276#ifdef USE_ZEPHYR
277 k_tid_t current_task = k_current_get();
278#else
279 TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
280#endif
281 if (current_task == main_task_) {
282 return nullptr; // Main task
283 } else {
284#if defined(USE_ESP32)
285 return pcTaskGetName(current_task);
286#elif defined(USE_LIBRETINY)
287 return pcTaskGetTaskName(current_task);
288#elif defined(USE_ZEPHYR)
289 return k_thread_name_get(current_task);
290#endif
291 }
292 }
293#endif
294
295#ifdef USE_ESP32
296 inline bool HOT check_and_set_task_log_recursion_(bool is_main_task) {
297 if (is_main_task) {
298 const bool was_recursive = main_task_recursion_guard_;
300 return was_recursive;
301 }
302
303 intptr_t current = (intptr_t) pthread_getspecific(log_recursion_key_);
304 if (current != 0)
305 return true;
306
307 pthread_setspecific(log_recursion_key_, (void *) 1);
308 return false;
309 }
310
311 inline void HOT reset_task_log_recursion_(bool is_main_task) {
312 if (is_main_task) {
314 return;
315 }
316
317 pthread_setspecific(log_recursion_key_, (void *) 0);
318 }
319#endif
320
321 inline void HOT write_header_to_buffer_(uint8_t level, const char *tag, int line, const char *thread_name,
322 char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
323 // Format header
324 // uint8_t level is already bounded 0-255, just ensure it's <= 7
325 if (level > 7)
326 level = 7;
327
328 const char *color = esphome::logger::LOG_LEVEL_COLORS[level];
329 const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level];
330
331#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR)
332 if (thread_name != nullptr) {
333 // Non-main task with thread name
334 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
335 ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
336 return;
337 }
338#endif
339 // Main task or non ESP32/LibreTiny platform
340 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
341 }
342
343 inline void HOT format_body_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,
344 va_list args) {
345 // Get remaining capacity in the buffer
346 if (*buffer_at >= buffer_size)
347 return;
348 const uint16_t remaining = buffer_size - *buffer_at;
349
350 const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
351
352 if (ret < 0) {
353 return; // Encoding error, do not increment buffer_at
354 }
355
356 // Update buffer_at with the formatted length (handle truncation)
357 uint16_t formatted_len = (ret >= remaining) ? remaining : ret;
358 *buffer_at += formatted_len;
359
360 // Remove all trailing newlines right after formatting
361 while (*buffer_at > 0 && buffer[*buffer_at - 1] == '\n') {
362 (*buffer_at)--;
363 }
364 }
365
366 inline void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size) {
367 static constexpr uint16_t RESET_COLOR_LEN = sizeof(ESPHOME_LOG_RESET_COLOR) - 1;
368 this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
369 }
370
371#ifdef USE_ESP32
372 // Disable loop when task buffer is empty (with USB CDC check)
374 // Thread safety note: This is safe even if another task calls enable_loop_soon_any_context()
375 // concurrently. If that happens between our check and disable_loop(), the enable request
376 // will be processed on the next main loop iteration since:
377 // - disable_loop() takes effect immediately
378 // - enable_loop_soon_any_context() sets a pending flag that's checked at loop start
379 this->disable_loop();
380 }
381#endif
382};
383extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
384
385class LoggerMessageTrigger : public Trigger<uint8_t, const char *, const char *> {
386 public:
387 explicit LoggerMessageTrigger(Logger *parent, uint8_t level) {
388 this->level_ = level;
389 parent->add_on_log_callback([this](uint8_t level, const char *tag, const char *message, size_t message_len) {
390 if (level <= this->level_) {
391 this->trigger(level, tag, message);
392 }
393 });
394 }
395
396 protected:
397 uint8_t level_;
398};
399
400} // 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:263
void printf_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size, const char *format,...)
Definition logger.h:219
CallbackManager< void(uint8_t, const char *, const char *, size_t)> log_callback_
Definition logger.h:252
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:166
const char *HOT get_thread_name_()
Definition logger.h:275
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:187
void dump_config() override
Definition logger.cpp:256
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:343
uint32_t get_baud_rate() const
Definition logger.h:118
const LogString * 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:296
void loop() override
Definition logger.cpp:177
uint8_t get_log_level()
Definition logger.h:136
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:253
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:229
void HOT write_footer_to_buffer_(char *buffer, uint16_t *buffer_at, uint16_t buffer_size)
Definition logger.h:366
float get_setup_priority() const override
Definition logger.cpp:232
UARTSelection get_uart() const
Get the UART used by the logger.
Definition logger.cpp:226
std::map< std::string, uint8_t > log_levels_
Definition logger.h:251
void disable_loop_when_buffer_empty_()
Definition logger.h:373
uart_port_t uart_num_
Definition logger.h:247
std::unique_ptr< logger::TaskLogBuffer > log_buffer_
Definition logger.h:255
pthread_key_t log_recursion_key_
Definition logger.h:246
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:279
void set_baud_rate(uint32_t baud_rate)
Manually set the baud rate for serial, set to 0 to disable.
Definition logger.cpp:222
void set_uart_selection(UARTSelection uart_selection)
Definition logger.h:127
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:150
void HOT reset_task_log_recursion_(bool is_main_task)
Definition logger.h:311
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:201
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:321
bool main_task_recursion_guard_
Definition logger.h:269
const device * uart_dev_
Definition logger.h:237
uint16_t tx_buffer_size_
Definition logger.h:260
LoggerMessageTrigger(Logger *parent, uint8_t level)
Definition logger.h:387
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:288
uint16_t length
Definition tt21100.cpp:0