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