Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-present The Bitcoin Core developers
3 : : // Distributed under the MIT software license, see the accompanying
4 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 : :
6 : : #include <logging.h>
7 : : #include <memusage.h>
8 : : #include <util/check.h>
9 : : #include <util/fs.h>
10 : : #include <util/string.h>
11 : : #include <util/threadnames.h>
12 : : #include <util/time.h>
13 : :
14 : : #include <array>
15 : : #include <cstring>
16 : : #include <map>
17 : : #include <optional>
18 : : #include <utility>
19 : :
20 : : using util::Join;
21 : : using util::RemovePrefixView;
22 : :
23 : : const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
24 : : constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
25 : :
26 : 944642 : BCLog::Logger& LogInstance()
27 : : {
28 : : /**
29 : : * NOTE: the logger instances is leaked on exit. This is ugly, but will be
30 : : * cleaned up by the OS/libc. Defining a logger as a global object doesn't work
31 : : * since the order of destruction of static/global objects is undefined.
32 : : * Consider if the logger gets destroyed, and then some later destructor calls
33 : : * LogInfo, maybe indirectly, and you get a core dump at shutdown trying to
34 : : * access the logger. When the shutdown sequence is fully audited and tested,
35 : : * explicit destruction of these objects can be implemented by changing this
36 : : * from a raw pointer to a std::unique_ptr.
37 : : * Since the ~Logger() destructor is never called, the Logger class and all
38 : : * its subclasses must have implicitly-defined destructors.
39 : : *
40 : : * This method of initialization was originally introduced in
41 : : * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
42 : : */
43 [ + + + - : 944642 : static BCLog::Logger* g_logger{new BCLog::Logger()};
+ - ]
44 : 944642 : return *g_logger;
45 : : }
46 : :
47 : : bool fLogIPs = DEFAULT_LOGIPS;
48 : :
49 : 315298 : static int FileWriteStr(std::string_view str, FILE *fp)
50 : : {
51 : 315298 : return fwrite(str.data(), 1, str.size(), fp);
52 : : }
53 : :
54 : 636 : bool BCLog::Logger::StartLogging()
55 : : {
56 : 636 : StdLockGuard scoped_lock(m_cs);
57 : :
58 [ - + ]: 636 : assert(m_buffering);
59 [ - + ]: 636 : assert(m_fileout == nullptr);
60 : :
61 [ + + ]: 636 : if (m_print_to_file) {
62 [ - + ]: 633 : assert(!m_file_path.empty());
63 [ + - ]: 633 : m_fileout = fsbridge::fopen(m_file_path, "a");
64 [ + - ]: 633 : if (!m_fileout) {
65 : : return false;
66 : : }
67 : :
68 : 633 : setbuf(m_fileout, nullptr); // unbuffered
69 : :
70 : : // Add newlines to the logfile to distinguish this execution from the
71 : : // last one.
72 [ + - ]: 633 : FileWriteStr("\n\n\n\n\n", m_fileout);
73 : : }
74 : :
75 : : // dump buffered messages from before we opened the log
76 : 636 : m_buffering = false;
77 [ - + ]: 636 : if (m_buffer_lines_discarded > 0) {
78 [ # # # # ]: 0 : LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), SourceLocation{__func__}, BCLog::ALL, Level::Info, /*should_ratelimit=*/false);
79 : : }
80 [ + + ]: 1905 : while (!m_msgs_before_open.empty()) {
81 [ - + ]: 1269 : const auto& buflog = m_msgs_before_open.front();
82 [ - + ]: 1269 : std::string s{buflog.str};
83 [ - + + - ]: 1269 : FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_loc, buflog.threadname, buflog.now, buflog.mocktime);
84 : 1269 : m_msgs_before_open.pop_front();
85 : :
86 [ + + - + : 1269 : if (m_print_to_file) FileWriteStr(s, m_fileout);
+ - ]
87 [ - + - - : 1269 : if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
- - ]
88 [ + + ]: 2538 : for (const auto& cb : m_print_callbacks) {
89 [ + - ]: 1269 : cb(s);
90 : : }
91 : 1269 : }
92 : 636 : m_cur_buffer_memusage = 0;
93 [ - + - - ]: 636 : if (m_print_to_console) fflush(stdout);
94 : :
95 : : return true;
96 : 636 : }
97 : :
98 : 635 : void BCLog::Logger::DisconnectTestLogger()
99 : : {
100 : 635 : StdLockGuard scoped_lock(m_cs);
101 : 635 : m_buffering = true;
102 [ + + + - ]: 635 : if (m_fileout != nullptr) fclose(m_fileout);
103 : 635 : m_fileout = nullptr;
104 : 635 : m_print_callbacks.clear();
105 : 635 : m_max_buffer_memusage = DEFAULT_MAX_LOG_BUFFER;
106 : 635 : m_cur_buffer_memusage = 0;
107 : 635 : m_buffer_lines_discarded = 0;
108 : 635 : m_msgs_before_open.clear();
109 : 635 : }
110 : :
111 : 0 : void BCLog::Logger::DisableLogging()
112 : : {
113 : 0 : {
114 : 0 : StdLockGuard scoped_lock(m_cs);
115 [ # # ]: 0 : assert(m_buffering);
116 [ # # ]: 0 : assert(m_print_callbacks.empty());
117 : 0 : }
118 : 0 : m_print_to_file = false;
119 : 0 : m_print_to_console = false;
120 : 0 : StartLogging();
121 : 0 : }
122 : :
123 : 644 : void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
124 : : {
125 : 644 : m_categories |= flag;
126 : 644 : }
127 : :
128 : 632 : bool BCLog::Logger::EnableCategory(std::string_view str)
129 : : {
130 : 632 : BCLog::LogFlags flag;
131 [ + - ]: 632 : if (!GetLogCategory(flag, str)) return false;
132 : 632 : EnableCategory(flag);
133 : 632 : return true;
134 : : }
135 : :
136 : 1278 : void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
137 : : {
138 : 1278 : m_categories &= ~flag;
139 : 1278 : }
140 : :
141 : 1264 : bool BCLog::Logger::DisableCategory(std::string_view str)
142 : : {
143 : 1264 : BCLog::LogFlags flag;
144 [ + - ]: 1264 : if (!GetLogCategory(flag, str)) return false;
145 : 1264 : DisableCategory(flag);
146 : 1264 : return true;
147 : : }
148 : :
149 : 342071 : bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
150 : : {
151 : 342071 : return (m_categories.load(std::memory_order_relaxed) & category) != 0;
152 : : }
153 : :
154 : 301481 : bool BCLog::Logger::WillLogCategoryLevel(BCLog::LogFlags category, BCLog::Level level) const
155 : : {
156 : : // Log messages at Info, Warning and Error level unconditionally, so that
157 : : // important troubleshooting information doesn't get lost.
158 [ + - ]: 301481 : if (level >= BCLog::Level::Info) return true;
159 : :
160 [ + + ]: 301481 : if (!WillLogCategory(category)) return false;
161 : :
162 : 293052 : StdLockGuard scoped_lock(m_cs);
163 : 293052 : const auto it{m_category_log_levels.find(category)};
164 [ + + ]: 293052 : return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
165 : 293052 : }
166 : :
167 : 1 : bool BCLog::Logger::DefaultShrinkDebugFile() const
168 : : {
169 : 1 : return m_categories == BCLog::NONE;
170 : : }
171 : :
172 : : static const std::map<std::string, BCLog::LogFlags, std::less<>> LOG_CATEGORIES_BY_STR{
173 : : {"net", BCLog::NET},
174 : : {"tor", BCLog::TOR},
175 : : {"mempool", BCLog::MEMPOOL},
176 : : {"http", BCLog::HTTP},
177 : : {"bench", BCLog::BENCH},
178 : : {"zmq", BCLog::ZMQ},
179 : : {"walletdb", BCLog::WALLETDB},
180 : : {"rpc", BCLog::RPC},
181 : : {"estimatefee", BCLog::ESTIMATEFEE},
182 : : {"addrman", BCLog::ADDRMAN},
183 : : {"selectcoins", BCLog::SELECTCOINS},
184 : : {"reindex", BCLog::REINDEX},
185 : : {"cmpctblock", BCLog::CMPCTBLOCK},
186 : : {"rand", BCLog::RAND},
187 : : {"prune", BCLog::PRUNE},
188 : : {"proxy", BCLog::PROXY},
189 : : {"mempoolrej", BCLog::MEMPOOLREJ},
190 : : {"libevent", BCLog::LIBEVENT},
191 : : {"coindb", BCLog::COINDB},
192 : : {"qt", BCLog::QT},
193 : : {"leveldb", BCLog::LEVELDB},
194 : : {"validation", BCLog::VALIDATION},
195 : : {"i2p", BCLog::I2P},
196 : : {"ipc", BCLog::IPC},
197 : : #ifdef DEBUG_LOCKCONTENTION
198 : : {"lock", BCLog::LOCK},
199 : : #endif
200 : : {"blockstorage", BCLog::BLOCKSTORAGE},
201 : : {"txreconciliation", BCLog::TXRECONCILIATION},
202 : : {"scan", BCLog::SCAN},
203 : : {"txpackages", BCLog::TXPACKAGES},
204 : : {"kernel", BCLog::KERNEL},
205 : : {"privatebroadcast", BCLog::PRIVBROADCAST},
206 : : };
207 : :
208 : : static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
209 : : // Swap keys and values from LOG_CATEGORIES_BY_STR.
210 : 147 : [](const auto& in) {
211 : 147 : std::unordered_map<BCLog::LogFlags, std::string> out;
212 [ + - + + ]: 4557 : for (const auto& [k, v] : in) {
213 : 4410 : const bool inserted{out.emplace(v, k).second};
214 [ - + ]: 4410 : assert(inserted);
215 : : }
216 : 147 : return out;
217 : 0 : }(LOG_CATEGORIES_BY_STR)
218 : : };
219 : :
220 : 1930 : bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str)
221 : : {
222 [ + + + - : 1930 : if (str.empty() || str == "1" || str == "all") {
- + ]
223 : 632 : flag = BCLog::ALL;
224 : 632 : return true;
225 : : }
226 : 1298 : auto it = LOG_CATEGORIES_BY_STR.find(str);
227 [ + - ]: 1298 : if (it != LOG_CATEGORIES_BY_STR.end()) {
228 : 1298 : flag = it->second;
229 : 1298 : return true;
230 : : }
231 : : return false;
232 : : }
233 : :
234 : 3800 : std::string BCLog::Logger::LogLevelToStr(BCLog::Level level)
235 : : {
236 [ + + + + : 3800 : switch (level) {
+ - ]
237 : 1626 : case BCLog::Level::Trace:
238 : 1626 : return "trace";
239 : 1276 : case BCLog::Level::Debug:
240 : 1276 : return "debug";
241 : 688 : case BCLog::Level::Info:
242 : 688 : return "info";
243 : 46 : case BCLog::Level::Warning:
244 : 46 : return "warning";
245 : 164 : case BCLog::Level::Error:
246 : 164 : return "error";
247 : : }
248 : 0 : assert(false);
249 : : }
250 : :
251 : 291383 : static std::string LogCategoryToStr(BCLog::LogFlags category)
252 : : {
253 [ + + ]: 291383 : if (category == BCLog::ALL) {
254 : 60 : return "all";
255 : : }
256 : 291323 : auto it = LOG_CATEGORIES_BY_FLAG.find(category);
257 [ - + ]: 291323 : assert(it != LOG_CATEGORIES_BY_FLAG.end());
258 [ - + ]: 291323 : return it->second;
259 : : }
260 : :
261 : 638 : static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str)
262 : : {
263 [ + + ]: 638 : if (level_str == "trace") {
264 : 634 : return BCLog::Level::Trace;
265 [ + + ]: 4 : } else if (level_str == "debug") {
266 : 2 : return BCLog::Level::Debug;
267 [ + - ]: 2 : } else if (level_str == "info") {
268 : 2 : return BCLog::Level::Info;
269 [ # # ]: 0 : } else if (level_str == "warning") {
270 : 0 : return BCLog::Level::Warning;
271 [ # # ]: 0 : } else if (level_str == "error") {
272 : 0 : return BCLog::Level::Error;
273 : : } else {
274 : 0 : return std::nullopt;
275 : : }
276 : : }
277 : :
278 : 1353 : std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
279 : : {
280 : 1353 : std::vector<LogCategory> ret;
281 [ + - ]: 1353 : ret.reserve(LOG_CATEGORIES_BY_STR.size());
282 [ - + + + ]: 41943 : for (const auto& [category, flag] : LOG_CATEGORIES_BY_STR) {
283 [ - + + - : 81180 : ret.push_back(LogCategory{.category = category, .active = WillLogCategory(flag)});
+ - ]
284 : : }
285 : 1353 : return ret;
286 : 0 : }
287 : :
288 : : /** Log severity levels that can be selected by the user. */
289 : : static constexpr std::array<BCLog::Level, 3> LogLevelsList()
290 : : {
291 : : return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace};
292 : : }
293 : :
294 : 632 : std::string BCLog::Logger::LogLevelsString() const
295 : : {
296 : 632 : const auto& levels = LogLevelsList();
297 [ + - + - ]: 3160 : return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [](BCLog::Level level) { return LogLevelToStr(level); });
298 : : }
299 : :
300 : 314736 : std::string BCLog::Logger::LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
301 : : {
302 [ + + ]: 314736 : std::string strStamped;
303 : :
304 [ + + ]: 314736 : if (!m_log_timestamps)
305 : : return strStamped;
306 : :
307 : 314646 : const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
308 [ + - ]: 314646 : strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
309 [ + - + - ]: 314646 : if (m_log_time_micros && !strStamped.empty()) {
310 : 314646 : strStamped.pop_back();
311 [ + - ]: 629292 : strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
312 : : }
313 [ + + ]: 314646 : if (mocktime > 0s) {
314 [ + - + - : 613242 : strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
- + ]
315 : : }
316 [ + - ]: 629382 : strStamped += ' ';
317 : :
318 : : return strStamped;
319 : 0 : }
320 : :
321 : : namespace BCLog {
322 : : /** Belts and suspenders: make sure outgoing log messages don't contain
323 : : * potentially suspicious characters, such as terminal control codes.
324 : : *
325 : : * This escapes control characters except newline ('\n') in C syntax.
326 : : * It escapes instead of removes them to still allow for troubleshooting
327 : : * issues where they accidentally end up in strings.
328 : : */
329 : 315311 : std::string LogEscapeMessage(std::string_view str) {
330 : 315311 : std::string ret;
331 [ + + ]: 31226411 : for (char ch_in : str) {
332 : 30911100 : uint8_t ch = (uint8_t)ch_in;
333 [ + + + + ]: 30911100 : if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
334 [ + - ]: 61822196 : ret += ch_in;
335 : : } else {
336 [ + - ]: 8 : ret += strprintf("\\x%02x", ch);
337 : : }
338 : : }
339 : 315311 : return ret;
340 : 0 : }
341 : : } // namespace BCLog
342 : :
343 : 314736 : std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level level) const
344 : : {
345 [ + + ]: 314736 : if (category == LogFlags::NONE) category = LogFlags::ALL;
346 : :
347 [ + + + + ]: 314736 : const bool has_category{m_always_print_category_level || category != LogFlags::ALL};
348 : :
349 : : // If there is no category, Info is implied
350 [ + + ]: 314736 : if (!has_category && level == Level::Info) return {};
351 : :
352 : 291590 : std::string s{"["};
353 [ + + ]: 291590 : if (has_category) {
354 [ + - ]: 582766 : s += LogCategoryToStr(category);
355 : : }
356 : :
357 [ + + + + ]: 291590 : if (m_always_print_category_level || !has_category || level != Level::Debug) {
358 : : // If there is a category, Debug is implied, so don't add the level
359 : :
360 : : // Only add separator if we have a category
361 [ + + + - ]: 1272 : if (has_category) s += ":";
362 [ + - ]: 2544 : s += Logger::LogLevelToStr(level);
363 : : }
364 : :
365 [ + - ]: 291590 : s += "] ";
366 : 291590 : return s;
367 : 291590 : }
368 : :
369 : 1840 : static size_t MemUsage(const BCLog::Logger::BufferedLog& buflog)
370 : : {
371 : 1840 : return memusage::DynamicUsage(buflog.str) +
372 : 1840 : memusage::DynamicUsage(buflog.threadname) +
373 : 1840 : memusage::MallocUsage(sizeof(memusage::list_node<BCLog::Logger::BufferedLog>));
374 : : }
375 : :
376 : 3 : BCLog::LogRateLimiter::LogRateLimiter(uint64_t max_bytes, std::chrono::seconds reset_window)
377 : 3 : : m_max_bytes{max_bytes}, m_reset_window{reset_window} {}
378 : :
379 : 3 : std::shared_ptr<BCLog::LogRateLimiter> BCLog::LogRateLimiter::Create(
380 : : SchedulerFunction&& scheduler_func, uint64_t max_bytes, std::chrono::seconds reset_window)
381 : : {
382 [ + - ]: 3 : auto limiter{std::shared_ptr<LogRateLimiter>(new LogRateLimiter(max_bytes, reset_window))};
383 [ + - ]: 3 : std::weak_ptr<LogRateLimiter> weak_limiter{limiter};
384 [ + - + - : 60 : auto reset = [weak_limiter] {
+ - + - -
- ]
385 [ + - + - ]: 2 : if (auto shared_limiter{weak_limiter.lock()}) shared_limiter->Reset();
386 : 2 : };
387 [ + - + - ]: 3 : scheduler_func(reset, limiter->m_reset_window);
388 [ + - ]: 3 : return limiter;
389 [ + - ]: 6 : }
390 : :
391 : 93 : BCLog::LogRateLimiter::Status BCLog::LogRateLimiter::Consume(
392 : : const SourceLocation& source_loc,
393 : : const std::string& str)
394 : : {
395 : 93 : StdLockGuard scoped_lock(m_mutex);
396 [ + - ]: 93 : auto& stats{m_source_locations.try_emplace(source_loc, m_max_bytes).first->second};
397 [ + + ]: 93 : Status status{stats.m_dropped_bytes > 0 ? Status::STILL_SUPPRESSED : Status::UNSUPPRESSED};
398 : :
399 [ - + + - : 93 : if (!stats.Consume(str.size()) && status == Status::UNSUPPRESSED) {
+ + + + ]
400 : 3 : status = Status::NEWLY_SUPPRESSED;
401 : 3 : m_suppression_active = true;
402 : : }
403 : :
404 : 93 : return status;
405 : 93 : }
406 : :
407 : 314736 : void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags category, BCLog::Level level, const SourceLocation& source_loc, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
408 : : {
409 [ - + ]: 320037 : if (!str.ends_with('\n')) str.push_back('\n');
410 : :
411 [ + - ]: 314736 : str.insert(0, GetLogPrefix(category, level));
412 : :
413 [ + + ]: 314736 : if (m_log_sourcelocations) {
414 [ + - + - ]: 943749 : str.insert(0, strprintf("[%s:%d] [%s] ", RemovePrefixView(source_loc.file_name(), "./"), source_loc.line(), source_loc.function_name_short()));
415 : : }
416 : :
417 [ + + ]: 314736 : if (m_log_threadnames) {
418 [ + + + - ]: 629152 : str.insert(0, strprintf("[%s] ", (threadname.empty() ? "unknown" : threadname)));
419 : : }
420 : :
421 [ + - ]: 314736 : str.insert(0, LogTimestampStr(now, mocktime));
422 : 314736 : }
423 : :
424 : 315306 : void BCLog::Logger::LogPrintStr(std::string_view str, SourceLocation&& source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit)
425 : : {
426 : 315306 : StdLockGuard scoped_lock(m_cs);
427 [ + - ]: 315306 : return LogPrintStr_(str, std::move(source_loc), category, level, should_ratelimit);
428 : 315306 : }
429 : :
430 : : // NOLINTNEXTLINE(misc-no-recursion)
431 : 315307 : void BCLog::Logger::LogPrintStr_(std::string_view str, SourceLocation&& source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit)
432 : : {
433 : 315307 : std::string str_prefixed = LogEscapeMessage(str);
434 : :
435 [ + + ]: 315307 : if (m_buffering) {
436 : 1840 : {
437 : 1840 : BufferedLog buf{
438 : 1840 : .now = SystemClock::now(),
439 [ + - ]: 1840 : .mocktime = GetMockTime(),
440 : : .str = str_prefixed,
441 : : .threadname = util::ThreadGetInternalName(),
442 : : .source_loc = std::move(source_loc),
443 : : .category = category,
444 : : .level = level,
445 [ + - ]: 1840 : };
446 : 1840 : m_cur_buffer_memusage += MemUsage(buf);
447 [ + - ]: 1840 : m_msgs_before_open.push_back(std::move(buf));
448 : 0 : }
449 : :
450 [ - + ]: 1840 : while (m_cur_buffer_memusage > m_max_buffer_memusage) {
451 [ # # ]: 0 : if (m_msgs_before_open.empty()) {
452 : 0 : m_cur_buffer_memusage = 0;
453 : 0 : break;
454 : : }
455 : 0 : m_cur_buffer_memusage -= MemUsage(m_msgs_before_open.front());
456 : 0 : m_msgs_before_open.pop_front();
457 : 0 : ++m_buffer_lines_discarded;
458 : : }
459 : :
460 : 1840 : return;
461 : : }
462 : :
463 [ + - + - : 626934 : FormatLogStrInPlace(str_prefixed, category, level, source_loc, util::ThreadGetInternalName(), SystemClock::now(), GetMockTime());
+ - ]
464 : 313467 : bool ratelimit{false};
465 [ + + + + ]: 313467 : if (should_ratelimit && m_limiter) {
466 [ + - ]: 85 : auto status{m_limiter->Consume(source_loc, str_prefixed)};
467 [ + + ]: 85 : if (status == LogRateLimiter::Status::NEWLY_SUPPRESSED) {
468 : : // NOLINTNEXTLINE(misc-no-recursion)
469 [ + - ]: 2 : LogPrintStr_(strprintf(
470 : : "Excessive logging detected from %s:%d (%s): >%d bytes logged during "
471 : : "the last time window of %is. Suppressing logging to disk from this "
472 : : "source location until time window resets. Console logging "
473 : : "unaffected. Last log entry.",
474 [ + - + - ]: 2 : source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
475 [ + - ]: 1 : m_limiter->m_max_bytes,
476 [ + - ]: 1 : Ticks<std::chrono::seconds>(m_limiter->m_reset_window)),
477 [ + - ]: 1 : SourceLocation{__func__}, LogFlags::ALL, Level::Warning, /*should_ratelimit=*/false); // with should_ratelimit=false, this cannot lead to infinite recursion
478 [ + + ]: 84 : } else if (status == LogRateLimiter::Status::STILL_SUPPRESSED) {
479 : 1 : ratelimit = true;
480 : : }
481 : : }
482 : :
483 : : // To avoid confusion caused by dropped log messages when debugging an issue,
484 : : // we prefix log lines with "[*]" when there are any suppressed source locations.
485 [ + + + + ]: 313467 : if (m_limiter && m_limiter->SuppressionsActive()) {
486 [ + - ]: 4 : str_prefixed.insert(0, "[*] ");
487 : : }
488 : :
489 [ - + ]: 313467 : if (m_print_to_console) {
490 : : // print to console
491 [ # # # # ]: 0 : fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
492 [ # # ]: 0 : fflush(stdout);
493 : : }
494 [ + + ]: 627807 : for (const auto& cb : m_print_callbacks) {
495 [ + - ]: 314340 : cb(str_prefixed);
496 : : }
497 [ + + + + ]: 313467 : if (m_print_to_file && !ratelimit) {
498 [ - + ]: 313401 : assert(m_fileout != nullptr);
499 : :
500 : : // reopen the log file, if requested
501 [ + + ]: 313401 : if (m_reopen_file) {
502 [ + - ]: 6 : m_reopen_file = false;
503 [ + - ]: 6 : FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
504 [ + - ]: 6 : if (new_fileout) {
505 : 6 : setbuf(new_fileout, nullptr); // unbuffered
506 [ + - ]: 6 : fclose(m_fileout);
507 : 6 : m_fileout = new_fileout;
508 : : }
509 : : }
510 [ - + + - ]: 313401 : FileWriteStr(str_prefixed, m_fileout);
511 : : }
512 : 315307 : }
513 : :
514 : 0 : void BCLog::Logger::ShrinkDebugFile()
515 : : {
516 : : // Amount of debug.log to save at end when shrinking (must fit in memory)
517 : 0 : constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
518 : :
519 [ # # ]: 0 : assert(!m_file_path.empty());
520 : :
521 : : // Scroll debug.log if it's getting too big
522 : 0 : FILE* file = fsbridge::fopen(m_file_path, "r");
523 : :
524 : : // Special files (e.g. device nodes) may not have a size.
525 : 0 : size_t log_size = 0;
526 : 0 : try {
527 [ # # ]: 0 : log_size = fs::file_size(m_file_path);
528 [ - - ]: 0 : } catch (const fs::filesystem_error&) {}
529 : :
530 : : // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
531 : : // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
532 [ # # ]: 0 : if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
533 : : {
534 : : // Restart the file with some of the end
535 : 0 : std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
536 [ # # # # ]: 0 : if (fseek(file, -((long)vch.size()), SEEK_END)) {
537 [ # # ]: 0 : LogWarning("Failed to shrink debug log file: fseek(...) failed");
538 [ # # ]: 0 : fclose(file);
539 : 0 : return;
540 : : }
541 [ # # # # ]: 0 : int nBytes = fread(vch.data(), 1, vch.size(), file);
542 [ # # ]: 0 : fclose(file);
543 : :
544 [ # # ]: 0 : file = fsbridge::fopen(m_file_path, "w");
545 [ # # ]: 0 : if (file)
546 : : {
547 [ # # ]: 0 : fwrite(vch.data(), 1, nBytes, file);
548 [ # # ]: 0 : fclose(file);
549 : : }
550 : 0 : }
551 [ # # ]: 0 : else if (file != nullptr)
552 : 0 : fclose(file);
553 : : }
554 : :
555 : 3 : void BCLog::LogRateLimiter::Reset()
556 : : {
557 [ + - ]: 3 : decltype(m_source_locations) source_locations;
558 : 3 : {
559 [ + - ]: 3 : StdLockGuard scoped_lock(m_mutex);
560 : 3 : source_locations.swap(m_source_locations);
561 : 3 : m_suppression_active = false;
562 : 3 : }
563 [ + + + + ]: 7 : for (const auto& [source_loc, stats] : source_locations) {
564 [ + + ]: 4 : if (stats.m_dropped_bytes == 0) continue;
565 [ + - + - ]: 6 : LogPrintLevel_(
566 : : LogFlags::ALL, Level::Warning, /*should_ratelimit=*/false,
567 : : "Restarting logging from %s:%d (%s): %d bytes were dropped during the last %ss.",
568 : : source_loc.file_name(), source_loc.line(), source_loc.function_name_short(),
569 : : stats.m_dropped_bytes, Ticks<std::chrono::seconds>(m_reset_window));
570 : : }
571 : 3 : }
572 : :
573 : 96 : bool BCLog::LogRateLimiter::Stats::Consume(uint64_t bytes)
574 : : {
575 [ + + ]: 96 : if (bytes > m_available_bytes) {
576 : 6 : m_dropped_bytes += bytes;
577 : 6 : m_available_bytes = 0;
578 : 6 : return false;
579 : : }
580 : :
581 : 90 : m_available_bytes -= bytes;
582 : 90 : return true;
583 : : }
584 : :
585 : 634 : bool BCLog::Logger::SetLogLevel(std::string_view level_str)
586 : : {
587 : 634 : const auto level = GetLogLevel(level_str);
588 [ + - + - ]: 634 : if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
589 : 634 : m_log_level = level.value();
590 : 634 : return true;
591 : : }
592 : :
593 : 4 : bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)
594 : : {
595 : 4 : BCLog::LogFlags flag;
596 [ + - ]: 4 : if (!GetLogCategory(flag, category_str)) return false;
597 : :
598 : 4 : const auto level = GetLogLevel(level_str);
599 [ + - + - ]: 4 : if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
600 : :
601 : 4 : StdLockGuard scoped_lock(m_cs);
602 [ + - ]: 4 : m_category_log_levels[flag] = level.value();
603 : 4 : return true;
604 : 4 : }
|