Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
logging.cpp
Go to the documentation of this file.
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 <memusage.h>
8#include <util/fs.h>
9#include <util/string.h>
10#include <util/threadnames.h>
11#include <util/time.h>
12
13#include <array>
14#include <map>
15#include <optional>
16
17using util::Join;
19
20const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
22
24{
40 static BCLog::Logger* g_logger{new BCLog::Logger()};
41 return *g_logger;
42}
43
45
46static int FileWriteStr(std::string_view str, FILE *fp)
47{
48 return fwrite(str.data(), 1, str.size(), fp);
49}
50
52{
53 StdLockGuard scoped_lock(m_cs);
54
55 assert(m_buffering);
56 assert(m_fileout == nullptr);
57
58 if (m_print_to_file) {
59 assert(!m_file_path.empty());
60 m_fileout = fsbridge::fopen(m_file_path, "a");
61 if (!m_fileout) {
62 return false;
63 }
64
65 setbuf(m_fileout, nullptr); // unbuffered
66
67 // Add newlines to the logfile to distinguish this execution from the
68 // last one.
69 FileWriteStr("\n\n\n\n\n", m_fileout);
70 }
71
72 // dump buffered messages from before we opened the log
73 m_buffering = false;
74 if (m_buffer_lines_discarded > 0) {
75 LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), __func__, __FILE__, __LINE__, BCLog::ALL, Level::Info);
76 }
77 while (!m_msgs_before_open.empty()) {
78 const auto& buflog = m_msgs_before_open.front();
79 std::string s{buflog.str};
80 FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_file, buflog.source_line, buflog.logging_function, buflog.threadname, buflog.now, buflog.mocktime);
81 m_msgs_before_open.pop_front();
82
83 if (m_print_to_file) FileWriteStr(s, m_fileout);
84 if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
85 for (const auto& cb : m_print_callbacks) {
86 cb(s);
87 }
88 }
89 m_cur_buffer_memusage = 0;
90 if (m_print_to_console) fflush(stdout);
91
92 return true;
93}
94
96{
97 StdLockGuard scoped_lock(m_cs);
98 m_buffering = true;
99 if (m_fileout != nullptr) fclose(m_fileout);
100 m_fileout = nullptr;
101 m_print_callbacks.clear();
102 m_max_buffer_memusage = DEFAULT_MAX_LOG_BUFFER;
103 m_cur_buffer_memusage = 0;
104 m_buffer_lines_discarded = 0;
105 m_msgs_before_open.clear();
106
107}
108
110{
111 {
112 StdLockGuard scoped_lock(m_cs);
113 assert(m_buffering);
114 assert(m_print_callbacks.empty());
115 }
116 m_print_to_file = false;
117 m_print_to_console = false;
118 StartLogging();
119}
120
122{
123 m_categories |= flag;
124}
125
126bool BCLog::Logger::EnableCategory(std::string_view str)
127{
128 BCLog::LogFlags flag;
129 if (!GetLogCategory(flag, str)) return false;
130 EnableCategory(flag);
131 return true;
132}
133
135{
136 m_categories &= ~flag;
137}
138
139bool BCLog::Logger::DisableCategory(std::string_view str)
140{
141 BCLog::LogFlags flag;
142 if (!GetLogCategory(flag, str)) return false;
143 DisableCategory(flag);
144 return true;
145}
146
148{
149 return (m_categories.load(std::memory_order_relaxed) & category) != 0;
150}
151
153{
154 // Log messages at Info, Warning and Error level unconditionally, so that
155 // important troubleshooting information doesn't get lost.
156 if (level >= BCLog::Level::Info) return true;
157
158 if (!WillLogCategory(category)) return false;
159
160 StdLockGuard scoped_lock(m_cs);
161 const auto it{m_category_log_levels.find(category)};
162 return level >= (it == m_category_log_levels.end() ? LogLevel() : it->second);
163}
164
166{
167 return m_categories == BCLog::NONE;
168}
169
170static const std::map<std::string, BCLog::LogFlags, std::less<>> LOG_CATEGORIES_BY_STR{
171 {"net", BCLog::NET},
172 {"tor", BCLog::TOR},
173 {"mempool", BCLog::MEMPOOL},
174 {"http", BCLog::HTTP},
175 {"bench", BCLog::BENCH},
176 {"zmq", BCLog::ZMQ},
177 {"walletdb", BCLog::WALLETDB},
178 {"rpc", BCLog::RPC},
179 {"estimatefee", BCLog::ESTIMATEFEE},
180 {"addrman", BCLog::ADDRMAN},
181 {"selectcoins", BCLog::SELECTCOINS},
182 {"reindex", BCLog::REINDEX},
183 {"cmpctblock", BCLog::CMPCTBLOCK},
184 {"rand", BCLog::RAND},
185 {"prune", BCLog::PRUNE},
186 {"proxy", BCLog::PROXY},
187 {"mempoolrej", BCLog::MEMPOOLREJ},
188 {"libevent", BCLog::LIBEVENT},
189 {"coindb", BCLog::COINDB},
190 {"qt", BCLog::QT},
191 {"leveldb", BCLog::LEVELDB},
192 {"validation", BCLog::VALIDATION},
193 {"i2p", BCLog::I2P},
194 {"ipc", BCLog::IPC},
195#ifdef DEBUG_LOCKCONTENTION
196 {"lock", BCLog::LOCK},
197#endif
198 {"blockstorage", BCLog::BLOCKSTORAGE},
199 {"txreconciliation", BCLog::TXRECONCILIATION},
200 {"scan", BCLog::SCAN},
201 {"txpackages", BCLog::TXPACKAGES},
202};
203
204static const std::unordered_map<BCLog::LogFlags, std::string> LOG_CATEGORIES_BY_FLAG{
205 // Swap keys and values from LOG_CATEGORIES_BY_STR.
206 [](const auto& in) {
207 std::unordered_map<BCLog::LogFlags, std::string> out;
208 for (const auto& [k, v] : in) {
209 const bool inserted{out.emplace(v, k).second};
210 assert(inserted);
211 }
212 return out;
214};
215
216bool GetLogCategory(BCLog::LogFlags& flag, std::string_view str)
217{
218 if (str.empty() || str == "1" || str == "all") {
219 flag = BCLog::ALL;
220 return true;
221 }
222 auto it = LOG_CATEGORIES_BY_STR.find(str);
223 if (it != LOG_CATEGORIES_BY_STR.end()) {
224 flag = it->second;
225 return true;
226 }
227 return false;
228}
229
231{
232 switch (level) {
234 return "trace";
236 return "debug";
238 return "info";
240 return "warning";
242 return "error";
243 }
244 assert(false);
245}
246
247static std::string LogCategoryToStr(BCLog::LogFlags category)
248{
249 if (category == BCLog::ALL) {
250 return "all";
251 }
252 auto it = LOG_CATEGORIES_BY_FLAG.find(category);
253 assert(it != LOG_CATEGORIES_BY_FLAG.end());
254 return it->second;
255}
256
257static std::optional<BCLog::Level> GetLogLevel(std::string_view level_str)
258{
259 if (level_str == "trace") {
260 return BCLog::Level::Trace;
261 } else if (level_str == "debug") {
262 return BCLog::Level::Debug;
263 } else if (level_str == "info") {
264 return BCLog::Level::Info;
265 } else if (level_str == "warning") {
267 } else if (level_str == "error") {
268 return BCLog::Level::Error;
269 } else {
270 return std::nullopt;
271 }
272}
273
274std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
275{
276 std::vector<LogCategory> ret;
277 ret.reserve(LOG_CATEGORIES_BY_STR.size());
278 for (const auto& [category, flag] : LOG_CATEGORIES_BY_STR) {
279 ret.push_back(LogCategory{.category = category, .active = WillLogCategory(flag)});
280 }
281 return ret;
282}
283
285static constexpr std::array<BCLog::Level, 3> LogLevelsList()
286{
288}
289
291{
292 const auto& levels = LogLevelsList();
293 return Join(std::vector<BCLog::Level>{levels.begin(), levels.end()}, ", ", [](BCLog::Level level) { return LogLevelToStr(level); });
294}
295
296std::string BCLog::Logger::LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
297{
298 std::string strStamped;
299
300 if (!m_log_timestamps)
301 return strStamped;
302
303 const auto now_seconds{std::chrono::time_point_cast<std::chrono::seconds>(now)};
305 if (m_log_time_micros && !strStamped.empty()) {
306 strStamped.pop_back();
307 strStamped += strprintf(".%06dZ", Ticks<std::chrono::microseconds>(now - now_seconds));
308 }
309 if (mocktime > 0s) {
310 strStamped += " (mocktime: " + FormatISO8601DateTime(count_seconds(mocktime)) + ")";
311 }
312 strStamped += ' ';
313
314 return strStamped;
315}
316
317namespace BCLog {
325 std::string LogEscapeMessage(std::string_view str) {
326 std::string ret;
327 for (char ch_in : str) {
328 uint8_t ch = (uint8_t)ch_in;
329 if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
330 ret += ch_in;
331 } else {
332 ret += strprintf("\\x%02x", ch);
333 }
334 }
335 return ret;
336 }
337} // namespace BCLog
338
340{
341 if (category == LogFlags::NONE) category = LogFlags::ALL;
342
343 const bool has_category{m_always_print_category_level || category != LogFlags::ALL};
344
345 // If there is no category, Info is implied
346 if (!has_category && level == Level::Info) return {};
347
348 std::string s{"["};
349 if (has_category) {
350 s += LogCategoryToStr(category);
351 }
352
353 if (m_always_print_category_level || !has_category || level != Level::Debug) {
354 // If there is a category, Debug is implied, so don't add the level
355
356 // Only add separator if we have a category
357 if (has_category) s += ":";
358 s += Logger::LogLevelToStr(level);
359 }
360
361 s += "] ";
362 return s;
363}
364
365static size_t MemUsage(const BCLog::Logger::BufferedLog& buflog)
366{
367 return buflog.str.size() + buflog.logging_function.size() + buflog.source_file.size() + buflog.threadname.size() + memusage::MallocUsage(sizeof(memusage::list_node<BCLog::Logger::BufferedLog>));
368}
369
370void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags category, BCLog::Level level, std::string_view source_file, int source_line, std::string_view logging_function, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
371{
372 str.insert(0, GetLogPrefix(category, level));
373
374 if (m_log_sourcelocations) {
375 str.insert(0, strprintf("[%s:%d] [%s] ", RemovePrefixView(source_file, "./"), source_line, logging_function));
376 }
377
378 if (m_log_threadnames) {
379 str.insert(0, strprintf("[%s] ", (threadname.empty() ? "unknown" : threadname)));
380 }
381
382 str.insert(0, LogTimestampStr(now, mocktime));
383}
384
385void BCLog::Logger::LogPrintStr(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
386{
387 StdLockGuard scoped_lock(m_cs);
388 return LogPrintStr_(str, logging_function, source_file, source_line, category, level);
389}
390
391void BCLog::Logger::LogPrintStr_(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level)
392{
393 std::string str_prefixed = LogEscapeMessage(str);
394
395 const bool starts_new_line = m_started_new_line;
396 m_started_new_line = !str.empty() && str[str.size()-1] == '\n';
397
398 if (m_buffering) {
399 if (!starts_new_line) {
400 if (!m_msgs_before_open.empty()) {
401 m_msgs_before_open.back().str += str_prefixed;
402 m_cur_buffer_memusage += str_prefixed.size();
403 return;
404 } else {
405 // unlikely edge case; add a marker that something was trimmed
406 str_prefixed.insert(0, "[...] ");
407 }
408 }
409
410 {
411 BufferedLog buf{
412 .now=SystemClock::now(),
413 .mocktime=GetMockTime(),
414 .str=str_prefixed,
415 .logging_function=std::string(logging_function),
416 .source_file=std::string(source_file),
417 .threadname=util::ThreadGetInternalName(),
418 .source_line=source_line,
419 .category=category,
420 .level=level,
421 };
422 m_cur_buffer_memusage += MemUsage(buf);
423 m_msgs_before_open.push_back(std::move(buf));
424 }
425
426 while (m_cur_buffer_memusage > m_max_buffer_memusage) {
427 if (m_msgs_before_open.empty()) {
428 m_cur_buffer_memusage = 0;
429 break;
430 }
431 m_cur_buffer_memusage -= MemUsage(m_msgs_before_open.front());
432 m_msgs_before_open.pop_front();
433 ++m_buffer_lines_discarded;
434 }
435
436 return;
437 }
438
439 if (starts_new_line) {
440 FormatLogStrInPlace(str_prefixed, category, level, source_file, source_line, logging_function, util::ThreadGetInternalName(), SystemClock::now(), GetMockTime());
441 }
442
443 if (m_print_to_console) {
444 // print to console
445 fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
446 fflush(stdout);
447 }
448 for (const auto& cb : m_print_callbacks) {
449 cb(str_prefixed);
450 }
451 if (m_print_to_file) {
452 assert(m_fileout != nullptr);
453
454 // reopen the log file, if requested
455 if (m_reopen_file) {
456 m_reopen_file = false;
457 FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
458 if (new_fileout) {
459 setbuf(new_fileout, nullptr); // unbuffered
460 fclose(m_fileout);
461 m_fileout = new_fileout;
462 }
463 }
464 FileWriteStr(str_prefixed, m_fileout);
465 }
466}
467
469{
470 // Amount of debug.log to save at end when shrinking (must fit in memory)
471 constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
472
473 assert(!m_file_path.empty());
474
475 // Scroll debug.log if it's getting too big
476 FILE* file = fsbridge::fopen(m_file_path, "r");
477
478 // Special files (e.g. device nodes) may not have a size.
479 size_t log_size = 0;
480 try {
481 log_size = fs::file_size(m_file_path);
482 } catch (const fs::filesystem_error&) {}
483
484 // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
485 // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
486 if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
487 {
488 // Restart the file with some of the end
489 std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
490 if (fseek(file, -((long)vch.size()), SEEK_END)) {
491 LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
492 fclose(file);
493 return;
494 }
495 int nBytes = fread(vch.data(), 1, vch.size(), file);
496 fclose(file);
497
498 file = fsbridge::fopen(m_file_path, "w");
499 if (file)
500 {
501 fwrite(vch.data(), 1, nBytes, file);
502 fclose(file);
503 }
504 }
505 else if (file != nullptr)
506 fclose(file);
507}
508
509bool BCLog::Logger::SetLogLevel(std::string_view level_str)
510{
511 const auto level = GetLogLevel(level_str);
512 if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
513 m_log_level = level.value();
514 return true;
515}
516
517bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)
518{
519 BCLog::LogFlags flag;
520 if (!GetLogCategory(flag, category_str)) return false;
521
522 const auto level = GetLogLevel(level_str);
523 if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
524
525 StdLockGuard scoped_lock(m_cs);
526 m_category_log_levels[flag] = level.value();
527 return true;
528}
int ret
static std::string LogLevelToStr(BCLog::Level level)
Returns the string representation of a log level.
Definition logging.cpp:230
bool WillLogCategory(LogFlags category) const
Definition logging.cpp:147
std::string LogTimestampStr(SystemClock::time_point now, std::chrono::seconds mocktime) const
Definition logging.cpp:296
bool DefaultShrinkDebugFile() const
Definition logging.cpp:165
void SetCategoryLogLevel(const std::unordered_map< LogFlags, Level > &levels) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Definition logging.h:196
void SetLogLevel(Level level)
Definition logging.h:204
bool WillLogCategoryLevel(LogFlags category, Level level) const EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Definition logging.cpp:152
fs::path m_file_path
Definition logging.h:147
void DisableLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Disable logging This offers a slight speedup and slightly smaller memory usage compared to leaving th...
Definition logging.cpp:109
std::vector< LogCategory > LogCategoriesList() const
Returns a vector of the log categories in alphabetical order.
Definition logging.cpp:274
bool StartLogging() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Start logging (and flush all buffered messages)
Definition logging.cpp:51
void EnableCategory(LogFlags flag)
Definition logging.cpp:121
void FormatLogStrInPlace(std::string &str, LogFlags category, Level level, std::string_view source_file, int source_line, std::string_view logging_function, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
Definition logging.cpp:370
std::string GetLogPrefix(LogFlags category, Level level) const
Definition logging.cpp:339
void LogPrintStr(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Send a string to the log output.
Definition logging.cpp:385
void DisconnectTestLogger() EXCLUSIVE_LOCKS_REQUIRED(!m_cs)
Only for testing.
Definition logging.cpp:95
std::string LogLevelsString() const
Returns a string with all user-selectable log levels.
Definition logging.cpp:290
void ShrinkDebugFile()
Definition logging.cpp:468
bool m_print_to_file
Definition logging.h:139
bool m_print_to_console
Definition logging.h:138
StdMutex m_cs
Definition logging.h:98
void DisableCategory(LogFlags flag)
Definition logging.cpp:134
void LogPrintStr_(std::string_view str, std::string_view logging_function, std::string_view source_file, int source_line, BCLog::LogFlags category, BCLog::Level level) EXCLUSIVE_LOCKS_REQUIRED(m_cs)
Send a string to the log output (internal)
Definition logging.cpp:391
static constexpr std::array< BCLog::Level, 3 > LogLevelsList()
Log severity levels that can be selected by the user.
Definition logging.cpp:285
static int FileWriteStr(std::string_view str, FILE *fp)
Definition logging.cpp:46
static std::string LogCategoryToStr(BCLog::LogFlags category)
Definition logging.cpp:247
bool GetLogCategory(BCLog::LogFlags &flag, std::string_view str)
Return true if str parses as a log category and set the flag.
Definition logging.cpp:216
static size_t MemUsage(const BCLog::Logger::BufferedLog &buflog)
Definition logging.cpp:365
static const std::map< std::string, BCLog::LogFlags, std::less<> > LOG_CATEGORIES_BY_STR
Definition logging.cpp:170
BCLog::Logger & LogInstance()
Definition logging.cpp:23
bool fLogIPs
Definition logging.cpp:44
static const std::unordered_map< BCLog::LogFlags, std::string > LOG_CATEGORIES_BY_FLAG
Definition logging.cpp:204
const char *const DEFAULT_DEBUGLOGFILE
Definition logging.cpp:20
static std::optional< BCLog::Level > GetLogLevel(std::string_view level_str)
Definition logging.cpp:257
constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL
Definition logging.cpp:21
static const bool DEFAULT_LOGIPS
Definition logging.h:25
#define LogPrintf(...)
Definition logging.h:274
Level
Definition logging.h:75
std::string LogEscapeMessage(std::string_view str)
Belts and suspenders: make sure outgoing log messages don't contain potentially suspicious characters...
Definition logging.cpp:325
constexpr size_t DEFAULT_MAX_LOG_BUFFER
Definition logging.h:83
LogFlags
Definition logging.h:40
@ ESTIMATEFEE
Definition logging.h:50
@ TXRECONCILIATION
Definition logging.h:70
@ RAND
Definition logging.h:55
@ BLOCKSTORAGE
Definition logging.h:69
@ COINDB
Definition logging.h:60
@ REINDEX
Definition logging.h:53
@ TXPACKAGES
Definition logging.h:72
@ WALLETDB
Definition logging.h:48
@ SCAN
Definition logging.h:71
@ ADDRMAN
Definition logging.h:51
@ ALL
Definition logging.h:73
@ RPC
Definition logging.h:49
@ HTTP
Definition logging.h:45
@ LEVELDB
Definition logging.h:62
@ NONE
Definition logging.h:41
@ VALIDATION
Definition logging.h:63
@ MEMPOOLREJ
Definition logging.h:58
@ PRUNE
Definition logging.h:56
@ TOR
Definition logging.h:43
@ LIBEVENT
Definition logging.h:59
@ CMPCTBLOCK
Definition logging.h:54
@ PROXY
Definition logging.h:57
@ ZMQ
Definition logging.h:47
@ IPC
Definition logging.h:65
@ MEMPOOL
Definition logging.h:44
@ SELECTCOINS
Definition logging.h:52
@ I2P
Definition logging.h:64
@ BENCH
Definition logging.h:46
@ NET
Definition logging.h:42
@ QT
Definition logging.h:61
FILE * fopen(const fs::path &p, const char *mode)
Definition fs.cpp:26
static size_t MallocUsage(size_t alloc)
Compute the total memory used by allocating alloc bytes.
Definition memusage.h:51
std::string ThreadGetInternalName()
Get the thread's internal (in-memory) name; used e.g.
std::string_view RemovePrefixView(std::string_view str, std::string_view prefix)
Definition string.h:92
auto Join(const C &container, const S &separator, UnaryOp unary_op)
Join all container items.
Definition string.h:115
std::string source_file
Definition logging.h:91
std::string logging_function
Definition logging.h:91
std::chrono::seconds GetMockTime()
For testing.
Definition time.cpp:39
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition time.cpp:46
constexpr auto TicksSinceEpoch(Timepoint t)
Definition time.h:50
constexpr int64_t count_seconds(std::chrono::seconds t)
Definition time.h:54
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:45
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
assert(!tx.IsCoinBase())