Skip to content

Commit a829064

Browse files
committed
logging: Add severity level to logs.
1 parent 4604508 commit a829064

File tree

3 files changed

+253
-11
lines changed

3 files changed

+253
-11
lines changed

src/logging.cpp

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ const CLogCategoryDesc LogCategories[] =
171171

172172
bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
173173
{
174-
if (str == "") {
174+
if (str.empty()) {
175175
flag = BCLog::ALL;
176176
return true;
177177
}
@@ -184,6 +184,91 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str)
184184
return false;
185185
}
186186

187+
std::string LogLevelToStr(BCLog::Level level)
188+
{
189+
switch (level) {
190+
case BCLog::Level::None:
191+
return "none";
192+
case BCLog::Level::Debug:
193+
return "debug";
194+
case BCLog::Level::Info:
195+
return "info";
196+
case BCLog::Level::Warning:
197+
return "warning";
198+
case BCLog::Level::Error:
199+
return "error";
200+
}
201+
assert(false);
202+
}
203+
204+
std::string LogCategoryToStr(BCLog::LogFlags category)
205+
{
206+
// Each log category string representation should sync with LogCategories
207+
switch (category) {
208+
case BCLog::LogFlags::NONE:
209+
return "none";
210+
case BCLog::LogFlags::NET:
211+
return "net";
212+
case BCLog::LogFlags::TOR:
213+
return "tor";
214+
case BCLog::LogFlags::MEMPOOL:
215+
return "mempool";
216+
case BCLog::LogFlags::HTTP:
217+
return "http";
218+
case BCLog::LogFlags::BENCH:
219+
return "bench";
220+
case BCLog::LogFlags::ZMQ:
221+
return "zmq";
222+
case BCLog::LogFlags::WALLETDB:
223+
return "walletdb";
224+
case BCLog::LogFlags::RPC:
225+
return "rpc";
226+
case BCLog::LogFlags::ESTIMATEFEE:
227+
return "estimatefee";
228+
case BCLog::LogFlags::ADDRMAN:
229+
return "addrman";
230+
case BCLog::LogFlags::SELECTCOINS:
231+
return "selectcoins";
232+
case BCLog::LogFlags::REINDEX:
233+
return "reindex";
234+
case BCLog::LogFlags::CMPCTBLOCK:
235+
return "cmpctblock";
236+
case BCLog::LogFlags::RAND:
237+
return "rand";
238+
case BCLog::LogFlags::PRUNE:
239+
return "prune";
240+
case BCLog::LogFlags::PROXY:
241+
return "proxy";
242+
case BCLog::LogFlags::MEMPOOLREJ:
243+
return "mempoolrej";
244+
case BCLog::LogFlags::LIBEVENT:
245+
return "libevent";
246+
case BCLog::LogFlags::COINDB:
247+
return "coindb";
248+
case BCLog::LogFlags::QT:
249+
return "qt";
250+
case BCLog::LogFlags::LEVELDB:
251+
return "leveldb";
252+
case BCLog::LogFlags::VALIDATION:
253+
return "validation";
254+
case BCLog::LogFlags::I2P:
255+
return "i2p";
256+
case BCLog::LogFlags::IPC:
257+
return "ipc";
258+
#ifdef DEBUG_LOCKCONTENTION
259+
case BCLog::LogFlags::LOCK:
260+
return "lock";
261+
#endif
262+
case BCLog::LogFlags::UTIL:
263+
return "util";
264+
case BCLog::LogFlags::BLOCKSTORE:
265+
return "blockstorage";
266+
case BCLog::LogFlags::ALL:
267+
return "all";
268+
}
269+
assert(false);
270+
}
271+
187272
std::vector<LogCategory> BCLog::Logger::LogCategoriesList() const
188273
{
189274
// Sort log categories by alphabetical order.
@@ -249,11 +334,31 @@ namespace BCLog {
249334
}
250335
} // namespace BCLog
251336

