upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.cpp')
-rw-r--r--src/util.cpp99
1 files changed, 90 insertions, 9 deletions
diff --git a/src/util.cpp b/src/util.cpp
index 0cd0133..b5b145f 100644
--- a/src/util.cpp
+++ b/src/util.cpp
@@ -47,6 +47,61 @@ struct LogDedupState {
47static std::map<std::string, LogDedupState> g_logDedup; 47static std::map<std::string, LogDedupState> g_logDedup;
48static CCriticalSection cs_logDedup; 48static CCriticalSection cs_logDedup;
49 49
50// A pending "(N similar)" summary, lifted out of the table while the lock is
51// held and written after it is released.
52struct LogDedupFlush {
53 std::string category;
54 int suppressed;
55 int64 elapsed; // how long the window that counted them ran
56 std::string lastMsg;
57};
58
59static int64 g_logDedupLastSweep = 0;
60
61static void EmitDedupSummary(const LogDedupFlush& f)
62{
63 OutputDebugStringF("[%s] (%d similar in %llds, last: %s)\n",
64 f.category.c_str(), f.suppressed,
65 (long long)f.elapsed, f.lastMsg.c_str());
66}
67
68// A window is otherwise only closed out when its own call site fires again, so
69// a burst that simply stops -- 100 ledger entries voided in one pass and then
70// silence -- leaves its count sitting in the table and the summary is never
71// written at all. That is how 99 of 100 voids went unreported, with no "(N
72// similar)" line to say anything was missing (issue #3). Sweep every expired
73// window instead, so a finished burst is summarised as soon as anything at all
74// is logged.
75//
76// Expired entries are erased rather than reopened: the window is over, so there
77// is no state left worth keeping, and the next call from that site then takes
78// the "first time" path and prints directly -- exactly what the rollover branch
79// below would have done. It also stops the table growing without bound.
80//
81// Caller holds cs_logDedup; the summaries are written after it is released.
82static void SweepExpiredWindowsLocked(int64 now, std::vector<LogDedupFlush>& out)
83{
84 for (std::map<std::string, LogDedupState>::iterator it = g_logDedup.begin();
85 it != g_logDedup.end(); )
86 {
87 if (now - it->second.windowStart < DEDUP_WINDOW_SECS)
88 {
89 ++it;
90 continue;
91 }
92 if (it->second.suppressed > 0)
93 {
94 LogDedupFlush f;
95 f.category = it->first.substr(0, it->first.find('|'));
96 f.suppressed = it->second.suppressed;
97 f.elapsed = now - it->second.windowStart;
98 f.lastMsg = it->second.lastMsg;
99 out.push_back(f);
100 }
101 g_logDedup.erase(it++);
102 }
103}
104
50void LogPrintDedup(const char* category, const char* file, int line, const std::string& msgIn) 105void LogPrintDedup(const char* category, const char* file, int line, const std::string& msgIn)
51{ 106{
52 // Call sites follow the existing printf() convention of ending their 107 // Call sites follow the existing printf() convention of ending their
@@ -61,11 +116,19 @@ void LogPrintDedup(const char* category, const char* file, int line, const std::
61 116
62 int64 now = GetTime(); 117 int64 now = GetTime();
63 bool fEmitNow = false; 118 bool fEmitNow = false;
64 int suppressedToFlush = 0; 119 std::vector<LogDedupFlush> flushes;
65 std::string flushedMsg;
66 120
67 CRITICAL_BLOCK(cs_logDedup) 121 CRITICAL_BLOCK(cs_logDedup)
68 { 122 {
123 // At most once a second. The table holds one entry per call site that
124 // has fired in the last few seconds, so the walk is short, but it must
125 // not run once per line of a burst.
126 if (now != g_logDedupLastSweep)
127 {
128 g_logDedupLastSweep = now;
129 SweepExpiredWindowsLocked(now, flushes);
130 }
131
69 std::map<std::string, LogDedupState>::iterator it = g_logDedup.find(key); 132 std::map<std::string, LogDedupState>::iterator it = g_logDedup.find(key);
70 if (it == g_logDedup.end()) 133 if (it == g_logDedup.end())
71 { 134 {
@@ -76,10 +139,18 @@ void LogPrintDedup(const char* category, const char* file, int line, const std::
76 } 139 }
77 else if (now - it->second.windowStart >= DEDUP_WINDOW_SECS) 140 else if (now - it->second.windowStart >= DEDUP_WINDOW_SECS)
78 { 141 {
79 // Window rolled over: flush what was suppressed during it (if 142 // Window rolled over within the same second as the last sweep:
80 // anything), then start a new window with this call printed. 143 // flush what was suppressed during it (if anything), then start a
81 suppressedToFlush = it->second.suppressed; 144 // new window with this call printed.
82 flushedMsg = it->second.lastMsg; 145 if (it->second.suppressed > 0)
146 {
147 LogDedupFlush f;
148 f.category = category;
149 f.suppressed = it->second.suppressed;
150 f.elapsed = now - it->second.windowStart;
151 f.lastMsg = it->second.lastMsg;
152 flushes.push_back(f);
153 }
83 it->second.windowStart = now; 154 it->second.windowStart = now;
84 it->second.suppressed = 0; 155 it->second.suppressed = 0;
85 it->second.lastMsg = msg; 156 it->second.lastMsg = msg;
@@ -94,13 +165,23 @@ void LogPrintDedup(const char* category, const char* file, int line, const std::
94 } 165 }
95 } 166 }
96 167
97 if (suppressedToFlush > 0) 168 for (size_t i = 0; i < flushes.size(); i++)
98 OutputDebugStringF("[%s] (%d similar in %llds, last: %s)\n", 169 EmitDedupSummary(flushes[i]);
99 category, suppressedToFlush, (long long)DEDUP_WINDOW_SECS, flushedMsg.c_str());
100 if (fEmitNow) 170 if (fEmitNow)
101 OutputDebugStringF("[%s] %s\n", category, msg.c_str()); 171 OutputDebugStringF("[%s] %s\n", category, msg.c_str());
102} 172}
103 173
174// LogPrintAlways() -- see the doc comment in util.h. No window, no counting,
175// no table entry: every call writes its own line.
176void LogPrintNoCollapse(const char* category, const std::string& msgIn)
177{
178 std::string msg = msgIn;
179 while (!msg.empty() && (msg.back() == '\n' || msg.back() == '\r'))
180 msg.pop_back();
181
182 OutputDebugStringF("[%s] %s\n", category, msg.c_str());
183}
184
104 185
105 186
106 187