diff options
Diffstat (limited to 'src/util.cpp')
| -rw-r--r-- | src/util.cpp | 108 |
1 files changed, 103 insertions, 5 deletions
diff --git a/src/util.cpp b/src/util.cpp index 7f56530..68bd18e 100644 --- a/src/util.cpp +++ b/src/util.cpp | |||
| @@ -8,6 +8,99 @@ | |||
| 8 | 8 | ||
| 9 | bool fDebug = false; | 9 | bool fDebug = false; |
| 10 | 10 | ||
| 11 | // --------------------------------------------------------------------------- | ||
| 12 | // Categorized, burst-collapsing logging -- see the LogPrint doc comment in | ||
| 13 | // util.h for the problem this solves. | ||
| 14 | // --------------------------------------------------------------------------- | ||
| 15 | |||
| 16 | // Categories that explain the pool/worker/payout pipeline are on unconditionally: | ||
| 17 | // silence there is exactly the "worker stuck, nothing then happens" complaint | ||
| 18 | // this exists to fix, so it can't be opted out of by forgetting -debug. | ||
| 19 | // "net" and "nostr" cover the per-address connection/discovery chatter that | ||
| 20 | // used to dominate debug.log; they're gated behind fDebug like the printf() | ||
| 21 | // calls they replace, so default output stays quiet unless requested. | ||
| 22 | static bool LogCategoryDefaultOn(const char* category) | ||
| 23 | { | ||
| 24 | return strcmp(category, "pool") == 0 || | ||
| 25 | strcmp(category, "worker") == 0 || | ||
| 26 | strcmp(category, "payout") == 0; | ||
| 27 | } | ||
| 28 | |||
| 29 | bool LogAcceptsCategory(const char* category) | ||
| 30 | { | ||
| 31 | if (LogCategoryDefaultOn(category)) | ||
| 32 | return true; | ||
| 33 | return fDebug; | ||
| 34 | } | ||
| 35 | |||
| 36 | // Only collapse bursts from the exact same call site within this window; | ||
| 37 | // a different LogPrint() call (different file/line) is never merged with | ||
| 38 | // this one, so unrelated messages never get mixed into one summary. | ||
| 39 | static const int64 DEDUP_WINDOW_SECS = 3; | ||
| 40 | |||
| 41 | struct LogDedupState { | ||
| 42 | int64 windowStart; // GetTime() when the current window opened | ||
| 43 | int suppressed; // calls swallowed so far this window | ||
| 44 | std::string lastMsg; // most recent message text, for the summary line | ||
| 45 | }; | ||
| 46 | |||
| 47 | static std::map<std::string, LogDedupState> g_logDedup; | ||
| 48 | static CCriticalSection cs_logDedup; | ||
| 49 | |||
| 50 | void LogPrintDedup(const char* category, const char* file, int line, const std::string& msgIn) | ||
| 51 | { | ||
| 52 | // Call sites follow the existing printf() convention of ending their | ||
| 53 | // format string with \n; strip it here so the single \n this function | ||
| 54 | // adds below doesn't turn into a blank line. | ||
| 55 | std::string msg = msgIn; | ||
| 56 | while (!msg.empty() && (msg.back() == '\n' || msg.back() == '\r')) | ||
| 57 | msg.pop_back(); | ||
| 58 | |||
| 59 | char key[512]; | ||
| 60 | my_snprintf(key, sizeof(key), "%s|%s:%d", category, file, line); | ||
| 61 | |||
| 62 | int64 now = GetTime(); | ||
| 63 | bool fEmitNow = false; | ||
| 64 | int suppressedToFlush = 0; | ||
| 65 | std::string flushedMsg; | ||
| 66 | |||
| 67 | CRITICAL_BLOCK(cs_logDedup) | ||
| 68 | { | ||
| 69 | std::map<std::string, LogDedupState>::iterator it = g_logDedup.find(key); | ||
| 70 | if (it == g_logDedup.end()) | ||
| 71 | { | ||
| 72 | // First time this call site has fired (or its last window is long | ||
| 73 | // gone) -- print it directly and open a fresh window. | ||
| 74 | g_logDedup[key] = LogDedupState{now, 0, msg}; | ||
| 75 | fEmitNow = true; | ||
| 76 | } | ||
| 77 | else if (now - it->second.windowStart >= DEDUP_WINDOW_SECS) | ||
| 78 | { | ||
| 79 | // Window rolled over: flush what was suppressed during it (if | ||
| 80 | // anything), then start a new window with this call printed. | ||
| 81 | suppressedToFlush = it->second.suppressed; | ||
| 82 | flushedMsg = it->second.lastMsg; | ||
| 83 | it->second.windowStart = now; | ||
| 84 | it->second.suppressed = 0; | ||
| 85 | it->second.lastMsg = msg; | ||
| 86 | fEmitNow = true; | ||
| 87 | } | ||
| 88 | else | ||
| 89 | { | ||
| 90 | // Still inside the window and already printed one line for it: | ||
| 91 | // count this occurrence instead of writing another near-duplicate. | ||
| 92 | it->second.suppressed++; | ||
| 93 | it->second.lastMsg = msg; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | if (suppressedToFlush > 0) | ||
| 98 | OutputDebugStringF("[%s] (%d similar in %llds, last: %s)\n", | ||
| 99 | category, suppressedToFlush, (long long)DEDUP_WINDOW_SECS, flushedMsg.c_str()); | ||
| 100 | if (fEmitNow) | ||
| 101 | OutputDebugStringF("[%s] %s\n", category, msg.c_str()); | ||
| 102 | } | ||
| 103 | |||
| 11 | 104 | ||
| 12 | 105 | ||
| 13 | 106 | ||
| @@ -200,11 +293,16 @@ void PrintException(std::exception* pex, const char* pszThread) | |||
| 200 | snprintf(pszMessage, sizeof(pszMessage), | 293 | snprintf(pszMessage, sizeof(pszMessage), |
| 201 | "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); | 294 | "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); |
| 202 | printf("\n\n************************\n%s", pszMessage); | 295 | printf("\n\n************************\n%s", pszMessage); |
| 203 | // Suppress the dialog and rethrow during shutdown — threads unwinding on | 296 | // Do NOT rethrow here. Every caller of PrintException (via |
| 204 | // exit cause spurious "UNKNOWN EXCEPTION" popups that aren't real errors. | 297 | // CATCH_PRINT_EXCEPTION) is a thread wrapped in its own |
| 205 | // Show error in GUI via repaint signal — GUI polls g_errorMessage | 298 | // "try { ThreadFoo2() } catch(...) { log, sleep, loop again }" self-healing |
| 206 | if (!fShutdown) | 299 | // retry loop -- that's the whole point of the wrapper. Rethrowing from |
| 207 | throw; | 300 | // inside that catch block escapes the retry loop entirely (nothing above |
| 301 | // it in the thread catches it) and takes down the whole process with | ||
| 302 | // std::terminate() on the very first transient error -- a flaky relay | ||
| 303 | // connection, a malformed message, anything -- instead of letting the | ||
| 304 | // thread log it, sleep 5s, and try again like it's designed to. | ||
| 305 | // Show error in GUI via repaint signal -- GUI polls g_errorMessage | ||
| 208 | } | 306 | } |
| 209 | 307 | ||
| 210 | 308 | ||