Bitcoin ABC  0.22.13
P2P Digital Currency
logging.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 The Bitcoin Core developers
3 // Copyright (c) 2017-2019 The Bitcoin developers
4 // Distributed under the MIT software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include <logging.h>
8 
9 #include <util/threadnames.h>
10 #include <util/time.h>
11 
13 const char *const DEFAULT_DEBUGLOGFILE = "debug.log";
14 
31  static BCLog::Logger *g_logger{new BCLog::Logger()};
32  return *g_logger;
33 }
34 
35 static int FileWriteStr(const std::string &str, FILE *fp) {
36  return fwrite(str.data(), 1, str.size(), fp);
37 }
38 
40  StdLockGuard scoped_lock(m_cs);
41 
42  assert(m_buffering);
43  assert(m_fileout == nullptr);
44 
45  if (m_print_to_file) {
46  assert(!m_file_path.empty());
47  m_fileout = fsbridge::fopen(m_file_path, "a");
48  if (!m_fileout) {
49  return false;
50  }
51 
52  // Unbuffered.
53  setbuf(m_fileout, nullptr);
54 
55  // Add newlines to the logfile to distinguish this execution from the
56  // last one.
57  FileWriteStr("\n\n\n\n\n", m_fileout);
58  }
59 
60  // Dump buffered messages from before we opened the log.
61  m_buffering = false;
62  while (!m_msgs_before_open.empty()) {
63  const std::string &s = m_msgs_before_open.front();
64 
65  if (m_print_to_file) {
66  FileWriteStr(s, m_fileout);
67  }
68  if (m_print_to_console) {
69  fwrite(s.data(), 1, s.size(), stdout);
70  }
71  for (const auto &cb : m_print_callbacks) {
72  cb(s);
73  }
74 
75  m_msgs_before_open.pop_front();
76  }
77  if (m_print_to_console) {
78  fflush(stdout);
79  }
80 
81  return true;
82 }
83 
85  StdLockGuard scoped_lock(m_cs);
86  m_buffering = true;
87  if (m_fileout != nullptr) {
88  fclose(m_fileout);
89  }
90  m_fileout = nullptr;
91  m_print_callbacks.clear();
92 }
93 
96  std::string category;
97 };
98 
100  {BCLog::NONE, "0"},
101  {BCLog::NONE, "none"},
102  {BCLog::NET, "net"},
103  {BCLog::TOR, "tor"},
104  {BCLog::MEMPOOL, "mempool"},
105  {BCLog::HTTP, "http"},
106  {BCLog::BENCH, "bench"},
107  {BCLog::ZMQ, "zmq"},
108  {BCLog::WALLETDB, "walletdb"},
109  {BCLog::RPC, "rpc"},
110  {BCLog::ESTIMATEFEE, "estimatefee"},
111  {BCLog::ADDRMAN, "addrman"},
112  {BCLog::SELECTCOINS, "selectcoins"},
113  {BCLog::REINDEX, "reindex"},
114  {BCLog::CMPCTBLOCK, "cmpctblock"},
115  {BCLog::RAND, "rand"},
116  {BCLog::PRUNE, "prune"},
117  {BCLog::PROXY, "proxy"},
118  {BCLog::MEMPOOLREJ, "mempoolrej"},
119  {BCLog::LIBEVENT, "libevent"},
120  {BCLog::COINDB, "coindb"},
121  {BCLog::QT, "qt"},
122  {BCLog::LEVELDB, "leveldb"},
123  {BCLog::VALIDATION, "validation"},
124  {BCLog::ALL, "1"},
125  {BCLog::ALL, "all"},
126 };
127 
128 bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str) {
129  if (str == "") {
130  flag = BCLog::ALL;
131  return true;
132  }
133  for (const CLogCategoryDesc &category_desc : LogCategories) {
134  if (category_desc.category == str) {
135  flag = category_desc.flag;
136  return true;
137  }
138  }
139  return false;
140 }
141 
142 std::vector<LogCategory> BCLog::Logger::LogCategoriesList() {
143  std::vector<LogCategory> ret;
144  for (const CLogCategoryDesc &category_desc : LogCategories) {
145  // Omit the special cases.
146  if (category_desc.flag != BCLog::NONE &&
147  category_desc.flag != BCLog::ALL) {
148  LogCategory catActive;
149  catActive.category = category_desc.category;
150  catActive.active = WillLogCategory(category_desc.flag);
151  ret.push_back(catActive);
152  }
153  }
154  return ret;
155 }
156 
158  if (m_fileout) {
159  fclose(m_fileout);
160  }
161 }
162 
163 std::string BCLog::Logger::LogTimestampStr(const std::string &str) {
164  std::string strStamped;
165 
166  if (!m_log_timestamps) {
167  return str;
168  }
169 
170  if (m_started_new_line) {
171  int64_t nTimeMicros = GetTimeMicros();
172  strStamped = FormatISO8601DateTime(nTimeMicros / 1000000);
173  if (m_log_time_micros) {
174  strStamped.pop_back();
175  strStamped += strprintf(".%06dZ", nTimeMicros % 1000000);
176  }
177  int64_t mocktime = GetMockTime();
178  if (mocktime) {
179  strStamped +=
180  " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
181  }
182  strStamped += ' ' + str;
183  } else {
184  strStamped = str;
185  }
186 
187  return strStamped;
188 }
189 
190 namespace BCLog {
198 std::string LogEscapeMessage(const std::string &str) {
199  std::string ret;
200  for (char ch_in : str) {
201  uint8_t ch = (uint8_t)ch_in;
202  if ((ch >= 32 || ch == '\n') && ch != '\x7f') {
203  ret += ch_in;
204  } else {
205  ret += strprintf("\\x%02x", ch);
206  }
207  }
208  return ret;
209 }
210 } // namespace BCLog
211 
212 void BCLog::Logger::LogPrintStr(const std::string &str) {
213  StdLockGuard scoped_lock(m_cs);
214  std::string str_prefixed = LogEscapeMessage(str);
215 
217  str_prefixed.insert(0, "[" + util::ThreadGetInternalName() + "] ");
218  }
219 
220  str_prefixed = LogTimestampStr(str_prefixed);
221 
222  m_started_new_line = !str.empty() && str[str.size() - 1] == '\n';
223 
224  if (m_buffering) {
225  // buffer if we haven't started logging yet
226  m_msgs_before_open.push_back(str_prefixed);
227  return;
228  }
229 
230  if (m_print_to_console) {
231  // Print to console.
232  fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout);
233  fflush(stdout);
234  }
235  for (const auto &cb : m_print_callbacks) {
236  cb(str_prefixed);
237  }
238  if (m_print_to_file) {
239  assert(m_fileout != nullptr);
240 
241  // Reopen the log file, if requested.
242  if (m_reopen_file) {
243  m_reopen_file = false;
244  FILE *new_fileout = fsbridge::fopen(m_file_path, "a");
245  if (new_fileout) {
246  // unbuffered.
247  setbuf(m_fileout, nullptr);
248  fclose(m_fileout);
249  m_fileout = new_fileout;
250  }
251  }
252  FileWriteStr(str_prefixed, m_fileout);
253  }
254 }
255 
257  // Amount of debug.log to save at end when shrinking (must fit in memory)
258  constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
259 
260  assert(!m_file_path.empty());
261 
262  // Scroll debug.log if it's getting too big.
263  FILE *file = fsbridge::fopen(m_file_path, "r");
264 
265  // Special files (e.g. device nodes) may not have a size.
266  size_t log_size = 0;
267  try {
268  log_size = fs::file_size(m_file_path);
269  } catch (const fs::filesystem_error &) {
270  }
271 
272  // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE
273  // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes.
274  if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) {
275  // Restart the file with some of the end.
276  std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0);
277  if (fseek(file, -((long)vch.size()), SEEK_END)) {
278  LogPrintf("Failed to shrink debug log file: fseek(...) failed\n");
279  fclose(file);
280  return;
281  }
282  int nBytes = fread(vch.data(), 1, vch.size(), file);
283  fclose(file);
284 
285  file = fsbridge::fopen(m_file_path, "w");
286  if (file) {
287  fwrite(vch.data(), 1, nBytes, file);
288  fclose(file);
289  }
290  } else if (file != nullptr) {
291  fclose(file);
292  }
293 }
294 
296  m_categories |= category;
297 }
298 
299 bool BCLog::Logger::EnableCategory(const std::string &str) {
300  BCLog::LogFlags flag;
301  if (!GetLogCategory(flag, str)) {
302  return false;
303  }
304  EnableCategory(flag);
305  return true;
306 }
307 
309  m_categories &= ~category;
310 }
311 
312 bool BCLog::Logger::DisableCategory(const std::string &str) {
313  BCLog::LogFlags flag;
314  if (!GetLogCategory(flag, str)) {
315  return false;
316  }
317  DisableCategory(flag);
318  return true;
319 }
320 
322  // ALL is not meant to be used as a logging category, but only as a mask
323  // representing all categories.
324  if (category == BCLog::NONE || category == BCLog::ALL) {
325  LogPrintf("Error trying to log using a category mask instead of an "
326  "explicit category.\n");
327  return true;
328  }
329 
330  return (m_categories.load(std::memory_order_relaxed) & category) != 0;
331 }
332 
334  return m_categories != BCLog::NONE;
335 }
StdMutex m_cs
Definition: logging.h:67
BCLog::Logger & LogInstance()
Definition: logging.cpp:15
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:22
void EnableCategory(LogFlags category)
Definition: logging.cpp:295
std::vector< LogCategory > LogCategoriesList()
Returns a vector of the log categories.
Definition: logging.cpp:142
fs::path m_file_path
Definition: logging.h:99
Definition: timer.h:16
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201
BCLog::LogFlags flag
Definition: logging.cpp:95
std::atomic< bool > m_reopen_file
Definition: logging.h:100
static void LogPrintf(const char *fmt, const Args &... args)
Definition: logging.h:174
std::string FormatISO8601DateTime(int64_t nTime)
ISO 8601 formatting is preferred.
Definition: time.cpp:79
bool m_print_to_console
Definition: logging.h:92
void DisableCategory(LogFlags category)
Definition: logging.cpp:308
bool m_print_to_file
Definition: logging.h:93
void LogPrintStr(const std::string &str)
Send a string to the log output.
Definition: logging.cpp:212
std::string category
Definition: logging.cpp:96
std::string category
Definition: logging.h:30
bool m_log_threadnames
Definition: logging.h:97
bool m_log_time_micros
Definition: logging.h:96
int64_t GetMockTime()
For testing.
Definition: time.cpp:55
bool WillLogCategory(LogFlags category) const
Return true if log accepts specified category.
Definition: logging.cpp:321
std::atomic< uint32_t > m_categories
Log categories bitfield.
Definition: logging.h:83
void DisconnectTestLogger()
Only for testing.
Definition: logging.cpp:84
bool StartLogging()
Start logging (and flush all buffered messages)
Definition: logging.cpp:39
bool GetLogCategory(BCLog::LogFlags &flag, const std::string &str)
Return true if str parses as a log category and set the flag.
Definition: logging.cpp:128
int64_t GetTimeMicros()
Returns the system time (not mockable)
Definition: time.cpp:67
std::atomic_bool m_started_new_line
m_started_new_line is a state variable that will suppress printing of the timestamp when multiple cal...
Definition: logging.h:78
const std::string & ThreadGetInternalName()
Get the thread&#39;s internal (in-memory) name; used e.g.
Definition: threadnames.cpp:39
static int FileWriteStr(const std::string &str, FILE *fp)
Definition: logging.cpp:35
const CLogCategoryDesc LogCategories[]
Definition: logging.cpp:99
bool fLogIPs
Definition: logging.cpp:12
bool active
Definition: logging.h:31
LogFlags
Definition: logging.h:36
bool m_log_timestamps
Definition: logging.h:95
static const bool DEFAULT_LOGIPS
Definition: logging.h:22
std::string LogEscapeMessage(const std::string &str)
Belts and suspenders: make sure outgoing log messages don&#39;t contain potentially suspicious characters...
Definition: logging.cpp:198
const char *const DEFAULT_DEBUGLOGFILE
Definition: logging.cpp:13
void ShrinkDebugFile()
Definition: logging.cpp:256
std::string LogTimestampStr(const std::string &str)
Definition: logging.cpp:163
bool DefaultShrinkDebugFile() const
Default for whether ShrinkDebugFile should be run.
Definition: logging.cpp:333