[logger] Reduce per-message overhead by inlining hot path helpers (#14851)

This commit is contained in:
J. Nick Koston
2026-03-16 07:35:04 -10:00
committed by GitHub
parent 7b4af76a61
commit 7131eafc09
10 changed files with 103 additions and 42 deletions
+19 -12
View File
@@ -111,7 +111,12 @@ struct LogBuffer {
}
#endif
void write_body(const char *text, uint16_t text_length) {
this->write_(text, text_length);
const uint16_t available = this->remaining_();
const uint16_t copy_len = (text_length < available) ? text_length : available;
if (copy_len > 0) {
memcpy(this->current_(), text, copy_len);
this->pos += copy_len;
}
this->finalize_();
}
@@ -119,21 +124,23 @@ struct LogBuffer {
bool full_() const { return this->pos >= this->size; }
uint16_t remaining_() const { return this->size - this->pos; }
char *current_() { return this->data + this->pos; }
void write_(const char *value, uint16_t length) {
const uint16_t available = this->remaining_();
const uint16_t copy_len = (length < available) ? length : available;
if (copy_len > 0) {
memcpy(this->current_(), value, copy_len);
this->pos += copy_len;
}
}
void finalize_() {
// Write color reset sequence
static constexpr uint16_t RESET_COLOR_LEN = sizeof(ESPHOME_LOG_RESET_COLOR) - 1;
this->write_(ESPHOME_LOG_RESET_COLOR, RESET_COLOR_LEN);
this->write_ansi_reset_();
// Null terminate
this->data[this->full_() ? this->size - 1 : this->pos] = '\0';
}
// Write ANSI reset sequence inline ("\033[0m") - avoids write_() call overhead
static constexpr uint16_t ANSI_RESET_LEN = 4; // "\033[0m"
void write_ansi_reset_() {
if (this->remaining_() >= ANSI_RESET_LEN) {
char *p = this->current_();
*p++ = '\033';
*p++ = '[';
*p++ = '0';
*p++ = 'm';
this->pos += ANSI_RESET_LEN;
}
}
void strip_trailing_newlines_() {
while (this->pos > 0 && this->data[this->pos - 1] == '\n')
this->pos--;
+17 -1
View File
@@ -233,7 +233,11 @@ class Logger final : public Component {
void cdc_loop_();
#endif
void process_messages_();
#if defined(USE_HOST) || defined(USE_ZEPHYR)
void write_msg_(const char *msg, uint16_t len);
#else
inline void write_msg_(const char *msg, uint16_t len); // Defined in platform-specific logger_*.h
#endif
// Format a log message with printf-style arguments and write it to a buffer with header, footer, and null terminator
// thread_name: name of the calling thread/task, or nullptr for main task (callers already know which task they're on)
@@ -366,7 +370,7 @@ class Logger final : public Component {
bool non_main_task_recursion_guard_{false}; // Shared guard for all non-main tasks on LibreTiny
#endif
#else
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
bool global_recursion_guard_{false}; // Simple global recursion guard for single-task platforms
#endif
// Large buffer placed last to keep frequently-accessed member offsets small
@@ -498,3 +502,15 @@ class LoggerMessageTrigger final : public Trigger<uint8_t, const char *, const c
};
} // namespace esphome::logger
// Platform-specific inline implementations of write_msg_()
// Must be included after the Logger class definition is complete
#if defined(USE_ESP32)
#include "logger_esp32.h"
#elif defined(USE_ESP8266)
#include "logger_esp8266.h"
#elif defined(USE_RP2040)
#include "logger_rp2040.h"
#elif defined(USE_LIBRETINY)
#include "logger_libretiny.h"
#endif
@@ -123,23 +123,6 @@ void Logger::pre_setup() {
#endif
}
void HOT Logger::write_msg_(const char *msg, uint16_t len) {
#if defined(USE_LOGGER_UART_SELECTION_USB_CDC) || defined(USE_LOGGER_UART_SELECTION_USB_SERIAL_JTAG)
// USB CDC/JTAG - single write including newline (already in buffer)
// Use fwrite to stdout which goes through VFS to USB console
//
// Note: These defines indicate the user's YAML configuration choice (hardware_uart: USB_CDC/USB_SERIAL_JTAG).
// They are ONLY defined when the user explicitly selects USB as the logger output in their config.
// This is compile-time selection, not runtime detection - if USB is configured, it's always used.
// There is no fallback to regular UART if "USB isn't connected" - that's the user's responsibility
// to configure correctly for their hardware. This approach eliminates runtime overhead.
fwrite(msg, 1, len, stdout);
#else
// Regular UART - single write including newline (already in buffer)
uart_write_bytes(this->uart_num_, msg, len);
#endif
}
const LogString *Logger::get_uart_selection_() {
switch (this->uart_) {
case UART_SELECTION_UART0:
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#ifdef USE_ESP32
#include "esphome/core/helpers.h"
#include <driver/uart.h>
namespace esphome::logger {
inline void HOT Logger::write_msg_(const char *msg, uint16_t len) {
#if defined(USE_LOGGER_UART_SELECTION_USB_CDC) || defined(USE_LOGGER_UART_SELECTION_USB_SERIAL_JTAG)
// USB CDC/JTAG - single write including newline (already in buffer)
// Use fwrite to stdout which goes through VFS to USB console
//
// Note: These defines indicate the user's YAML configuration choice (hardware_uart: USB_CDC/USB_SERIAL_JTAG).
// They are ONLY defined when the user explicitly selects USB as the logger output in their config.
// This is compile-time selection, not runtime detection - if USB is configured, it's always used.
// There is no fallback to regular UART if "USB isn't connected" - that's the user's responsibility
// to configure correctly for their hardware. This approach eliminates runtime overhead.
fwrite(msg, 1, len, stdout);
#else
// Regular UART - single write including newline (already in buffer)
uart_write_bytes(this->uart_num_, msg, len);
#endif
}
} // namespace esphome::logger
#endif
@@ -28,11 +28,6 @@ void Logger::pre_setup() {
ESP_LOGI(TAG, "Log initialized");
}
void HOT Logger::write_msg_(const char *msg, uint16_t len) {
// Single write with newline already in buffer (added by caller)
this->hw_serial_->write(msg, len);
}
const LogString *Logger::get_uart_selection_() {
#if defined(USE_ESP8266_LOGGER_SERIAL)
if (this->uart_ == UART_SELECTION_UART0_SWAP) {
@@ -0,0 +1,13 @@
#pragma once
#ifdef USE_ESP8266
#include "esphome/core/helpers.h"
namespace esphome::logger {
// Single write with newline already in buffer (added by caller)
inline void HOT Logger::write_msg_(const char *msg, uint16_t len) { this->hw_serial_->write(msg, len); }
} // namespace esphome::logger
#endif
@@ -49,8 +49,6 @@ void Logger::pre_setup() {
ESP_LOGI(TAG, "Log initialized");
}
void HOT Logger::write_msg_(const char *msg, uint16_t len) { this->hw_serial_->write(msg, len); }
const LogString *Logger::get_uart_selection_() {
switch (this->uart_) {
case UART_SELECTION_DEFAULT:
@@ -0,0 +1,13 @@
#pragma once
#ifdef USE_LIBRETINY
#include "esphome/core/helpers.h"
namespace esphome::logger {
// Single write with newline already in buffer (added by caller)
inline void HOT Logger::write_msg_(const char *msg, uint16_t len) { this->hw_serial_->write(msg, len); }
} // namespace esphome::logger
#endif
@@ -34,11 +34,6 @@ void Logger::pre_setup() {
#endif
}
void HOT Logger::write_msg_(const char *msg, uint16_t len) {
// Single write with newline already in buffer (added by caller)
this->hw_serial_->write(msg, len);
}
const LogString *Logger::get_uart_selection_() {
switch (this->uart_) {
case UART_SELECTION_UART0:
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#ifdef USE_RP2040
#include "esphome/core/helpers.h"
namespace esphome::logger {
// Single write with newline already in buffer (added by caller)
inline void HOT Logger::write_msg_(const char *msg, uint16_t len) { this->hw_serial_->write(msg, len); }
} // namespace esphome::logger
#endif