ESPHome 2025.6.3
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
32namespace esphome {
33
34namespace logger {
35
36// Color and letter constants for log levels
37static const char *const LOG_LEVEL_COLORS[] = {
38 "", // NONE
39 ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), // ERROR
40 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_YELLOW), // WARNING
41 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GREEN), // INFO
42 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_MAGENTA), // CONFIG
43 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_CYAN), // DEBUG
44 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_GRAY), // VERBOSE
45 ESPHOME_LOG_COLOR(ESPHOME_LOG_COLOR_WHITE), // VERY_VERBOSE
46};
47
48static const char *const LOG_LEVEL_LETTERS[] = {
49 "", // NONE
50 "E", // ERROR
51 "W", // WARNING
52 "I", // INFO
53 "C", // CONFIG
54 "D", // DEBUG
55 "V", // VERBOSE
56 "VV", // VERY_VERBOSE
57};
58
59#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
65#ifdef USE_LIBRETINY
68#else
70#endif
72#if defined(USE_LIBRETINY) || defined(USE_ESP32_VARIANT_ESP32)
74#endif
75#ifdef USE_LOGGER_USB_CDC
77#endif
78#ifdef USE_LOGGER_USB_SERIAL_JTAG
80#endif
81#ifdef USE_ESP8266
83#endif // USE_ESP8266
84};
85#endif // USE_ESP32 || USE_ESP8266 || USE_RP2040 || USE_LIBRETINY
86
104class Logger : public Component {
105 public:
106 explicit Logger(uint32_t baud_rate, size_t tx_buffer_size);
107#ifdef USE_ESPHOME_TASK_LOG_BUFFER
108 void init_log_buffer(size_t total_buffer_size);
109#endif
110#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
111 void loop() override;
112#endif
114 void set_baud_rate(uint32_t baud_rate);
115 uint32_t get_baud_rate() const { return baud_rate_; }
116#ifdef USE_ARDUINO
117 Stream *get_hw_serial() const { return hw_serial_; }
118#endif
119#ifdef USE_ESP_IDF
120 uart_port_t get_uart_num() const { return uart_num_; }
121#endif
122#ifdef USE_ESP32
123 void create_pthread_key() { pthread_key_create(&log_recursion_key_, nullptr); }
124#endif
125#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
126 void set_uart_selection(UARTSelection uart_selection) { uart_ = uart_selection; }
128 UARTSelection get_uart() const;
129#endif
130
132 void set_log_level(int level);
134 void set_log_level(const std::string &tag, int log_level);
135 int get_log_level() { return this->current_level_; }
136
137 // ========== INTERNAL METHODS ==========
138 // (In most use cases you won't need these)
140 void pre_setup();
141 void dump_config() override;
142
143 inline int level_for(const char *tag);
144
146 void add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback);
147
148 // add a listener for log level changes
149 void add_listener(std::function<void(int)> &&callback) { this->level_callback_.add(std::move(callback)); }
150
151 float get_setup_priority() const override;
152
153 void log_vprintf_(int level, const char *tag, int line, const char *format, va_list args); // NOLINT
154#ifdef USE_STORE_LOG_STR_IN_FLASH
155 void log_vprintf_(int level, const char *tag, int line, const __FlashStringHelper *format, va_list args); // NOLINT
156#endif
157
158 protected:
159 void write_msg_(const char *msg);
160
161 // Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
162 // It's the caller's responsibility to initialize buffer_at (typically to 0)
163 inline void HOT format_log_to_buffer_with_terminator_(int level, const char *tag, int line, const char *format,
164 va_list args, char *buffer, int *buffer_at, int buffer_size) {
165#if defined(USE_ESP32) || defined(USE_LIBRETINY)
166 this->write_header_to_buffer_(level, tag, line, this->get_thread_name_(), buffer, buffer_at, buffer_size);
167#else
168 this->write_header_to_buffer_(level, tag, line, nullptr, buffer, buffer_at, buffer_size);
169#endif
170 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, args);
171 this->write_footer_to_buffer_(buffer, buffer_at, buffer_size);
172
173 // Always ensure the buffer has a null terminator, even if we need to
174 // overwrite the last character of the actual content
175 if (*buffer_at >= buffer_size) {
176 buffer[buffer_size - 1] = '\0'; // Truncate and ensure null termination
177 } else {
178 buffer[*buffer_at] = '\0'; // Normal case, append null terminator
179 }
180 }
181
182 // Helper to format and send a log message to both console and callbacks
183 inline void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format,
184 va_list args) {
185 // Format to tx_buffer and prepare for output
186 this->tx_buffer_at_ = 0; // Initialize buffer position
187 this->format_log_to_buffer_with_terminator_(level, tag, line, format, args, this->tx_buffer_, &this->tx_buffer_at_,
188 this->tx_buffer_size_);
189
190 if (this->baud_rate_ > 0) {
191 this->write_msg_(this->tx_buffer_); // If logging is enabled, write to console
192 }
193 this->log_callback_.call(level, tag, this->tx_buffer_);
194 }
195
196 // Write the body of the log message to the buffer
197 inline void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size) {
198 // Calculate available space
199 const int available = buffer_size - *buffer_at;
200 if (available <= 0)
201 return;
202
203 // Determine copy length (minimum of remaining capacity and string length)
204 const size_t copy_len = (length < static_cast<size_t>(available)) ? length : available;
205
206 // Copy the data
207 if (copy_len > 0) {
208 memcpy(buffer + *buffer_at, value, copy_len);
209 *buffer_at += copy_len;
210 }
211 }
212
213 // Format string to explicit buffer with varargs
214 inline void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, ...) {
215 va_list arg;
216 va_start(arg, format);
217 this->format_body_to_buffer_(buffer, buffer_at, buffer_size, format, arg);
218 va_end(arg);
219 }
220
221#ifndef USE_HOST
222 const char *get_uart_selection_();
223#endif
224
225 uint32_t baud_rate_;
226 char *tx_buffer_{nullptr};
229#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040)
231#endif
232#ifdef USE_LIBRETINY
234#endif
235#ifdef USE_ARDUINO
236 Stream *hw_serial_{nullptr};
237#endif
238#ifdef USE_ESP_IDF
239 uart_port_t uart_num_;
240#endif
241 std::map<std::string, int> log_levels_{};
242 CallbackManager<void(int, const char *, const char *)> log_callback_{};
243 int current_level_{ESPHOME_LOG_LEVEL_VERY_VERBOSE};
244#ifdef USE_ESPHOME_TASK_LOG_BUFFER
245 std::unique_ptr<logger::TaskLogBuffer> log_buffer_; // Will be initialized with init_log_buffer
246#endif
247#ifdef USE_ESP32
248 // Task-specific recursion guards:
249 // - Main task uses a dedicated member variable for efficiency
250 // - Other tasks use pthread TLS with a dynamically created key via pthread_key_create
252 pthread_key_t log_recursion_key_;
253#else
254 bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
255#endif
257
258#if defined(USE_ESP32) || defined(USE_LIBRETINY)
259 void *main_task_ = nullptr; // Only used for thread name identification
260 const char *HOT get_thread_name_() {
261 TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
262 if (current_task == main_task_) {
263 return nullptr; // Main task
264 } else {
265#if defined(USE_ESP32)
266 return pcTaskGetName(current_task);
267#elif defined(USE_LIBRETINY)
268 return pcTaskGetTaskName(current_task);
269#endif
270 }
271 }
272#endif
273
274#ifdef USE_ESP32
275 inline bool HOT check_and_set_task_log_recursion_(bool is_main_task) {
276 if (is_main_task) {
277 const bool was_recursive = main_task_recursion_guard_;
279 return was_recursive;
280 }
281
282 intptr_t current = (intptr_t) pthread_getspecific(log_recursion_key_);
283 if (current != 0)
284 return true;
285
286 pthread_setspecific(log_recursion_key_, (void *) 1);
287 return false;
288 }
289
290 inline void HOT reset_task_log_recursion_(bool is_main_task) {
291 if (is_main_task) {
293 return;
294 }
295
296 pthread_setspecific(log_recursion_key_, (void *) 0);
297 }
298#endif
299
300 inline void HOT write_header_to_buffer_(int level, const char *tag, int line, const char *thread_name, char *buffer,
301 int *buffer_at, int buffer_size) {
302 // Format header
303 if (level < 0)
304 level = 0;
305 if (level > 7)
306 level = 7;
307
308 const char *color = esphome::logger::LOG_LEVEL_COLORS[level];
309 const char *letter = esphome::logger::LOG_LEVEL_LETTERS[level];
310
311#if defined(USE_ESP32) || defined(USE_LIBRETINY)
312 if (thread_name != nullptr) {
313 // Non-main task with thread name
314 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]%s[%s]%s: ", color, letter, tag, line,
315 ESPHOME_LOG_BOLD(ESPHOME_LOG_COLOR_RED), thread_name, color);
316 return;
317 }
318#endif
319 // Main task or non ESP32/LibreTiny platform
320 this->printf_to_buffer_(buffer, buffer_at, buffer_size, "%s[%s][%s:%03u]: ", color, letter, tag, line);
321 }
322
323 inline void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format,
324 va_list args) {
325 // Get remaining capacity in the buffer
326 const int remaining = buffer_size - *buffer_at;
327 if (remaining <= 0)
328 return;
329
330 const int ret = vsnprintf(buffer + *buffer_at, remaining, format, args);
331
332 if (ret < 0) {
333 return; // Encoding error, do not increment buffer_at
334 }
335
336 // Update buffer_at with the formatted length (handle truncation)
337 int formatted_len = (ret >= remaining) ? remaining : ret;
338 *buffer_at += formatted_len;
339
340 // Remove all trailing newlines right after formatting
341 while (*buffer_at > 0 && buffer[*buffer_at - 1] == '\n') {
342 (*buffer_at)--;
343 }
344 }
345
346 inline void HOT write_footer_to_buffer_(char *buffer, int *buffer_at, int buffer_size) {
347 static const int RESET_COLOR_LEN = strlen(ESPHOME_LOG_RESET_COLOR);
348 this->write_body_to_buffer_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN, buffer, buffer_at, buffer_size);
349 }
350};
351extern Logger *global_logger; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
352
353class LoggerMessageTrigger : public Trigger<int, const char *, const char *> {
354 public:
355 explicit LoggerMessageTrigger(Logger *parent, int level) {
356 this->level_ = level;
357 parent->add_on_log_callback([this](int level, const char *tag, const char *message) {
358 if (level <= this->level_) {
359 this->trigger(level, tag, message);
360 }
361 });
362 }
363
364 protected:
366};
367
368} // namespace logger
369
370} // namespace esphome
Logger component for all ESPHome logging.
Definition logger.h:104
UARTSelection uart_
Definition logger.h:230
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:346
const char *HOT get_thread_name_()
Definition logger.h:260
void dump_config() override
Definition logger.cpp:210
uint32_t get_baud_rate() const
Definition logger.h:115
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:275
void printf_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format,...)
Definition logger.h:214
void loop() override
Definition logger.cpp:146
Stream * get_hw_serial() const
Definition logger.h:117
void pre_setup()
Set up this component.
CallbackManager< void(int, const char *, const char *)> log_callback_
Definition logger.h:242
void HOT log_message_to_buffer_and_send_(int level, const char *tag, int line, const char *format, va_list args)
Definition logger.h:183
float get_setup_priority() const override
Definition logger.cpp:207
UARTSelection get_uart() const
Get the UART used by the logger.
Definition logger.cpp:201
uart_port_t uart_num_
Definition logger.h:239
std::unique_ptr< logger::TaskLogBuffer > log_buffer_
Definition logger.h:245
pthread_key_t log_recursion_key_
Definition logger.h:252
uart_port_t get_uart_num() const
Definition logger.h:120
void init_log_buffer(size_t total_buffer_size)
Definition logger.cpp:140
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:300
void set_baud_rate(uint32_t baud_rate)
Manually set the baud rate for serial, set to 0 to disable.
Definition logger.cpp:197
void set_uart_selection(UARTSelection uart_selection)
Definition logger.h:126
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:163
Logger(uint32_t baud_rate, size_t tx_buffer_size)
Definition logger.cpp:132
std::map< std::string, int > log_levels_
Definition logger.h:241
void HOT format_body_to_buffer_(char *buffer, int *buffer_at, int buffer_size, const char *format, va_list args)
Definition logger.h:323
void write_body_to_buffer_(const char *value, size_t length, char *buffer, int *buffer_at, int buffer_size)
Definition logger.h:197
void HOT reset_task_log_recursion_(bool is_main_task)
Definition logger.h:290
void write_msg_(const char *msg)
CallbackManager< void(int)> level_callback_
Definition logger.h:256
bool main_task_recursion_guard_
Definition logger.h:251
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:204
void set_log_level(int level)
Set the default log level for this logger.
Definition logger.cpp:233
void add_listener(std::function< void(int)> &&callback)
Definition logger.h:149
LoggerMessageTrigger(Logger *parent, int level)
Definition logger.h:355
UARTSelection
Enum for logging UART selection.
Definition logger.h:64
@ UART_SELECTION_UART0_SWAP
Definition logger.h:82
@ UART_SELECTION_UART2
Definition logger.h:73
@ UART_SELECTION_USB_SERIAL_JTAG
Definition logger.h:79
@ UART_SELECTION_DEFAULT
Definition logger.h:66
@ UART_SELECTION_USB_CDC
Definition logger.h:76
@ UART_SELECTION_UART0
Definition logger.h:67
@ UART_SELECTION_UART1
Definition logger.h:71
Logger * global_logger
Definition logger.cpp:242
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t length
Definition tt21100.cpp:0