ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
task_log_buffer_esp32.cpp
Go to the documentation of this file.
1#ifdef USE_ESP32
2
5#include "esphome/core/log.h"
6
7#ifdef USE_ESPHOME_TASK_LOG_BUFFER
8
9namespace esphome::logger {
10
11TaskLogBuffer::TaskLogBuffer(size_t total_buffer_size) {
12 // Store the buffer size
13 this->size_ = total_buffer_size;
14 // Allocate memory for the ring buffer using ESPHome's RAM allocator
15 RAMAllocator<uint8_t> allocator;
16 this->storage_ = allocator.allocate(this->size_);
17 // Create a static ring buffer with RINGBUF_TYPE_NOSPLIT for message integrity
18 this->ring_buffer_ = xRingbufferCreateStatic(this->size_, RINGBUF_TYPE_NOSPLIT, this->storage_, &this->structure_);
19}
20
22 if (this->ring_buffer_ != nullptr) {
23 // Delete the ring buffer
24 vRingbufferDelete(this->ring_buffer_);
25 this->ring_buffer_ = nullptr;
26
27 // Free the allocated memory
28 RAMAllocator<uint8_t> allocator;
29 allocator.deallocate(this->storage_, this->size_);
30 this->storage_ = nullptr;
31 }
32}
33
35 if (this->current_token_) {
36 return false;
37 }
38
39 size_t item_size = 0;
40 void *received_item = xRingbufferReceive(ring_buffer_, &item_size, 0);
41 if (received_item == nullptr) {
42 return false;
43 }
44
45 LogMessage *msg = static_cast<LogMessage *>(received_item);
46 message = msg;
47 text_length = msg->text_length;
48 this->current_token_ = received_item;
49
50 return true;
51}
52
54 if (this->current_token_ == nullptr) {
55 return;
56 }
57 vRingbufferReturnItem(ring_buffer_, this->current_token_);
58 this->current_token_ = nullptr;
59 // Update counter to mark all messages as processed
60 last_processed_counter_ = message_counter_.load(std::memory_order_relaxed);
61}
62
63bool TaskLogBuffer::send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name,
64 const char *format, va_list args) {
65 // First, calculate the exact length needed using a null buffer (no actual writing)
66 va_list args_copy;
67 va_copy(args_copy, args);
68 int ret = vsnprintf(nullptr, 0, format, args_copy);
69 va_end(args_copy);
70
71 if (ret <= 0) {
72 return false; // Formatting error or empty message
73 }
74
75 // Calculate actual text length (capped to maximum size)
76 static constexpr size_t MAX_TEXT_SIZE = 255;
77 size_t text_length = (static_cast<size_t>(ret) > MAX_TEXT_SIZE) ? MAX_TEXT_SIZE : ret;
78
79 // Calculate total size needed (header + text length + null terminator)
80 size_t total_size = sizeof(LogMessage) + text_length + 1;
81
82 // Acquire memory directly from the ring buffer
83 void *acquired_memory = nullptr;
84 BaseType_t result = xRingbufferSendAcquire(ring_buffer_, &acquired_memory, total_size, 0);
85
86 if (result != pdTRUE || acquired_memory == nullptr) {
87 return false; // Failed to acquire memory
88 }
89
90 // Set up the message header in the acquired memory
91 LogMessage *msg = static_cast<LogMessage *>(acquired_memory);
92 msg->level = level;
93 msg->tag = tag;
94 msg->line = line;
95
96 // Store the thread name now instead of waiting until main loop processing
97 // This avoids crashes if the task completes or is deleted between when this message
98 // is enqueued and when it's processed by the main loop
99 if (thread_name != nullptr) {
100 strncpy(msg->thread_name, thread_name, sizeof(msg->thread_name) - 1);
101 msg->thread_name[sizeof(msg->thread_name) - 1] = '\0'; // Ensure null termination
102 } else {
103 msg->thread_name[0] = '\0'; // Empty string if no thread name
104 }
105
106 // Format the message text directly into the acquired memory
107 // We add 1 to text_length to ensure space for null terminator during formatting
108 char *text_area = msg->text_data();
109 ret = vsnprintf(text_area, text_length + 1, format, args);
110
111 // Handle unexpected formatting error
112 if (ret <= 0) {
113 vRingbufferReturnItem(ring_buffer_, acquired_memory);
114 return false;
115 }
116
117 // Remove trailing newlines
118 while (text_length > 0 && text_area[text_length - 1] == '\n') {
119 text_length--;
120 }
121
122 msg->text_length = text_length;
123 // Complete the send operation with the acquired memory
124 result = xRingbufferSendComplete(ring_buffer_, acquired_memory);
125
126 if (result != pdTRUE) {
127 return false; // Failed to complete the message send
128 }
129
130 // Message sent successfully, increment the counter
131 message_counter_.fetch_add(1, std::memory_order_relaxed);
132 return true;
133}
134
135} // namespace esphome::logger
136
137#endif // USE_ESPHOME_TASK_LOG_BUFFER
138#endif // USE_ESP32
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1665
void deallocate(T *p, size_t n)
Definition helpers.h:1723
T * allocate(size_t n)
Definition helpers.h:1685
TaskLogBuffer(size_t total_buffer_size)
bool borrow_message_main_loop(LogMessage *&message, uint16_t &text_length)
bool send_message_thread_safe(uint8_t level, const char *tag, uint16_t line, const char *thread_name, const char *format, va_list args)
const char * message
Definition component.cpp:38
va_end(args)