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