Skip to content

Commit 8418d6f

Browse files
feat(log_router): Option to append string to truncated log messages
Detect when a log message is truncated, and append a short string to indicate this. The string is formatted as purple italics to distinguish it from normal log messages. Also appends a newline character, so that the next log message starts on its own line. The string can be changed using menuconfig. An empty string will only print a newline character. It can also be completely disabled.
1 parent 07a0117 commit 8418d6f

2 files changed

Lines changed: 130 additions & 80 deletions

File tree

components/utilities/log_router/Kconfig

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,29 @@ menu "LOG ROUTER"
3636
Larger buffer can handle longer log messages but uses more stack space.
3737
Range: 256 to 1024 bytes
3838

39+
config LOG_ROUTER_APPEND_STRING_ON_TRUNCATE
40+
bool "Show message on truncate"
41+
default y
42+
help
43+
When enabled, log messages that are truncated because they do not fit
44+
into LOG_ROUTER_FORMAT_BUFFER_SIZE will have a string appended to them,
45+
to indicate that the log message is incomplete.
46+
47+
if LOG_ROUTER_APPEND_STRING_ON_TRUNCATE
48+
config LOG_ROUTER_TRUNCATE_STRING
49+
string
50+
default "<TRUNCATED>"
51+
prompt "Append this string to truncated logs"
52+
help
53+
String appended to truncated log messages. If log colors are enabled,
54+
it will be formatted as purple italics: see CONFIG_LOG_COLORS
55+
56+
A newline character is also added, so that the next log message starts
57+
on its own line.
58+
59+
60+
endif #LOG_ROUTER_APPEND_STRING_ON_TRUNCATE
61+
3962
config LOG_ROUTER_DEBUG_OUTPUT
4063
bool "Enable debug output"
4164
default y

components/utilities/log_router/log_router.c

Lines changed: 107 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@
1515
#include "freertos/FreeRTOS.h"
1616
#include "freertos/semphr.h"
1717
#include "log_router.h"
18+
#include "esp_log_color.h"
19+
20+
#include "sdkconfig.h"
1821

1922
static const char *TAG = "log_router";
2023

24+
// Truncated messages will be immediately followed by this message
25+
#if (CONFIG_LOG_ROUTER_APPEND_STRING_ON_TRUNCATE)
26+
#if (CONFIG_LOG_COLORS)
27+
static const char *TRUNCATE_MESSAGE = LOG_ITALIC(LOG_COLOR_PURPLE) "" CONFIG_LOG_ROUTER_TRUNCATE_STRING "" LOG_RESET_COLOR "\n" ;
28+
#else
29+
static const char *TRUNCATE_MESSAGE = CONFIG_LOG_ROUTER_TRUNCATE_STRING "\n" ;
30+
#endif
31+
#endif
2132
// Global mutex for thread safety
2233
static SemaphoreHandle_t g_log_router_mutex = NULL;
2334

@@ -93,6 +104,96 @@ static esp_err_t esp_log_router_flush_buffer(esp_log_router_slist_t *item)
93104
return ESP_OK;
94105
}
95106

