Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2018 The Bitcoin developers
3 : // Copyright (c) 2015-2022 The PIVX Core 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 : #include "utiltime.h"
9 :
10 :
11 : const char * const DEFAULT_DEBUGLOGFILE = "debug.log";
12 :
13 : /**
14 : * NOTE: the logger instances is leaked on exit. This is ugly, but will be
15 : * cleaned up by the OS/libc. Defining a logger as a global object doesn't work
16 : * since the order of destruction of static/global objects is undefined.
17 : * Consider if the logger gets destroyed, and then some later destructor calls
18 : * LogPrintf, maybe indirectly, and you get a core dump at shutdown trying to
19 : * access the logger. When the shutdown sequence is fully audited and tested,
20 : * explicit destruction of these objects can be implemented by changing this
21 : * from a raw pointer to a std::unique_ptr.
22 : *
23 : * This method of initialization was originally introduced in
24 : * bitcoin@ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
25 : */
26 : BCLog::Logger* const g_logger = new BCLog::Logger();
27 :
28 : bool fLogIPs = DEFAULT_LOGIPS;
29 :
30 :
31 4393160 : static int FileWriteStr(const std::string &str, FILE *fp)
32 : {
33 4393160 : return fwrite(str.data(), 1, str.size(), fp);
34 : }
35 :
36 378 : bool BCLog::Logger::OpenDebugLog()
37 : {
38 756 : std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
39 :
40 378 : assert(m_fileout == nullptr);
41 378 : assert(!m_file_path.empty());
42 :
43 378 : m_fileout = fsbridge::fopen(m_file_path, "a");
44 378 : if (!m_fileout) return false;
45 :
46 376 : setbuf(m_fileout, nullptr); // unbuffered
47 : // dump buffered messages from before we opened the log
48 2149 : while (!m_msgs_before_open.empty()) {
49 1773 : FileWriteStr(m_msgs_before_open.front(), m_fileout);
50 2149 : m_msgs_before_open.pop_front();
51 : }
52 :
53 : return true;
54 : }
55 :
56 481 : void BCLog::Logger::EnableCategory(BCLog::LogFlags flag)
57 : {
58 481 : m_categories |= flag;
59 481 : }
60 :
61 481 : bool BCLog::Logger::EnableCategory(const std::string& str)
62 : {
63 481 : BCLog::LogFlags flag;
64 481 : if (!GetLogCategory(flag, str)) return false;
65 481 : EnableCategory(flag);
66 481 : return true;
67 : }
68 :
69 764 : void BCLog::Logger::DisableCategory(BCLog::LogFlags flag)
70 : {
71 764 : m_categories &= ~flag;
72 764 : }
73 :
74 764 : bool BCLog::Logger::DisableCategory(const std::string& str)
75 : {
76 764 : BCLog::LogFlags flag;
77 764 : if (!GetLogCategory(flag, str)) return false;
78 764 : DisableCategory(flag);
79 764 : return true;
80 : }
81 :
82 3934310 : bool BCLog::Logger::WillLogCategory(BCLog::LogFlags category) const
83 : {
84 3934310 : return (m_categories.load(std::memory_order_relaxed) & category) != 0;
85 : }
86 :
87 838 : bool BCLog::Logger::DefaultShrinkDebugFile() const
88 : {
89 838 : return m_categories == BCLog::NONE;
90 : }
91 :
92 : struct CLogCategoryDesc
93 : {
94 : BCLog::LogFlags flag;
95 : std::string category;
96 : };
97 :
98 : const CLogCategoryDesc LogCategories[] = {
99 : {BCLog::NONE, "0"},
100 : {BCLog::NET, "net"},
101 : {BCLog::TOR, "tor"},
102 : {BCLog::MEMPOOL, "mempool"},
103 : {BCLog::HTTP, "http"},
104 : {BCLog::BENCHMARK, "bench"},
105 : {BCLog::ZMQ, "zmq"},
106 : {BCLog::DB, "db"},
107 : {BCLog::RPC, "rpc"},
108 : {BCLog::ESTIMATEFEE, "estimatefee"},
109 : {BCLog::ADDRMAN, "addrman"},
110 : {BCLog::REINDEX, "reindex"},
111 : {BCLog::PROXY, "proxy"},
112 : {BCLog::MEMPOOLREJ, "mempoolrej"},
113 : {BCLog::LIBEVENT, "libevent"},
114 : {BCLog::COINDB, "coindb"},
115 : {BCLog::QT, "qt"},
116 : {BCLog::LEVELDB, "leveldb"},
117 : {BCLog::STAKING, "staking"},
118 : {BCLog::MASTERNODE, "masternode"},
119 : {BCLog::MNBUDGET, "mnbudget"},
120 : {BCLog::LEGACYZC, "zero"},
121 : {BCLog::MNPING, "mnping"},
122 : {BCLog::SAPLING, "sapling"},
123 : {BCLog::SPORKS, "sporks"},
124 : {BCLog::VALIDATION, "validation"},
125 : {BCLog::LLMQ, "llmq"},
126 : {BCLog::NET_MN, "net_mn"},
127 : {BCLog::DKG, "dkg"},
128 : {BCLog::CHAINLOCKS, "chainlocks"},
129 : {BCLog::ALL, "1"},
130 : {BCLog::ALL, "all"},
131 : };
132 :
133 1245 : bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
134 : {
135 1245 : if (str == "") {
136 382 : flag = BCLog::ALL;
137 382 : return true;
138 : }
139 14520 : for (const CLogCategoryDesc& category_desc : LogCategories) {
140 14520 : if (category_desc.category == str) {
141 863 : flag = category_desc.flag;
142 863 : return true;
143 : }
144 : }
145 : return false;
146 : }
147 :
148 1 : std::string ListLogCategories()
149 : {
150 1 : std::string ret;
151 1 : int outcount = 0;
152 33 : for (const CLogCategoryDesc& category_desc : LogCategories) {
153 : // Omit the special cases.
154 32 : if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
155 29 : if (outcount != 0) ret += ", ";
156 29 : ret += category_desc.category;
157 29 : outcount++;
158 : }
159 : }
160 1 : return ret;
161 : }
162 :
163 0 : std::vector<CLogCategoryActive> ListActiveLogCategories()
164 : {
165 0 : std::vector<CLogCategoryActive> ret;
166 0 : for (const CLogCategoryDesc& category_desc : LogCategories) {
167 : // Omit the special cases.
168 0 : if (category_desc.flag != BCLog::NONE && category_desc.flag != BCLog::ALL) {
169 0 : CLogCategoryActive catActive;
170 0 : catActive.category = category_desc.category;
171 0 : catActive.active = LogAcceptCategory(category_desc.flag);
172 0 : ret.push_back(catActive);
173 : }
174 : }
175 0 : return ret;
176 : }
177 :
178 4393260 : std::string BCLog::Logger::LogTimestampStr(const std::string &str)
179 : {
180 8786530 : std::string strStamped;
181 :
182 4393260 : if (!m_log_timestamps)
183 4393260 : return str;
184 :
185 4393260 : if (m_started_new_line) {
186 4386590 : int64_t nTimeMicros = GetTimeMicros();
187 4386590 : strStamped = FormatISO8601DateTime(nTimeMicros/1000000);
188 4386590 : if (m_log_time_micros) {
189 0 : strStamped.pop_back();
190 0 : strStamped += strprintf(".%06dZ", nTimeMicros % 1000000);
191 : }
192 4386590 : int64_t mocktime = GetMockTime();
193 4386590 : if (mocktime) {
194 1770770 : strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")";
195 : }
196 13156800 : strStamped += ' ' + str;
197 : } else
198 6676 : strStamped = str;
199 :
200 4393260 : if (!str.empty() && str[str.size()-1] == '\n')
201 4386580 : m_started_new_line = true;
202 : else
203 6679 : m_started_new_line = false;
204 :
205 8786530 : return strStamped;
206 : }
207 :
208 4393260 : void BCLog::Logger::LogPrintStr(const std::string &str)
209 : {
210 4393260 : std::string strTimestamped = LogTimestampStr(str);
211 :
212 4393260 : if (m_print_to_console) {
213 : // print to console
214 0 : fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout);
215 0 : fflush(stdout);
216 : }
217 :
218 4393260 : if (m_print_to_file) {
219 8786530 : std::lock_guard<std::mutex> scoped_lock(m_file_mutex);
220 :
221 : // buffer if we haven't opened the log yet
222 4393260 : if (m_fileout == nullptr) {
223 4393260 : m_msgs_before_open.push_back(strTimestamped);
224 :
225 : } else {
226 : // reopen the log file, if requested
227 4391390 : if (m_reopen_file) {
228 0 : m_reopen_file = false;
229 0 : FILE* new_fileout = fsbridge::fopen(m_file_path, "a");
230 0 : if (new_fileout) {
231 0 : setbuf(new_fileout, nullptr); // unbuffered
232 0 : fclose(m_fileout);
233 0 : m_fileout = new_fileout;
234 : }
235 : }
236 4391390 : FileWriteStr(strTimestamped, m_fileout);
237 : }
238 : }
239 4393260 : }
240 :
241 4 : void BCLog::Logger::ShrinkDebugFile()
242 : {
243 : // Amount of debug.log to save at end when shrinking (must fit in memory)
244 4 : constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000;
245 :
246 4 : assert(!m_file_path.empty());
247 :
248 : // Scroll debug.log if it's getting too big
249 4 : FILE* file = fsbridge::fopen(m_file_path, "r");
250 4 : if (file && fs::file_size(m_file_path) > RECENT_DEBUG_HISTORY_SIZE) {
251 : // Restart the file with some of the end
252 0 : std::vector<char> vch(200000, 0);
253 0 : fseek(file, -((long)vch.size()), SEEK_END);
254 0 : int nBytes = fread(vch.data(), 1, vch.size(), file);
255 0 : fclose(file);
256 :
257 0 : file = fsbridge::fopen(m_file_path, "w");
258 0 : if (file) {
259 0 : fwrite(vch.data(), 1, nBytes, file);
260 0 : fclose(file);
261 : }
262 4 : } else if (file != nullptr)
263 0 : fclose(file);
264 4 : }
265 :
266 : /// PIVX
267 :
268 1501 : CBatchedLogger::CBatchedLogger(BCLog::Logger* _logger, BCLog::LogFlags _category, const std::string& _header) :
269 : logger(_logger),
270 1501 : accept(LogAcceptCategory(_category)),
271 1501 : header(_header)
272 1501 : {}
273 :
274 2327 : CBatchedLogger::~CBatchedLogger()
275 : {
276 1501 : Flush();
277 1501 : }
278 :
279 1883 : void CBatchedLogger::Flush()
280 : {
281 1883 : if (!accept || msg.empty()) {
282 : return;
283 : }
284 826 : if (logger && logger->Enabled()) {
285 826 : logger->LogPrintStr(strprintf("%s:\n%s", header, msg));
286 1883 : msg.clear();
287 : }
288 : }
|