ESPHome 2026.3.0-dev
Loading...
Searching...
No Matches
log_buffer.h
Go to the documentation of this file.
1#pragma once
2
4#include "esphome/core/log.h"
5
6namespace esphome::logger {
7
8// Maximum header size: 35 bytes fixed + 32 bytes tag + 16 bytes thread name = 83 bytes (45 byte safety margin)
9static constexpr uint16_t MAX_HEADER_SIZE = 128;
10
11// ANSI color code last digit (30-38 range, store only last digit to save RAM)
12static constexpr char LOG_LEVEL_COLOR_DIGIT[] = {
13 '\0', // NONE
14 '1', // ERROR (31 = red)
15 '3', // WARNING (33 = yellow)
16 '2', // INFO (32 = green)
17 '5', // CONFIG (35 = magenta)
18 '6', // DEBUG (36 = cyan)
19 '7', // VERBOSE (37 = gray)
20 '8', // VERY_VERBOSE (38 = white)
21};
22
23static constexpr char LOG_LEVEL_LETTER_CHARS[] = {
24 '\0', // NONE
25 'E', // ERROR
26 'W', // WARNING
27 'I', // INFO
28 'C', // CONFIG
29 'D', // DEBUG
30 'V', // VERBOSE (VERY_VERBOSE uses two 'V's)
31};
32
33// Buffer wrapper for log formatting functions
34struct LogBuffer {
35 char *data;
36 uint16_t size;
37 uint16_t pos{0};
38 // Replaces the null terminator with a newline for console output.
39 // Must be called after notify_listeners_() since listeners need null-terminated strings.
40 // Console output uses length-based writes (buf.pos), so null terminator is not needed.
42 if (this->pos < this->size) {
43 this->data[this->pos++] = '\n';
44 } else if (this->size > 0) {
45 // Buffer was full - replace last char with newline to ensure it's visible
46 this->data[this->size - 1] = '\n';
47 this->pos = this->size;
48 }
49 }
50 void HOT write_header(uint8_t level, const char *tag, int line, const char *thread_name) {
51 // Early return if insufficient space - intentionally don't update pos to prevent partial writes
52 if (this->pos + MAX_HEADER_SIZE > this->size)
53 return;
54
55 char *p = this->current_();
56
57 // Write ANSI color
58 this->write_ansi_color_(p, level);
59
60 // Construct: [LEVEL][tag:line]
61 *p++ = '[';
62 if (level != 0) {
63 if (level >= 7) {
64 *p++ = 'V'; // VERY_VERBOSE = "VV"
65 *p++ = 'V';
66 } else {
67 *p++ = LOG_LEVEL_LETTER_CHARS[level];
68 }
69 }
70 *p++ = ']';
71 *p++ = '[';
72
73 // Copy tag
74 this->copy_string_(p, tag);
75
76 *p++ = ':';
77
78 // Format line number without modulo operations
79 if (line > 999) [[unlikely]] {
80 int thousands = line / 1000;
81 *p++ = '0' + thousands;
82 line -= thousands * 1000;
83 }
84 int hundreds = line / 100;
85 int remainder = line - hundreds * 100;
86 int tens = remainder / 10;
87 *p++ = '0' + hundreds;
88 *p++ = '0' + tens;
89 *p++ = '0' + (remainder - tens * 10);
90 *p++ = ']';
91
92#if defined(USE_ESP32) || defined(USE_LIBRETINY) || defined(USE_ZEPHYR) || defined(USE_HOST)
93 // Write thread name with bold red color
94 if (thread_name != nullptr) {
95 this->write_ansi_color_(p, 1); // Bold red for thread name
96 *p++ = '[';
97 this->copy_string_(p, thread_name);
98 *p++ = ']';
99 this->write_ansi_color_(p, level); // Restore original color
100 }
101#endif
102
103 *p++ = ':';
104 *p++ = ' ';
105
106 this->pos = p - this->data;
107 }
108 void HOT format_body(const char *format, va_list args) {
109 this->format_vsnprintf_(format, args);
110 this->finalize_();
111 }
112#ifdef USE_STORE_LOG_STR_IN_FLASH
113 void HOT format_body_P(PGM_P format, va_list args) {
114 this->format_vsnprintf_P_(format, args);
115 this->finalize_();
116 }
117#endif
118 void write_body(const char *text, uint16_t text_length) {
119 this->write_(text, text_length);
120 this->finalize_();
121 }
122
123 private:
124 bool full_() const { return this->pos >= this->size; }
125 uint16_t remaining_() const { return this->size - this->pos; }
126 char *current_() { return this->data + this->pos; }
127 void write_(const char *value, uint16_t length) {
128 const uint16_t available = this->remaining_();
129 const uint16_t copy_len = (length < available) ? length : available;
130 if (copy_len > 0) {
131 memcpy(this->current_(), value, copy_len);
132 this->pos += copy_len;
133 }
134 }
135 void finalize_() {
136 // Write color reset sequence
137 static constexpr uint16_t RESET_COLOR_LEN = sizeof(ESPHOME_LOG_RESET_COLOR) - 1;
138 this->write_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN);
139 // Null terminate
140 this->data[this->full_() ? this->size - 1 : this->pos] = '\0';
141 }
142 void strip_trailing_newlines_() {
143 while (this->pos > 0 && this->data[this->pos - 1] == '\n')
144 this->pos--;
145 }
146 void process_vsnprintf_result_(int ret) {
147 if (ret < 0)
148 return;
149 const uint16_t rem = this->remaining_();
150 this->pos += (ret >= rem) ? (rem - 1) : static_cast<uint16_t>(ret);
151 this->strip_trailing_newlines_();
152 }
153 void format_vsnprintf_(const char *format, va_list args) {
154 if (this->full_())
155 return;
156 this->process_vsnprintf_result_(vsnprintf(this->current_(), this->remaining_(), format, args));
157 }
158#ifdef USE_STORE_LOG_STR_IN_FLASH
159 void format_vsnprintf_P_(PGM_P format, va_list args) {
160 if (this->full_())
161 return;
162 this->process_vsnprintf_result_(vsnprintf_P(this->current_(), this->remaining_(), format, args));
163 }
164#endif
165 // Write ANSI color escape sequence to buffer, updates pointer in place
166 // Caller is responsible for ensuring buffer has sufficient space
167 void write_ansi_color_(char *&p, uint8_t level) {
168 if (level == 0)
169 return;
170 // Direct buffer fill: "\033[{bold};3{color}m" (7 bytes)
171 *p++ = '\033';
172 *p++ = '[';
173 *p++ = (level == 1) ? '1' : '0'; // Only ERROR is bold
174 *p++ = ';';
175 *p++ = '3';
176 *p++ = LOG_LEVEL_COLOR_DIGIT[level];
177 *p++ = 'm';
178 }
179 // Copy string without null terminator, updates pointer in place
180 // Caller is responsible for ensuring buffer has sufficient space
181 void copy_string_(char *&p, const char *str) {
182 const size_t len = strlen(str);
183 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - intentionally no null terminator, building string piece by
184 // piece
185 memcpy(p, str, len);
186 p += len;
187 }
188};
189
190} // namespace esphome::logger
std::string size_t len
Definition helpers.h:692
void HOT format_body_P(PGM_P format, va_list args)
Definition log_buffer.h:113
void write_body(const char *text, uint16_t text_length)
Definition log_buffer.h:118
void HOT write_header(uint8_t level, const char *tag, int line, const char *thread_name)
Definition log_buffer.h:50
void HOT format_body(const char *format, va_list args)
Definition log_buffer.h:108
uint16_t length
Definition tt21100.cpp:0