107+
static void router_fwrite(const void *__restrict buffer, size_t size, size_t n, esp_log_router_slist_t *item, uint32_t now)
108+
{
109+
bool should_flush = false;
110+
111+
size_t len = n * size;
112+
113+
// Check if we need to flush due to threshold
114+
if ((item->buffer_pos + len) >= item->flush_threshold) {
115+
should_flush = true;
116+
log_router_debug_printf("Flush buffer due to threshold\n");
117+
}
118+
119+
// Check if we need to flush due to timeout
120+
if ((now - item->last_flush_time) >= item->flush_timeout_ms) {
121+
should_flush = true;
122+
log_router_debug_printf("Flush buffer due to timeout\n");
123+
}
124+
125+
// Flush buffer if needed
126+
if (should_flush) {
127+
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
128+
uint64_t flush_start_time = esp_timer_get_time();
129+
#endif
130+
esp_err_t flush_ret = esp_log_router_flush_buffer(item);
131+
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
132+
uint32_t flush_duration_us = esp_timer_get_time() - flush_start_time;
133+
log_router_debug_printf("Flush operation took %ums, file: %s\n", flush_duration_us / 1000, item->file_path);
134+
#endif
135+
if (flush_ret != ESP_OK) {
136+
log_router_debug_printf("Failed to flush buffer: %s\n", esp_err_to_name(flush_ret));
137+
// Flush failed, write directly to file
138+
size_t written = fwrite(buffer, 1, len, item->log_fp);
139+
if (written != len) {
140+
log_router_debug_printf("Failed to write directly to file, written: %zu, expected: %d\n", written, len);
141+
// Try to seek to beginning and retry
142+
if (fseek(item->log_fp, 0, SEEK_SET) == 0 && ftruncate(fileno(item->log_fp), 0) == 0) {
143+
log_router_debug_printf("File truncated, retrying direct write\n");
144+
written = fwrite(buffer, 1, len, item->log_fp);
145+
if (written != len) {
146+
log_router_debug_printf("Retry direct write also failed\n");
147+
return;
148+
}
149+
} else {
150+
log_router_debug_printf("Failed to seek/truncate file for direct write\n");
151+
return;
152+
}
153+
}
154+
fflush(item->log_fp);
155+
fsync(fileno(item->log_fp));
156+
return; // Skip buffer write since we already wrote to file
157+
}
158+
// Flush successful, buffer is now empty, continue to buffer write
159+
}
160+
161+
// Try to add to buffer, if still too large, write directly to file
162+
if ((item->buffer_pos + len) < item->buffer_size) {
163+
memcpy(item->buffer + item->buffer_pos, buffer, len);
164+
item->buffer_pos += len;
165+
} else {
166+
// Buffer is too small, write directly to file
167+
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
168+
uint64_t write_start_time = esp_timer_get_time();
169+
#endif
170+
size_t written = fwrite(buffer, 1, len, item->log_fp);
171+
if (written != len) {
172+
log_router_debug_printf("Failed to write directly to file, written: %zu, expected: %d\n", written, len);
173+
// Try to seek to beginning and retry
174+
if (fseek(item->log_fp, 0, SEEK_SET) == 0 && ftruncate(fileno(item->log_fp), 0) == 0) {
175+
log_router_debug_printf("File truncated, retrying direct write\n");
176+
written = fwrite(buffer, 1, len, item->log_fp);
177+
if (written != len) {
178+
log_router_debug_printf("Retry direct write also failed\n");
179+
return;
180+
}
181+
} else {
182+
log_router_debug_printf("Failed to seek/truncate file for direct write\n");
183+
return;
184+
}
185+
}
186+
fflush(item->log_fp);
187+
fsync(fileno(item->log_fp));
188+
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
189+
uint32_t write_duration_us = esp_timer_get_time() - write_start_time;
190+
log_router_debug_printf("Direct write took %ums, size: %d, file: %s\n", write_duration_us / 1000, len, item->file_path);
191+
#endif
192+
}
193+
194+
return;
195+
}
196+
96197
int esp_log_router_flash_vprintf(const char *format, va_list args)
97198
{
98199
int ret = 0;
@@ -124,8 +225,9 @@ int esp_log_router_flash_vprintf(const char *format, va_list args)
124225
esp_log_router_slist_t *item;
125226
int len = vsnprintf(vprintf_buffer, sizeof(vprintf_buffer), format, args);
126227
if (len > 0) {
228+
int trunc_b = 0;
127229
if (len > (sizeof(vprintf_buffer) - 1)) {
128-
int trunc_b = len - (sizeof(vprintf_buffer) - 1);
230+
trunc_b = len - (sizeof(vprintf_buffer) - 1);
129231
log_router_debug_printf("Buffer too small, lost %d bytes\n", trunc_b);
130232
len = sizeof(vprintf_buffer) - 1;
131233
}
@@ -161,88 +263,13 @@ int esp_log_router_flash_vprintf(const char *format, va_list args)
161263
bool tag_match = (item->tag == NULL) || (tag_len > 0 && strncmp(tag_start, item->tag, tag_len) == 0 && item->tag[tag_len] == '\0');
162264

163265
if (level_match && tag_match && item->log_fp) {
164-
bool should_flush = false;
165-
166-
// Check if we need to flush due to threshold
167-
if ((item->buffer_pos + len) >= item->flush_threshold) {
168-
should_flush = true;
169-
log_router_debug_printf("Flush buffer due to threshold\n");
170-
}
171-
172-
// Check if we need to flush due to timeout
173-
if ((now - item->last_flush_time) >= item->flush_timeout_ms) {
174-
should_flush = true;
175-
log_router_debug_printf("Flush buffer due to timeout\n");
176-
}
266+
router_fwrite(vprintf_buffer, 1, len, item, now);
177267

178-
// Flush buffer if needed
179-
if (should_flush) {
180-
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
181-
uint64_t flush_start_time = esp_timer_get_time();
182-
#endif
183-
esp_err_t flush_ret = esp_log_router_flush_buffer(item);
184-
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
185-
uint32_t flush_duration_us = esp_timer_get_time() - flush_start_time;
186-
log_router_debug_printf("Flush operation took %ums, file: %s\n", flush_duration_us / 1000, item->file_path);
187-
#endif
188-
if (flush_ret != ESP_OK) {
189-
log_router_debug_printf("Failed to flush buffer: %s\n", esp_err_to_name(flush_ret));
190-
// Flush failed, write directly to file
191-
size_t written = fwrite(vprintf_buffer, 1, len, item->log_fp);
192-
if (written != len) {
193-
log_router_debug_printf("Failed to write directly to file, written: %zu, expected: %d\n", written, len);
194-
// Try to seek to beginning and retry
195-
if (fseek(item->log_fp, 0, SEEK_SET) == 0 && ftruncate(fileno(item->log_fp), 0) == 0) {
196-
log_router_debug_printf("File truncated, retrying direct write\n");
197-
written = fwrite(vprintf_buffer, 1, len, item->log_fp);
198-
if (written != len) {
199-
log_router_debug_printf("Retry direct write also failed\n");
200-
continue;
201-
}
202-
} else {
203-
log_router_debug_printf("Failed to seek/truncate file for direct write\n");
204-
continue;
205-
}
206-
}
207-
fflush(item->log_fp);
208-
fsync(fileno(item->log_fp));
209-
continue; // Skip buffer write since we already wrote to file
210-
}
211-
// Flush successful, buffer is now empty, continue to buffer write
268+
#if (CONFIG_LOG_ROUTER_APPEND_STRING_ON_TRUNCATE)
269+
if (trunc_b > 0) {
270+
router_fwrite(TRUNCATE_MESSAGE, 1, strlen(TRUNCATE_MESSAGE), item, now);
212271
}
213-
214-
// Try to add to buffer, if still too large, write directly to file
215-
if ((item->buffer_pos + len) < item->buffer_size) {
216-
memcpy(item->buffer + item->buffer_pos, vprintf_buffer, len);
217-
item->buffer_pos += len;
218-
} else {
219-
// Buffer is too small, write directly to file
220-
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
221-
uint64_t write_start_time = esp_timer_get_time();
222-
#endif
223-
size_t written = fwrite(vprintf_buffer, 1, len, item->log_fp);
224-
if (written != len) {
225-
log_router_debug_printf("Failed to write directly to file, written: %zu, expected: %d\n", written, len);
226-
// Try to seek to beginning and retry
227-
if (fseek(item->log_fp, 0, SEEK_SET) == 0 && ftruncate(fileno(item->log_fp), 0) == 0) {
228-
log_router_debug_printf("File truncated, retrying direct write\n");
229-
written = fwrite(vprintf_buffer, 1, len, item->log_fp);
230-
if (written != len) {
231-
log_router_debug_printf("Retry direct write also failed\n");
232-
continue;
233-
}
234-
} else {
235-
log_router_debug_printf("Failed to seek/truncate file for direct write\n");
236-
continue;
237-
}
238-
}
239-
fflush(item->log_fp);
240-
fsync(fileno(item->log_fp));
241-
#ifdef CONFIG_LOG_ROUTER_DEBUG_OUTPUT
242-
uint32_t write_duration_us = esp_timer_get_time() - write_start_time;
243-
log_router_debug_printf("Direct write took %ums, size: %d, file: %s\n", write_duration_us / 1000, len, item->file_path);
244272
#endif
245-
}
246273
}
247274
}
248275
}

0 commit comments

Comments
 (0)