Branch data Line data Source code
1 : : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : : // Copyright (c) 2009-2022 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 <util/fs.h>
8 : : #include <util/string.h>
9 : : #include <util/threadnames.h>
10 : : #include <util/time.h>
11 : :
12 : : #include <algorithm>
13 : : #include <array>
14 : : #include <mutex>
15 : : #include <optional>
16 : :
17 : : const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
18 : : constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
19 : :
20 : 9314 : BCLog::Logger& LogInstance()
21 : : {
22 : : /**
23 : : * NOTE: the logger instances is leaked on exit. This is ugly, but will be
24 : : * cleaned up by the OS/libc. Defining a logger as a global object doesn't work
25 : : * since the order of destruction of static/global objects is undefined.
26 : : * Consider if the logger gets destroyed, and then some later destructor calls
27 : : * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to
28 : : * access the logger. When the shutdown sequence is fully audited and tested,
29 : : * explicit destruction of these objects can be implemented by changing this
30 : : * from a raw pointer to a std::unique_ptr.
31 : : * Since the ~Logger() destructor is never called, the Logger class and all
32 : : * its subclasses must have implicitly-defined destructors.
33 : : *
34 : : * This method of initialization was originally introduced in
35 : : * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
36 : : */
37 [ + + ][ - + ]: 9314 : static BCLog::Logger* g_logger{new BCLog::Logger()};
[ + - ]
38 : 9314 : return *g_logger;
39 : 0 : }
40 : :
41 : : bool fLogIPs = DEFAULT_LOGIPS;
42 : :
43 : 0 : static int FileWriteStr(const std::string &str, FILE *fp)
44 : : {
45 : 0 : return fwrite(str.data(), 1, str.size(), fp);
46 : : }
47 : :
48 : 1 : bool BCLog::Logger::StartLogging()
49 : : {
50 : 1 : StdLockGuard scoped_lock(m_cs);
51 : :
52 [ + - ]: 1 : assert(m_buffering);
53 [ + - ]: 1 : assert(m_fileout == nullptr);
54 : :
55 [ + - ]: 1 : if (m_print_to_file) {
56 [ # # ]: 0 : assert(!m_file_path.empty());
57 [ # # ]: 0 : m_fileout = fsbridge::fopen(m_file_path, "a");
58 [ # # ]: 0 : if (!m_fileout) {
59 : 0 : return false;
60 : : }
61 : :
62 : 0 : setbuf(m_fileout, nullptr); // unbuffered
63 : :
64 : : // Add newlines to the logfile to distinguish this execution from the
65 : : // last one.
66 [ # # ][ # # ]: 0 : FileWriteStr("\n\n\n\n\n", m_fileout);
67 : 0 : }
68 : :
69 : : // dump buffered messages from before we opened the log
70 : 1 : m_buffering = false;
71 [ + + ]: 3 : while (!m_msgs_before_open.empty()) {
72 : 2 : const std::string& s = m_msgs_before_open.front();
73 : :
74 [ - + ][ # # ]: 2 : if (m_print_to_file) FileWriteStr(s, m_fileout);
75 [ - + ][ # # ]: 2 : if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
76 [ - + ]: 2 : for (const auto& cb : m_print_callbacks) {
77 [ # # ]: 0 : cb(s);
78 : : }
79 : :
80 : 2 : m_msgs_before_open.pop_front();
81 : : }
82 [ - + ][ # # ]: 1 : if (m_print_to_console) fflush(stdout);
83 : :
84 : 1 : return true;
85 : 1 : }
86 : :
87 : 1 : void BCLog::Logger::DisconnectTestLogger()
88 : : {
89 : 1 : StdLockGuard scoped_lock(m_cs);
90 : 1 : m_buffering = true;
91 [ + - ][ # # ]: 1 : if (m_fileout != nullptr) fclose(m_fileout);
92 : 1 : m_fileout = nullptr;
93 : 1 : m_print_callbacks.clear();
94 : 1 : }
95 : :
96 : 0 : void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
97 : : {
98 : 0 : m_categories |= flag;
99 : 0 : }
100 : :
101 : 0 : bool BCLog::Logger::EnableCategory(const std::string& str)
102 : : {
103 : : BCLog::LogFlags flag;
104 [ # # ]: 0 : if (!GetLogCategory(flag, str)) return false;
105 : 0 : EnableCategory(flag);
106 : 0 : return true;
107 : 0 : }
108 : :
109 : 2 : void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
110 : : {
111 : 2 : m_categories &= ~flag;
112 : 2 : }
113 : :
114 : 2 : bool BCLog::Logger::DisableCategory(const std::string& str)
115 : : {
116 : : BCLog::LogFlags flag;
117 [ - + ]: 2 : if (!GetLogCategory(flag, str)) return false;
118 : 2 : DisableCategory(flag);
119 : 2 : return true;
120 : 2 : }
121 : :
122 : 8954 : bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
123 : : {
124 : 8954 : return (m_categories.load(std::memory_order_relaxed) & category) != 0;
125 : : }
126 : :
127 : 8837 : bool BCLog::Logger::WillLogCategoryLevel(BCLog::LogFlags category, BCLog::Level level) const
128 : : {
129 : : // Log messages at Warning and Error level unconditionally, so that
130 : : // important troubleshooting information doesn't get lost.
131 [ - + ]: 8837 : if (level >= BCLog::Level::Warning) return true;
132 : :
133 [ - + ]: 8837 : if (!WillLogCategory(category)) return false;
134 : :
135 : 0 : StdLockGuard scoped_lock(m_cs);
136 [ # # ]: 0 : const auto it{m_category_log_levels.find(category)};
137 [ # # ][ # # ]: 0 : return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
138 : 8837 : }
139 : :
140 : 0 : bool BCLog::Logger::DefaultShrinkDebugFile() const
141 : : {
142 : 0 : return m_categories == BCLog::NONE;
143 : : }
144 : :
145 : 0 : struct CLogCategoryDesc {
146 : : BCLog::LogFlags flag;
147 : : std::string category;
148 : : };
149 : :
150 : 0 : const CLogCategoryDesc LogCategories[] =
151 [ # # ]: 2 : {
152 [ + - ]: 2 : {BCLog::NONE, "0"},
153 [ + - ]: 2 : {BCLog::NONE, ""},
154 [ + - ]: 2 : {BCLog::NET, "net"},
155 [ + - ]: 2 : {BCLog::TOR, "tor"},
156 [ + - ]: 2 : {BCLog::MEMPOOL, "mempool"},
157 [ + - ]: 2 : {BCLog::HTTP, "http"},
158 [ + - ]: 2 : {BCLog::BENCH, "bench"},
159 [ + - ]: 2 : {BCLog::ZMQ, "zmq"},
160 [ + - ]: 2 : {BCLog::WALLETDB, "walletdb"},
161 [ + - ]: 2 : {BCLog::RPC, "rpc"},
162 [ + - ]: 2 : {BCLog::ESTIMATEFEE, "estimatefee"},
163 [ + - ]: 2 : {BCLog::ADDRMAN, "addrman"},
164 [ + - ]: 2 : {BCLog::SELECTCOINS, "selectcoins"},
165 [ + - ]: 2 : {BCLog::REINDEX, "reindex"},
166 [ + - ]: 2 : {BCLog::CMPCTBLOCK, "cmpctblock"},
167 [ + - ]: 2 : {BCLog::RAND, "rand"},
168 [ + - ]: 2 : {BCLog::PRUNE, "prune"},
169 [ + - ]: 2 : {BCLog::PROXY, "proxy"},
170 [ + - ]: 2 : {BCLog::MEMPOOLREJ, "mempoolrej"},
171 [ + - ]: 2 : {BCLog::LIBEVENT, "libevent"},
172 [ + - ]: 2 : {BCLog::COINDB, "coindb"},
173 [ + - ]: 2 : {BCLog::QT, "qt"},
174 [ + - ]: 2 : {BCLog::LEVELDB, "leveldb"},
175 [ + - ]: 2 : {BCLog::VALIDATION, "validation"},
176 [ + - ]: 2 : {BCLog::I2P, "i2p"},
177 [ + - ]: 2 : {BCLog::IPC, "ipc"},
178 : : #ifdef DEBUG_LOCKCONTENTION
179 : : {BCLog::LOCK, "lock"},
180 : : #endif
181 [ + - ]: 2 : {BCLog::UTIL, "util"},
182 [ + - ]: 2 : {BCLog::BLOCKSTORAGE, "blockstorage"},
183 [ + - ]: 2 : {BCLog::TXRECONCILIATION, "txreconciliation"},
184 [ + - ]: 2 : {BCLog::SCAN, "scan"},
185 [ + - ]: 2 : {BCLog::TXPACKAGES, "txpackages"},
186 [ + - ]: 2 : {BCLog::ALL, "1"},
187 [ + - ]: 2 : {BCLog::ALL, "all"},
188 : : };
189 : :
190 : 2 : bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
191 : : {
192 [ - + ]: 2 : if (str.empty()) {
193 : 0 : flag = BCLog::ALL;
194 : 0 : return true;
195 : : }
196 [ + - ]: 43 : for (const CLogCategoryDesc& category_desc : LogCategories) {
197 [ + + ]: 43 : if (category_desc.category == str) {
198 : 2 : flag = category_desc.flag;
199 : 2 : return true;
200 : : }
201 : : }
202 : 0 : return false;
203 : 2 : }
204 : :
205 : 4 : std::string BCLog::Logger::LogLevelToStr(BCLog::Level level) const
206 : : {
207 [ - + + - : 4 : switch (level) {
- - + ]
208 : : case BCLog::Level::Trace:
209 [ + - ]: 1 : return "trace";
210 : : case BCLog::Level::Debug:
211 [ + - ]: 2 : return "debug";
212 : : case BCLog::Level::Info:
213 [ + - ]: 1 : return "info";
214 : : case BCLog::Level::Warning:
215 [ # # ]: 0 : return "warning";
216 : : case BCLog::Level::Error:
217 [ # # ]: 0 : return "error";
218 : : case BCLog::Level::None:
219 [ # # ]: 0 : return "";
220 : : }
221 : 0 : assert(false);
222 : 4 : }
223 : :
224 : 0 : std::string LogCategoryToStr(BCLog::LogFlags category)
225 : : {
226 : : // Each log category string representation should sync with LogCategories
227 [ # # # # : 0 : switch (category) {
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
228 : : case BCLog::LogFlags::NONE:
229 [ # # ]: 0 : return "";
230 : : case BCLog::LogFlags::NET:
231 [ # # ]: 0 : return "net";
232 : : case BCLog::LogFlags::TOR:
233 [ # # ]: 0 : return "tor";
234 : : case BCLog::LogFlags::MEMPOOL:
235 [ # # ]: 0 : return "mempool";
236 : : case BCLog::LogFlags::HTTP:
237 [ # # ]: 0 : return "http";
238 : : case BCLog::LogFlags::BENCH:
239 [ # # ]: 0 : return "bench";
240 : : case BCLog::LogFlags::ZMQ:
241 [ # # ]: 0 : return "zmq";
242 : : case BCLog::LogFlags::WALLETDB:
243 [ # # ]: 0 : return "walletdb";
244 : : case BCLog::LogFlags::RPC:
245 [ # # ]: 0 : return "rpc";
246 : : case BCLog::LogFlags::ESTIMATEFEE:
247 [ # # ]: 0 : return "estimatefee";
248 : : case BCLog::LogFlags::ADDRMAN:
249 [ # # ]: 0 : return "addrman";
250 : : case BCLog::LogFlags::SELECTCOINS:
251 [ # # ]: 0 : return "selectcoins";
252 : : case BCLog::LogFlags::REINDEX:
253 [ # # ]: 0 : return "reindex";
254 : : case BCLog::LogFlags::CMPCTBLOCK:
255 [ # # ]: 0 : return "cmpctblock";
256 : : case BCLog::LogFlags::RAND:
257 [ # # ]: 0 : return "rand";
258 : : case BCLog::LogFlags::PRUNE:
259 [ # # ]: 0 : return "prune";
260 : : case BCLog::LogFlags::PROXY:
261 [ # # ]: 0 : return "proxy";
262 : : case BCLog::LogFlags::MEMPOOLREJ:
263 [ # # ]: 0 : return "mempoolrej";
264 : : case BCLog::LogFlags::LIBEVENT:
265 [ # # ]: 0 : return "libevent";
266 : : case BCLog::LogFlags::COINDB:
267 [ # # ]: 0 : return "coindb";
268 : : case BCLog::LogFlags::QT:
269 [ # # ]: 0 : return "qt";
270 : : case BCLog::LogFlags::LEVELDB:
271 [ # # ]: 0 : return "leveldb";
272 : : case BCLog::LogFlags::VALIDATION:
273 [ # # ]: 0 : return "validation";
274 : : case BCLog::LogFlags::I2P:
275 [ # # ]: 0 : return "i2p";
276 : : case BCLog::LogFlags::IPC:
277 [ # # ]: 0 : return "ipc";
278 : : #ifdef DEBUG_LOCKCONTENTION
279 : : case BCLog::LogFlags::LOCK:
280 : : return "lock";
281 : : #endif
282 : : case BCLog::LogFlags::UTIL:
283 [ # # ]: 0 : return "util";
284 : : case BCLog::LogFlags::BLOCKSTORAGE:
285 [ # # ]: 0 : return "blockstorage";
286 : : case BCLog::LogFlags::TXRECONCILIATION:
287 [ # # ]: 0 : return "txreconciliation";
288 : : case BCLog::LogFlags::SCAN:
289 [ # # ]: 0 : return "scan";
290 : : case BCLog::LogFlags::TXPACKAGES:
291 [ # # ]: 0 : return "txpackages";
292 : : case BCLog::LogFlags::ALL:
293 [ # # ]: 0 : return "all";
294 : : }
295 : 0 : assert(false);
296 : 0 : }
297 : :
298 : 1 : static std::optional<BCLog::Level> GetLogLevel(const std::string& level_str)
299 : : {
300 [ + - ]: 1 : if (level_str == "trace") {
301 : 1 : return BCLog::Level::Trace;
302 [ # # ]: 0 : } else if (level_str == "debug") {
303 : 0 : return BCLog::Level::Debug;
304 [ # # ]: 0 : } else if (level_str == "info") {
305 : 0 : return BCLog::Level::Info;
306 [ # # ]: 0 : } else if (level_str == "warning") {
307 : 0 : return BCLog::Level::Warning;
308 [ # # ]: 0 : } else if (level_str == "error") {
309 : 0 : return BCLog::Level::Error;
310 [ # # ]: 0 : } else if (level_str == "none") {
311 : 0 : return BCLog::Level::None;
312 : : } else {
313 : 0 : return std::nullopt;
314 : : }
315 : 1 : }
316 : :
317 : 4 : std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
318 : : {
319 : : // Sort log categories by alphabetical order.
320 : 4 : std::array<CLogCategoryDesc, std::size(LogCategories)> categories;
321 [ + - ]: 4 : std::copy(std::begin(LogCategories), std::end(LogCategories), categories.begin());
322 [ + - ]: 848 : std::sort(categories.begin(), categories.end(), [](auto a, auto b) { return a.category < b.category; });
323 : :
324 : 4 : std::vector<LogCategory> ret;
325 [ + + ]: 136 : for (const CLogCategoryDesc& category_desc : categories) {
326 [ + + ][ + + ]: 132 : if (category_desc.flag == BCLog::NONE || category_desc.flag == BCLog::ALL) continue;
327 : 116 : LogCategory catActive;
328 [ + - ]: 116 : catActive.category = category_desc.category;
329 : 116 : catActive.active = WillLogCategory(category_desc.flag);
330 [ + - ]: 116 : ret.push_back(catActive);
331 : 116 : }
332 : 4 : return ret;
333 [ + - ]: 4 : }
334 : :
335 : : /** Log severity levels that can be selected by the user. */
336 : 1 : static constexpr std::array<BCLog::Level, 3> LogLevelsList()
337 : : {
338 : 1 : return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace};
339 : : }
340 : :
341 : 1 : std::string BCLog::Logger::LogLevelsString() const
342 : : {
343 : 1 : const auto& levels = LogLevelsList();
344 [ + - ][ - + ]: 4 : return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [this](BCLog::Level level) { return LogLevelToStr(level); });
345 : 0 : }
346 : :
347 : 2 : std::string BCLog::Logger::LogTimestampStr(const std::string& str)
348 : : {
349 : 2 : std::string strStamped;
350 : :
351 [ - + ]: 2 : if (!m_log_timestamps)
352 [ # # ]: 0 : return str;
353 : :
354 [ + - ]: 2 : if (m_started_new_line) {
355 : 2 : const auto now{SystemClock::now()};
356 [ + - ]: 2 : const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
357 [ + - ][ + - ]: 2 : strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
358 [ + + ][ + - ]: 2 : if (m_log_time_micros && !strStamped.empty()) {
359 : 1 : strStamped.pop_back();
360 [ + - ][ + - ]: 1 : strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
[ + - ][ - + ]
361 : 1 : }
362 [ + - ]: 2 : std::chrono::seconds mocktime = GetMockTime();
363 [ + - ][ + - ]: 2 : if (mocktime > 0s) {
[ - + ]
364 [ # # ][ # # ]: 0 : strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
[ # # ][ # # ]
[ # # ]
365 : 0 : }
366 [ + - ][ + - ]: 2 : strStamped += ' ' + str;
367 : 2 : } else
368 [ # # ]: 0 : strStamped = str;
369 : :
370 : 2 : return strStamped;
371 : 2 : }
372 : :
373 : : namespace BCLog {
374 : : /** Belts and suspenders: make sure outgoing log messages don't contain
375 : : * potentially suspicious characters, such as terminal control codes.
376 : : *
377 : : * This escapes control characters except newline ('\n') in C syntax.
378 : : * It escapes instead of removes them to still allow for troubleshooting
379 : : * issues where they accidentally end up in strings.
380 : : */
381 : 2 : std::string LogEscapeMessage(const std::string& str) {
382 : 2 : std::string ret;
383 [ + + ]: 189 : for (char ch_in : str) {
384 : 187 : uint8_t ch = (uint8_t)ch_in;
385 [ + + ][ + - ]: 187 : if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
386 [ + - ]: 187 : ret += ch_in;
387 : 187 : } else {
388 [ # # ][ # # ]: 0 : ret += strprintf("\\x%02x", ch);
389 : : }
390 : : }
391 : 2 : return ret;
392 [ + - ]: 2 : }
393 : : } // namespace BCLog
394 : :
395 : 2 : void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
396 : : {
397 : 2 : StdLockGuard scoped_lock(m_cs);
398 [ + - ]: 2 : std::string str_prefixed = LogEscapeMessage(str);
399 : :
400 [ + - ][ + - ]: 2 : if ((category != LogFlags::NONE || level != Level::None) && m_started_new_line) {
401 [ # # ]: 0 : std::string s{"["};
402 : :
403 [ # # ]: 0 : if (category != LogFlags::NONE) {
404 [ # # ][ # # ]: 0 : s += LogCategoryToStr(category);
405 : 0 : }
406 : :
407 [ # # ][ # # ]: 0 : if (category != LogFlags::NONE && level != Level::None) {
408 : : // Only add separator if both flag and level are not NONE
409 [ # # ]: 0 : s += ":";
410 : 0 : }
411 : :
412 [ # # ]: 0 : if (level != Level::None) {
413 [ # # ][ # # ]: 0 : s += LogLevelToStr(level);
414 : 0 : }
415 : :
416 [ # # ]: 0 : s += "] ";
417 [ # # ]: 0 : str_prefixed.insert(0, s);
418 : 0 : }
419 : :
420 [ + + ][ + - ]: 2 : if (m_log_sourcelocations && m_started_new_line) {
421 [ + - ][ + - ]: 1 : str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ]
422 : 1 : }
423 : :
424 [ + + ][ + - ]: 2 : if (m_log_threadnames && m_started_new_line) {
425 [ + - ]: 1 : const auto& threadname = util::ThreadGetInternalName();
426 [ + - ][ # # ]: 1 : str_prefixed.insert(0, "[" + (threadname.empty() ? "unknown" : threadname) + "] ");
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ # # ]
427 : 1 : }
428 : :
429 [ + - ]: 2 : str_prefixed = LogTimestampStr(str_prefixed);
430 : :
431 [ - + ]: 2 : m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
432 : :
433 [ + - ]: 2 : if (m_buffering) {
434 : : // buffer if we haven't started logging yet
435 [ + - ]: 2 : m_msgs_before_open.push_back(str_prefixed);
436 : 2 : return;
437 : : }
438 : :
439 [ # # ]: 0 : if (m_print_to_console) {
440 : : // print to console
441 [ # # ]: 0 : fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
442 [ # # ]: 0 : fflush(stdout);
443 : 0 : }
444 [ # # ]: 0 : for (const auto& cb : m_print_callbacks) {
445 [ # # ]: 0 : cb(str_prefixed);
446 : : }
447 [ # # ]: 0 : if (m_print_to_file) {
448 [ # # ]: 0 : assert(m_fileout != nullptr);
449 : :
450 : : // reopen the log file, if requested
451 [ # # ]: 0 : if (m_reopen_file) {
452 : 0 : m_reopen_file = false;
453 [ # # ]: 0 : FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
454 [ # # ]: 0 : if (new_fileout) {
455 : 0 : setbuf(new_fileout, nullptr); // unbuffered
456 [ # # ]: 0 : fclose(m_fileout);
457 : 0 : m_fileout = new_fileout;
458 : 0 : }
459 : 0 : }
460 [ # # ]: 0 : FileWriteStr(str_prefixed, m_fileout);
461 : 0 : }
462 [ - + ]: 2 : }
463 : :
464 : 0 : void BCLog::Logger::ShrinkDebugFile()
465 : : {
466 : : // Amount of debug.log to save at end when shrinking (must fit in memory)
467 : 0 : constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
468 : :
469 [ # # ]: 0 : assert(!m_file_path.empty());
470 : :
471 : : // Scroll debug.log if it's getting too big
472 : 0 : FILE* file = fsbridge::fopen(m_file_path, "r");
473 : :
474 : : // Special files (e.g. device nodes) may not have a size.
475 : 0 : size_t log_size = 0;
476 : : try {
477 [ # # ]: 0 : log_size = fs::file_size(m_file_path);
478 [ # # ]: 0 : } catch (const fs::filesystem_error&) {}
479 : :
480 : : // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
481 : : // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
482 [ # # ][ # # ]: 0 : if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
483 : : {
484 : : // Restart the file with some of the end
485 [ # # ]: 0 : std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
486 [ # # ][ # # ]: 0 : if (fseek(file, -((long)vch.size()), SEEK_END)) {
487 [ # # ][ # # ]: 0 : LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
[ # # ]
488 [ # # ]: 0 : fclose(file);
489 : 0 : return;
490 : : }
491 [ # # ]: 0 : int nBytes = fread(vch.data(), 1, vch.size(), file);
492 [ # # ]: 0 : fclose(file);
493 : :
494 [ # # ]: 0 : file = fsbridge::fopen(m_file_path, "w");
495 [ # # ]: 0 : if (file)
496 : : {
497 [ # # ]: 0 : fwrite(vch.data(), 1, nBytes, file);
498 [ # # ]: 0 : fclose(file);
499 : 0 : }
500 [ # # # ]: 0 : }
501 [ # # ]: 0 : else if (file != nullptr)
502 : 0 : fclose(file);
503 : 0 : }
504 : :
505 : 1 : bool BCLog::Logger::SetLogLevel(const std::string& level_str)
506 : : {
507 : 1 : const auto level = GetLogLevel(level_str);
508 [ + - ][ - + ]: 1 : if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
509 : 1 : m_log_level = level.value();
510 : 1 : return true;
511 : 1 : }
512 : :
513 : 0 : bool BCLog::Logger::SetCategoryLogLevel(const std::string& category_str, const std::string& level_str)
514 : : {
515 : : BCLog::LogFlags flag;
516 [ # # ]: 0 : if (!GetLogCategory(flag, category_str)) return false;
517 : :
518 : 0 : const auto level = GetLogLevel(level_str);
519 [ # # ][ # # ]: 0 : if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
520 : :
521 : 0 : StdLockGuard scoped_lock(m_cs);
522 [ # # ][ # # ]: 0 : m_category_log_levels[flag] = level.value();
523 : 0 : return true;
524 : 0 : }
|