252-
void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line)
337+
void BCLog::Logger::LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level)
253338
{
254339
StdLockGuard scoped_lock(m_cs);
255340
std::string str_prefixed = LogEscapeMessage(str);
256341

342+
if ((category != LogFlags::NONE || level != Level::None) && m_started_new_line) {
343+
std::string s{"["};
344+
345+
if (category != LogFlags::NONE) {
346+
s += LogCategoryToStr(category);
347+
}
348+
349+
if (category != LogFlags::NONE && level != Level::None) {
350+
// Only add separator if both flag and level are not NONE
351+
s += ":";
352+
}
353+
354+
if (level != Level::None) {
355+
s += LogLevelToStr(level);
356+
}
357+
358+
s += "] ";
359+
str_prefixed.insert(0, s);
360+
}
361+
257362
if (m_log_sourcelocations && m_started_new_line) {
258363
str_prefixed.insert(0, "[" + RemovePrefix(source_file, "./") + ":" + ToString(source_line) + "] [" + logging_function + "] ");
259364
}

src/logging.h

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,13 @@ namespace BCLog {
6767
BLOCKSTORE = (1 << 26),
6868
ALL = ~(uint32_t)0,
6969
};
70+
enum class Level {
71+
Debug = 0,
72+
None = 1,
73+
Info = 2,
74+
Warning = 3,
75+
Error = 4,
76+
};
7077

7178
class Logger
7279
{
@@ -105,7 +112,7 @@ namespace BCLog {
105112
std::atomic<bool> m_reopen_file{false};
106113

107114
/** Send a string to the log output */
108-
void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line);
115+
void LogPrintStr(const std::string& str, const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags category, const BCLog::Level level);
109116

110117
/** Returns whether logs will be written to any output */
111118
bool Enabled() const
@@ -173,7 +180,7 @@ bool GetLogCategory(BCLog::LogFlags& flag, const std::string& str);
173180
// peer can fill up a user's disk with debug.log entries.
174181

175182
template <typename... Args>
176-
static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const char* fmt, const Args&... args)
183+
static inline void LogPrintf_(const std::string& logging_function, const std::string& source_file, const int source_line, const BCLog::LogFlags flag, const BCLog::Level level, const char* fmt, const Args&... args)
177184
{
178185
if (LogInstance().Enabled()) {
179186
std::string log_msg;
@@ -183,19 +190,29 @@ static inline void LogPrintf_(const std::string& logging_function, const std::st
183190
/* Original format string will have newline so don't add one here */
184191
log_msg = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + fmt;
185192
}
186-
LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line);
193+
LogInstance().LogPrintStr(log_msg, logging_function, source_file, source_line, flag, level);
187194
}
188195
}
189196

190-
#define LogPrintf(...) LogPrintf_(__func__, __FILE__, __LINE__, __VA_ARGS__)
197+
198+
#define LogPrintLevel_(category, level, ...) LogPrintf_(__func__, __FILE__, __LINE__, category, level, __VA_ARGS__)
199+
200+
#define LogPrintf(...) LogPrintLevel_(BCLog::LogFlags::NONE, BCLog::Level::None, __VA_ARGS__)
191201

