/ 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 <map>
 16  #include <optional>
 17  
 18  using util::Join;
 19  using util::RemovePrefixView;
 20  
 21  const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
 22  constexpr auto MAX_USER_SETABLE_SEVERITY_LEVEL{BCLog::Level::Info};
 23  
 24  BCLog::Logger& LogInstance()
 25  {
 26  /**
 27   * NOTE: the logger instances is leaked on exit. This is ugly, but will be
 28   * cleaned up by the OS/libc. Defining a logger as a global object doesn't work
 29   * since the order of destruction of static/global objects is undefined.
 30   * Consider if the logger gets destroyed, and then some later destructor calls
 31   * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to
 32   * access the logger. When the shutdown sequence is fully audited and tested,
 33   * explicit destruction of these objects can be implemented by changing this
 34   * from a raw pointer to a std::unique_ptr.
 35   * Since the ~Logger() destructor is never called, the Logger class and all
 36   * its subclasses must have implicitly-defined destructors.
 37   *
 38   * This method of initialization was originally introduced in
 39   * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
 40   */
 41      static BCLog::Logger* g_logger{new BCLog::Logger()};
 42      return *g_logger;
 43  }
 44  
 45  bool fLogIPs = DEFAULT_LOGIPS;
 46  
 47  static int FileWriteStr(std::string_view str, FILE *fp)
 48  {
 49      return fwrite(str.data(), 1, str.size(), fp);
 50  }
 51  
 52  bool BCLog::Logger::StartLogging()
 53  {
 54      StdLockGuard scoped_lock(m_cs);
 55  
 56      assert(m_buffering);
 57      assert(m_fileout == nullptr);
 58  
 59      if (m_print_to_file) {
 60          assert(!m_file_path.empty());
 61          m_fileout = fsbridge::fopen(m_file_path, "a");
 62          if (!m_fileout) {
 63              return false;
 64          }
 65  
 66          setbuf(m_fileout, nullptr); // unbuffered
 67  
 68          // Add newlines to the logfile to distinguish this execution from the
 69          // last one.
 70          FileWriteStr("\n\n\n\n\n", m_fileout);
 71      }
 72  
 73      // dump buffered messages from before we opened the log
 74      m_buffering = false;
 75      if (m_buffer_lines_discarded > 0) {
 76          LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), __func__, __FILE__, __LINE__, BCLog::ALL, Level::Info);
 77      }
 78      while (!m_msgs_before_open.empty()) {
 79          const auto& buflog = m_msgs_before_open.front();
 80          std::string s{buflog.str};
 81          FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_file, buflog.source_line, buflog.logging_function, buflog.threadname, buflog.now, buflog.mocktime);
 82          m_msgs_before_open.pop_front();
 83  
 84          if (m_print_to_file) FileWriteStr(s, m_fileout);
 85          if (m_print_to_console) fwrite(s.data(), 1, s.size(), stdout);
 86          for (const auto& cb : m_print_callbacks) {
 87              cb(s);
 88          }
 89      }
 90      m_cur_buffer_memusage = 0;
 91      if (m_print_to_console) fflush(stdout);
 92  
 93      return true;
 94  }
 95  
 96  void BCLog::Logger::DisconnectTestLogger()
 97  {
 98      StdLockGuard scoped_lock(m_cs);
 99      m_buffering = true;
100      if (m_fileout != nullptr) fclose(m_fileout);
101      m_fileout = nullptr;
102      m_print_callbacks.clear();
103      m_max_buffer_memusage = DEFAULT_MAX_LOG_BUFFER;
104      m_cur_buffer_memusage = 0;
105      m_buffer_lines_discarded = 0;
106      m_msgs_before_open.clear();
107  }
108  
109  void BCLog::Logger::DisableLogging()
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  
121  void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
122  {
123      m_categories |= flag;
124  }
125  
126  bool 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  
134  void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
135  {
136      m_categories &= ~flag;
137  }
138  
139  bool 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  
147  bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
148  {
149      return (m_categories.load(std::memory_order_relaxed) & category) != 0;
150  }
151  
152  bool BCLog::Logger::WillLogCategoryLevel(BCLog::LogFlags category, BCLog::Level level) const
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  
165  bool BCLog::Logger::DefaultShrinkDebugFile() const
166  {
167      return m_categories == BCLog::NONE;
168  }
169  
170  static 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  
204  static 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;
213      }(LOG_CATEGORIES_BY_STR)
214  };
215  
216  bool 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  
230  std::string BCLog::Logger::LogLevelToStr(BCLog::Level level)
231  {
232      switch (level) {
233      case BCLog::Level::Trace:
234          return "trace";
235      case BCLog::Level::Debug:
236          return "debug";
237      case BCLog::Level::Info:
238          return "info";
239      case BCLog::Level::Warning:
240          return "warning";
241      case BCLog::Level::Error:
242          return "error";
243      }
244      assert(false);
245  }
246  
247  static 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  
257  static 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") {
266          return BCLog::Level::Warning;
267      } else if (level_str == "error") {
268          return BCLog::Level::Error;
269      } else {
270          return std::nullopt;
271      }
272  }
273  
274  std::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  
284  /** Log severity levels that can be selected by the user. */
285  static constexpr std::array<BCLog::Level, 3> LogLevelsList()
286  {
287      return {BCLog::Level::Info, BCLog::Level::Debug, BCLog::Level::Trace};
288  }
289  
290  std::string BCLog::Logger::LogLevelsString() const
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  
296  std::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)};
304      strStamped = FormatISO8601DateTime(TicksSinceEpoch<std::chrono::seconds>(now_seconds));
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  
317  namespace BCLog {
318      /** Belts and suspenders: make sure outgoing log messages don't contain
319       * potentially suspicious characters, such as terminal control codes.
320       *
321       * This escapes control characters except newline ('\n') in C syntax.
322       * It escapes instead of removes them to still allow for troubleshooting
323       * issues where they accidentally end up in strings.
324       */
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  
339  std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level level) const
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  
365  static 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  
370  void 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      if (!str.ends_with('\n')) str.push_back('\n');
373  
374      str.insert(0, GetLogPrefix(category, level));
375  
376      if (m_log_sourcelocations) {
377          str.insert(0, strprintf("[%s:%d] [%s] ", RemovePrefixView(source_file, "./"), source_line, logging_function));
378      }
379  
380      if (m_log_threadnames) {
381          str.insert(0, strprintf("[%s] ", (threadname.empty() ? "unknown" : threadname)));
382      }
383  
384      str.insert(0, LogTimestampStr(now, mocktime));
385  }
386  
387  void 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)
388  {
389      StdLockGuard scoped_lock(m_cs);
390      return LogPrintStr_(str, logging_function, source_file, source_line, category, level);
391  }
392  
393  void 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)
394  {
395      std::string str_prefixed = LogEscapeMessage(str);
396  
397      if (m_buffering) {
398          {
399              BufferedLog buf{
400                  .now=SystemClock::now(),
401                  .mocktime=GetMockTime(),
402                  .str=str_prefixed,
403                  .logging_function=std::string(logging_function),
404                  .source_file=std::string(source_file),
405                  .threadname=util::ThreadGetInternalName(),
406                  .source_line=source_line,
407                  .category=category,
408                  .level=level,
409              };
410              m_cur_buffer_memusage += MemUsage(buf);
411              m_msgs_before_open.push_back(std::move(buf));
412          }
413  
414          while (m_cur_buffer_memusage > m_max_buffer_memusage) {
415              if (m_msgs_before_open.empty()) {
416                  m_cur_buffer_memusage = 0;
417                  break;
418              }
419              m_cur_buffer_memusage -= MemUsage(m_msgs_before_open.front());
420              m_msgs_before_open.pop_front();
421              ++m_buffer_lines_discarded;
422          }
423  
424          return;
425      }
426  
427      FormatLogStrInPlace(str_prefixed, category, level, source_file, source_line, logging_function, util::ThreadGetInternalName(), SystemClock::now(), GetMockTime());
428  
429      if (m_print_to_console) {
430          // print to console
431          fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
432          fflush(stdout);
433      }
434      for (const auto& cb : m_print_callbacks) {
435          cb(str_prefixed);
436      }
437      if (m_print_to_file) {
438          assert(m_fileout != nullptr);
439  
440          // reopen the log file, if requested
441          if (m_reopen_file) {
442              m_reopen_file = false;
443              FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
444              if (new_fileout) {
445                  setbuf(new_fileout, nullptr); // unbuffered
446                  fclose(m_fileout);
447                  m_fileout = new_fileout;
448              }
449          }
450          FileWriteStr(str_prefixed, m_fileout);
451      }
452  }
453  
454  void BCLog::Logger::ShrinkDebugFile()
455  {
456      // Amount of debug.log to save at end when shrinking (must fit in memory)
457      constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
458  
459      assert(!m_file_path.empty());
460  
461      // Scroll debug.log if it's getting too big
462      FILE* file = fsbridge::fopen(m_file_path, "r");
463  
464      // Special files (e.g. device nodes) may not have a size.
465      size_t log_size = 0;
466      try {
467          log_size = fs::file_size(m_file_path);
468      } catch (const fs::filesystem_error&) {}
469  
470      // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
471      // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes
472      if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10))
473      {
474          // Restart the file with some of the end
475          std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
476          if (fseek(file, -((long)vch.size()), SEEK_END)) {
477              LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
478              fclose(file);
479              return;
480          }
481          int nBytes = fread(vch.data(), 1, vch.size(), file);
482          fclose(file);
483  
484          file = fsbridge::fopen(m_file_path, "w");
485          if (file)
486          {
487              fwrite(vch.data(), 1, nBytes, file);
488              fclose(file);
489          }
490      }
491      else if (file != nullptr)
492          fclose(file);
493  }
494  
495  bool BCLog::Logger::SetLogLevel(std::string_view level_str)
496  {
497      const auto level = GetLogLevel(level_str);
498      if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
499      m_log_level = level.value();
500      return true;
501  }
502  
503  bool BCLog::Logger::SetCategoryLogLevel(std::string_view category_str, std::string_view level_str)
504  {
505      BCLog::LogFlags flag;
506      if (!GetLogCategory(flag, category_str)) return false;
507  
508      const auto level = GetLogLevel(level_str);
509      if (!level.has_value() || level.value() > MAX_USER_SETABLE_SEVERITY_LEVEL) return false;
510  
511      StdLockGuard scoped_lock(m_cs);
512      m_category_log_levels[flag] = level.value();
513      return true;
514  }