fix: skip formatting request timings that won't be logged

timeRequest built both 112-byte descriptions (with a String copy of the URL)
on async_tcp for every request, and logElapsed threw them away for anything
under 100ms.  Check worthLogging() first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
cjkas 2026-10-12 18:36:09 +02:00
parent 9988a2bec8
commit 6a3769406f
4 changed files with 28 additions and 3 deletions

View file

@ -26,10 +26,13 @@ class ElapsedTimer {
uint32_t us() const { return micros() - _start; } uint32_t us() const { return micros() - _start; }
}; };
// Whether logElapsed() would print a timing this long. Check it before
// building the description so the common, fast case costs nothing.
static inline bool worthLogging(uint32_t us) { return us / 1000 >= TIMING_LOG_MIN_MS; }
// Logs how long something took as milliseconds with microsecond precision. // Logs how long something took as milliseconds with microsecond precision.
static inline void logElapsed(const char *tag, const char *what, uint32_t us) { static inline void logElapsed(const char *tag, const char *what, uint32_t us) {
if(!worthLogging(us)) return;
const unsigned long ms = us / 1000, frac = us % 1000; const unsigned long ms = us / 1000, frac = us % 1000;
if(ms < TIMING_LOG_MIN_MS) return;
if(ms >= TIMING_SLOW_MS) ESP_LOGW(tag, "%s took %lu.%03lums (slow)", what, ms, frac); if(ms >= TIMING_SLOW_MS) ESP_LOGW(tag, "%s took %lu.%03lums (slow)", what, ms, frac);
else ESP_LOGI(tag, "%s took %lu.%03lums", what, ms, frac); else ESP_LOGI(tag, "%s took %lu.%03lums", what, ms, frac);
} }

View file

@ -1300,17 +1300,21 @@ static void timeRequest(AsyncWebServerRequest *request, ArMiddlewareNext next, c
// this runs, and replacing it would stop the reboot after an update. // this runs, and replacing it would stop the reboot after an update.
if(!request->multipart()) { if(!request->multipart()) {
request->onDisconnect([request, timer, server, ip]() { request->onDisconnect([request, timer, server, ip]() {
const uint32_t us = timer.us();
if(!worthLogging(us)) return;
char what[112]; char what[112];
snprintf(what, sizeof(what), "%s %s %s from %u.%u.%u.%u total", server, request->methodToString(), request->url().c_str(), snprintf(what, sizeof(what), "%s %s %s from %u.%u.%u.%u total", server, request->methodToString(), request->url().c_str(),
ip[0], ip[1], ip[2], ip[3]); ip[0], ip[1], ip[2], ip[3]);
logElapsed(TAG, what, timer.us()); logElapsed(TAG, what, us);
}); });
} }
next(); next();
const uint32_t us = timer.us();
if(!worthLogging(us)) return;
char what[112]; char what[112];
snprintf(what, sizeof(what), "%s %s %s from %u.%u.%u.%u handler", server, request->methodToString(), request->url().c_str(), snprintf(what, sizeof(what), "%s %s %s from %u.%u.%u.%u handler", server, request->methodToString(), request->url().c_str(),
ip[0], ip[1], ip[2], ip[3]); ip[0], ip[1], ip[2], ip[3]);
logElapsed(TAG, what, timer.us()); logElapsed(TAG, what, us);
} }
void Web::begin() { void Web::begin() {

View file

@ -16,6 +16,7 @@ typedef uint8_t byte;
// Fixed so anything formatted from it is deterministic. // Fixed so anything formatted from it is deterministic.
inline unsigned long millis() { return 0; } inline unsigned long millis() { return 0; }
inline unsigned long micros() { return 0; }
inline bool getLocalTime(struct tm *info, uint32_t ms = 5000) { inline bool getLocalTime(struct tm *info, uint32_t ms = 5000) {
(void)ms; (void)ms;
time_t now = time(nullptr); time_t now = time(nullptr);

View file

@ -1,5 +1,6 @@
#include <unity.h> #include <unity.h>
#include "LogLockPolicy.h" #include "LogLockPolicy.h"
#include "Timing.h"
void setUp() {} void setUp() {}
void tearDown() {} void tearDown() {}
@ -25,6 +26,20 @@ static void test_logLock_no_wait_allowed() {
TEST_ASSERT_FALSE(keepWaitingForLogLock(LogHolder::running, 0, 0)); TEST_ASSERT_FALSE(keepWaitingForLogLock(LogHolder::running, 0, 0));
} }
/*********************************************************************
* worthLogging
********************************************************************/
static void test_worthLogging_fast_timings_are_skipped() {
TEST_ASSERT_FALSE(worthLogging(0));
TEST_ASSERT_FALSE(worthLogging(999));
TEST_ASSERT_FALSE(worthLogging(TIMING_LOG_MIN_MS * 1000 - 1));
}
static void test_worthLogging_from_the_threshold() {
TEST_ASSERT_TRUE(worthLogging(TIMING_LOG_MIN_MS * 1000));
TEST_ASSERT_TRUE(worthLogging(TIMING_SLOW_MS * 1000));
TEST_ASSERT_TRUE(worthLogging(UINT32_MAX));
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
(void)argc; (void)argc;
(void)argv; (void)argv;
@ -34,5 +49,7 @@ int main(int argc, char **argv) {
RUN_TEST(test_logLock_waits_for_a_holder_preempted_here); RUN_TEST(test_logLock_waits_for_a_holder_preempted_here);
RUN_TEST(test_logLock_gives_up_on_a_holder_preempted_elsewhere); RUN_TEST(test_logLock_gives_up_on_a_holder_preempted_elsewhere);
RUN_TEST(test_logLock_no_wait_allowed); RUN_TEST(test_logLock_no_wait_allowed);
RUN_TEST(test_worthLogging_fast_timings_are_skipped);
RUN_TEST(test_worthLogging_from_the_threshold);
return UNITY_END(); return UNITY_END();
} }