192202
// Use a macro instead of a function for conditional logging to prevent
193203
// evaluating arguments when logging for the category is not enabled.
194-
#define LogPrint(category, ...) \
195-
do { \
196-
if (LogAcceptCategory((category))) { \
197-
LogPrintf(__VA_ARGS__); \
198-
} \
204+
#define LogPrint(category, ...) \
205+
do { \
206+
if (LogAcceptCategory((category))) { \
207+
LogPrintLevel_(category, BCLog::Level::None, __VA_ARGS__); \
208+
} \
209+
} while (0)
210+
211+
#define LogPrintLevel(level, category, ...) \
212+
do { \
213+
if (LogAcceptCategory((category))) { \
214+
LogPrintLevel_(category, level, __VA_ARGS__); \
215+
} \
199216
} while (0)
200217

201218
#endif // BITCOIN_LOGGING_H

src/test/logging_tests.cpp

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,55 @@
55
#include <logging.h>
66
#include <logging/timer.h>
77
#include <test/util/setup_common.h>
8+
#include <util/string.h>
89

910
#include <chrono>
11+
#include <fstream>
12+
#include <iostream>
13+
#include <utility>
14+
#include <vector>
1015

1116
#include <boost/test/unit_test.hpp>
1217

1318
BOOST_FIXTURE_TEST_SUITE(logging_tests, BasicTestingSetup)
1419

20+
struct LogSetup : public BasicTestingSetup {
21+
fs::path prev_log_path;
22+
fs::path tmp_log_path;
23+
bool prev_reopen_file;
24+
bool prev_print_to_file;
25+
bool prev_log_timestamps;
26+
bool prev_log_threadnames;
27+
bool prev_log_sourcelocations;
28+
29+
LogSetup() : prev_log_path{LogInstance().m_file_path},
30+
tmp_log_path{m_args.GetDataDirBase() / "tmp_debug.log"},
31+
prev_reopen_file{LogInstance().m_reopen_file},
32+
prev_print_to_file{LogInstance().m_print_to_file},
33+
prev_log_timestamps{LogInstance().m_log_timestamps},
34+
prev_log_threadnames{LogInstance().m_log_threadnames},
35+
prev_log_sourcelocations{LogInstance().m_log_sourcelocations}
36+
{
37+
LogInstance().m_file_path = tmp_log_path;
38+
LogInstance().m_reopen_file = true;
39+
LogInstance().m_print_to_file = true;
40+
LogInstance().m_log_timestamps = false;
41+
LogInstance().m_log_threadnames = false;
42+
LogInstance().m_log_sourcelocations = true;
43+
}
44+
45+
~LogSetup()
46+
{
47+
LogInstance().m_file_path = prev_log_path;
48+
LogPrintf("Sentinel log to reopen log file\n");
49+
LogInstance().m_print_to_file = prev_print_to_file;
50+
LogInstance().m_reopen_file = prev_reopen_file;
51+
LogInstance().m_log_timestamps = prev_log_timestamps;
52+
LogInstance().m_log_threadnames = prev_log_threadnames;
53+
LogInstance().m_log_sourcelocations = prev_log_sourcelocations;
54+
}
55+
};
56+
1557
BOOST_AUTO_TEST_CASE(logging_timer)
1658
{
1759
SetMockTime(1);
@@ -30,4 +72,82 @@ BOOST_AUTO_TEST_CASE(logging_timer)
3072
BOOST_CHECK_EQUAL(sec_timer.LogMsg("test secs"), "tests: test secs (1.00s)");
3173
}
3274

75+
BOOST_FIXTURE_TEST_CASE(logging_LogPrintf_, LogSetup)
76+
{
77+
LogPrintf_("fn1", "src1", 1, BCLog::LogFlags::NET, BCLog::Level::Debug, "foo1: %s", "bar1\n");
78+
LogPrintf_("fn2", "src2", 2, BCLog::LogFlags::NET, BCLog::Level::None, "foo2: %s", "bar2\n");
79+
LogPrintf_("fn3", "src3", 3, BCLog::LogFlags::NONE, BCLog::Level::Debug, "foo3: %s", "bar3\n");
80+
LogPrintf_("fn4", "src4", 4, BCLog::LogFlags::NONE, BCLog::Level::None, "foo4: %s", "bar4\n");
81+
std::ifstream file{tmp_log_path};
82+
std::vector<std::string> log_lines;
83+
for (std::string log; std::getline(file, log);) {
84+
log_lines.push_back(log);
85+
}
86+
std::vector<std::string> expected = {
87+
"[src1:1] [fn1] [net:debug] foo1: bar1",
88+
"[src2:2] [fn2] [net] foo2: bar2",
89+
"[src3:3] [fn3] [debug] foo3: bar3",
90+
"[src4:4] [fn4] foo4: bar4",
91+
};
92+
BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
93+
}
94+
95+
BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros, LogSetup)
96+
{
97+
// Prevent tests from failing when the line number of the following log calls changes.
98+
LogInstance().m_log_sourcelocations = false;
99+
100+
LogPrintf("foo5: %s\n", "bar5");
101+
LogPrint(BCLog::NET, "foo6: %s\n", "bar6");
102+
LogPrintLevel(BCLog::Level::Debug, BCLog::NET, "foo7: %s\n", "bar7");
103+
LogPrintLevel(BCLog::Level::Info, BCLog::NET, "foo8: %s\n", "bar8");
104+
LogPrintLevel(BCLog::Level::Warning, BCLog::NET, "foo9: %s\n", "bar9");
105+
LogPrintLevel(BCLog::Level::Error, BCLog::NET, "foo10: %s\n", "bar10");
106+
std::ifstream file{tmp_log_path};
107+
std::vector<std::string> log_lines;
108+
for (std::string log; std::getline(file, log);) {
109+
log_lines.push_back(log);
110+
}
111+
std::vector<std::string> expected = {
112+
"foo5: bar5",
113+
"[net] foo6: bar6",
114+
"[net:debug] foo7: bar7",
115+
"[net:info] foo8: bar8",
116+
"[net:warning] foo9: bar9",
117+
"[net:error] foo10: bar10"};
118+
BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
119+
}
120+
121+
BOOST_FIXTURE_TEST_CASE(logging_LogPrintMacros_CategoryName, LogSetup)
122+
{
123+
// Prevent tests from failing when the line number of the following log calls changes.
124+
LogInstance().m_log_sourcelocations = false;
125+
LogInstance().EnableCategory(BCLog::LogFlags::ALL);
126+
const auto concated_categery_names = LogInstance().LogCategoriesString();
127+
std::vector<std::pair<BCLog::LogFlags, std::string>> expected_category_names;
128+
const auto category_names = SplitString(concated_categery_names, ',');
129+
for (const auto& category_name : category_names) {
130+
BCLog::LogFlags category = BCLog::NONE;
131+
const auto trimmed_category_name = TrimString(category_name);
132+
BOOST_TEST(GetLogCategory(category, trimmed_category_name));
133+
expected_category_names.emplace_back(category, trimmed_category_name);
134+
}
135+
136+
std::vector<std::string> expected;
137+
for (const auto& [category, name] : expected_category_names) {
138+
LogPrint(category, "foo: %s\n", "bar");
139+
std::string expected_log = "[";
140+
expected_log += name;
141+
expected_log += "] foo: bar";
142+
expected.push_back(expected_log);
143+
}
144+
145+
std::ifstream file{tmp_log_path};
146+
std::vector<std::string> log_lines;
147+
for (std::string log; std::getline(file, log);) {
148+
log_lines.push_back(log);
149+
}
150+
BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
151+
}
152+
33153
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)