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