ESPHome 2025.6.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->log_callback_.call(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
132Logger::Logger(uint32_t baud_rate, size_t tx_buffer_size) : baud_rate_(baud_rate), tx_buffer_size_(tx_buffer_size) {
133 // add 1 to buffer size for null terminator
134 this->tx_buffer_ = new char[this->tx_buffer_size_ + 1]; // NOLINT
135#if defined(USE_ESP32) || defined(USE_LIBRETINY)
136 this->main_task_ = xTaskGetCurrentTaskHandle();
137#endif
138}
139#ifdef USE_ESPHOME_TASK_LOG_BUFFER
140void Logger::init_log_buffer(size_t total_buffer_size) {
142}
143#endif
144
145#if defined(USE_LOGGER_USB_CDC) || defined(USE_ESP32)
147#if defined(USE_LOGGER_USB_CDC) && defined(USE_ARDUINO)
148 if (this->uart_ == UART_SELECTION_USB_CDC) {
149 static bool opened = false;
150 if (opened == Serial) {
151 return;
152 }
153 if (false == opened) {
155 }
156 opened = !opened;
157 }
158#endif
159
160#ifdef USE_ESPHOME_TASK_LOG_BUFFER
161 // Process any buffered messages when available
162 if (this->log_buffer_->has_messages()) {
164 const char *text;
165 void *received_token;
166
167 // Process messages from the buffer
168 while (this->log_buffer_->borrow_message_main_loop(&message, &text, &received_token)) {
169 this->tx_buffer_at_ = 0;
170 // Use the thread name that was stored when the message was created
171 // This avoids potential crashes if the task no longer exists
172 const char *thread_name = message->thread_name[0] != '\0' ? message->thread_name : nullptr;
173 this->write_header_to_buffer_(message->level, message->tag, message->line, thread_name, this->tx_buffer_,
174 &this->tx_buffer_at_, this->tx_buffer_size_);
175 this->write_body_to_buffer_(text, message->text_length, this->tx_buffer_, &this->tx_buffer_at_,
176 this->tx_buffer_size_);
178 this->tx_buffer_[this->tx_buffer_at_] = '\0';
179 this->log_callback_.call(message->level, message->tag, this->tx_buffer_);
180 // At this point all the data we need from message has been transferred to the tx_buffer
181 // so we can release the message to allow other tasks to use it as soon as possible.
182 this->log_buffer_->release_message_main_loop(received_token);
183
184 // Write to console from the main loop to prevent corruption from concurrent writes
185 // This ensures all log messages appear on the console in a clean, serialized manner
186 // Note: Messages may appear slightly out of order due to async processing, but
187 // this is preferred over corrupted/interleaved console output
188 if (this->baud_rate_ > 0) {
189 this->write_msg_(this->tx_buffer_);
190 }
191 }
192 }
193#endif
194}
195#endif
196
197void Logger::set_baud_rate(uint32_t baud_rate) { this->baud_rate_ = baud_rate; }
198void Logger::set_log_level(const std::string &tag, int log_level) { this->log_levels_[tag] = log_level; }
199
200#if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY)
201UARTSelection Logger::get_uart() const { return this->uart_; }
202#endif
203
204void Logger::add_on_log_callback(std::function<void(int, const char *, const char *)> &&callback) {
205 this->log_callback_.add(std::move(callback));
206}
207float Logger::get_setup_priority() const { return setup_priority::BUS + 500.0f; }
208static const char *const LOG_LEVELS[] = {"NONE", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "VERBOSE", "VERY_VERBOSE"};
209
211 ESP_LOGCONFIG(TAG,
212 "Logger:\n"
213 " Max Level: %s\n"
214 " Initial Level: %s",
215 LOG_LEVELS[ESPHOME_LOG_LEVEL], LOG_LEVELS[this->current_level_]);
216#ifndef USE_HOST
217 ESP_LOGCONFIG(TAG,
218 " Log Baud Rate: %" PRIu32 "\n"
219 " Hardware UART: %s",
221#endif
222#ifdef USE_ESPHOME_TASK_LOG_BUFFER
223 if (this->log_buffer_) {
224 ESP_LOGCONFIG(TAG, " Task Log Buffer Size: %u", this->log_buffer_->size());
225 }
226#endif
227
228 for (auto &it : this->log_levels_) {
229 ESP_LOGCONFIG(TAG, " Level for '%s': %s", it.first.c_str(), LOG_LEVELS[it.second]);
230 }
231}
232
233void Logger::set_log_level(int level) {
234 if (level > ESPHOME_LOG_LEVEL) {
235 level = ESPHOME_LOG_LEVEL;
236 ESP_LOGW(TAG, "Cannot set log level higher than pre-compiled %s", LOG_LEVELS[ESPHOME_LOG_LEVEL]);
237 }
238 this->current_level_ = level;
239 this->level_callback_.call(level);
240}
241
242Logger *global_logger = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
243
244} // namespace logger
245} // 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
void dump_config() override
Definition logger.cpp:210
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 loop() override
Definition logger.cpp:146
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
std::unique_ptr< logger::TaskLogBuffer > log_buffer_
Definition logger.h:245
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 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 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
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
UARTSelection
Enum for logging UART selection.
Definition logger.h:64
@ UART_SELECTION_USB_CDC
Definition logger.h:76
Logger * global_logger
Definition logger.cpp:242
const float BUS
For communication buses like i2c/spi.
Definition component.cpp:17
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:86
Application App
Global storage of Application pointer - only one Application can exist.
uint8_t progmem_read_byte(const uint8_t *addr)
Definition core.cpp:58