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