Skip to content

Commit 7d4654b

Browse files
ryanofskyhodlinator
andcommitted
log, refactor: Disallow category args to logging macros
Disallow passing BCLog category constants to LogInfo(), LogWarning(), and LogError() macros, and disallow omitting BCLog categories when calling LogDebug() and LogTrace() macros. Additionally, drop category information from log output at higher levels as suggested by Hodlinator bitcoin#29256 (comment). None of these restrictions are technically necessary, and not everybody believes they are good. However, they have existed since the Log{Trace,Debug,Info,Warning,Error} macros were introduced in bitcoin#28318, and removing these restrictions is orthogonal to the goals of this PR which are: (1) to allow the Log macros to work without accessing global state and (2) to support local logging customization with additional metadata. This change just more clearly documents the current logging restrictions and provides better error messages to developers when enforcing them. Co-Authored-By: Hodlinator <172445034+hodlinator@users.noreply.github.com>
1 parent 2f2ff16 commit 7d4654b

File tree

2 files changed

+61
-24
lines changed

2 files changed

+61
-24
lines changed

src/logging.h

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -424,11 +424,38 @@ namespace BCLog {
424424

425425
namespace detail {
426426
//! Internal helper to get log context object from the first macro argument.
427-
template <typename Context>
427+
template <bool take_category, typename Context>
428428
requires (Context::log_context)
429429
static const Context& GetContext(const Context& ctx LIFETIMEBOUND) { return ctx; }
430-
static inline Context GetContext(LogFlags category) { return Context{LogInstance(), category}; }
431-
static inline Context GetContext(std::string_view fmt) { return Context{LogInstance()}; }
430+
431+
template <bool take_category>
432+
static const Context& GetContext(const Context& ctx LIFETIMEBOUND) { return ctx; }
433+
434+
template <bool take_category>
435+
static Context GetContext(LogFlags category)
436+
{
437+
//! Trigger compile error if caller tries to pass a category constant as a
438+
//! first argument to a logging call that specifies take_category == false.
439+
//! There is no technical reason why all logging calls could not accept
440+
//! category arguments, but for various reasons, such as (1) not wanting to
441+
//! allow users filter by category at high priority levels, and (2) wanting
442+
//! to incentivize developers to use lower log levels to avoid log spam,
443+
//! passing category constants at higher levels is forbidden.
444+
static_assert(take_category, "Cannot pass BCLog::LogFlags category argument to Info/Warning/Error logging call. Please switch to Debug/Trace call, or drop the category argument!");
445+
return Context{LogInstance(), category};
446+
}
447+
448+
template <bool take_category>
449+
static Context GetContext(std::string_view fmt)
450+
{
451+
//! Trigger compile error if caller does not pass a category constant as the
452+
//! first argument to a logging call that specifies take_category == true.
453+
//! There is no technical reason why category arguments need to be required,
454+
//! but categories are useful for finding and filtering relevant messages
455+
//! when debugging, so we want to encourage them.
456+
static_assert(!take_category, "Missing required BCLog::LogFlags category argument for Debug/Trace logging call. Category can only be omitted for Info/Warning/Error calls.");
457+
return Context{LogInstance()};
458+
}
432459

433460
//! Internal helper to format log arguments and call a logging function.
434461
template <typename LogFn, typename Context, typename ContextArg, typename... Args>
@@ -526,11 +553,11 @@ inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags
526553
//! Internal helper to conditionally log. Only evaluates arguments when needed.
527554
// Allow __func__ to be used in any context without warnings:
528555
// NOLINTBEGIN(bugprone-lambda-function-name)
529-
#define LogPrint_(level, ratelimit, ...) \
556+
#define LogPrint_(level, ratelimit, take_category, ...) \
530557
do { \
531-
const auto& ctx{BCLog::detail::GetContext(FirstArg_((__VA_ARGS__)))}; \
558+
const auto& ctx{BCLog::detail::GetContext<take_category>(FirstArg_((__VA_ARGS__)))}; \
532559
if (const BCLog::Level lvl{level}; LogEnabled(ctx, lvl)) { \
533-
const BCLog::LogFlags cat{ctx.category}; \
560+
const BCLog::LogFlags cat{take_category ? ctx.category : BCLog::LogFlags::ALL}; \
534561
const bool rl{ratelimit}; \
535562
SourceLocation loc{SourceLocation{__func__}}; \
536563
BCLog::detail::Format([&](auto&& message) { \
@@ -580,12 +607,12 @@ inline void LogPrintFormatInternal(SourceLocation&& source_loc, BCLog::LogFlags
580607
//! disk filling attacks. Users enabling logging at Debug and lower levels are
581608
//! assumed to be developers or power users who are aware that -debug may cause
582609
//! excessive disk usage due to logging.
583-
#define LogError(...) LogPrint_(BCLog::Level::Error, true, __VA_ARGS__)
584-
#define LogWarning(...) LogPrint_(BCLog::Level::Warning, true, __VA_ARGS__)
585-
#define LogInfo(...) LogPrint_(BCLog::Level::Info, true, __VA_ARGS__)
586-
#define LogDebug(...) LogPrint_(BCLog::Level::Debug, false, __VA_ARGS__)
587-
#define LogTrace(...) LogPrint_(BCLog::Level::Trace, false, __VA_ARGS__)
588-
#define LogPrintLevel_(ctx, level, ratelimit, ...) LogPrint_((level), (ratelimit), (ctx), __VA_ARGS__)
610+
#define LogError(...) LogPrint_(BCLog::Level::Error, true, false, __VA_ARGS__)
611+
#define LogWarning(...) LogPrint_(BCLog::Level::Warning, true, false, __VA_ARGS__)
612+
#define LogInfo(...) LogPrint_(BCLog::Level::Info, true, false, __VA_ARGS__)
613+
#define LogDebug(...) LogPrint_(BCLog::Level::Debug, false, true, __VA_ARGS__)
614+
#define LogTrace(...) LogPrint_(BCLog::Level::Trace, false, true, __VA_ARGS__)
615+
#define LogPrintLevel_(ctx, level, ratelimit, ...) LogPrint_((level), (ratelimit), true, (ctx), __VA_ARGS__)
589616

590617
>>>>>>> 05d22deffdb (log, refactor: Allow log macros to accept context arguments)
591618
#endif // BITCOIN_LOGGING_H

src/test/logging_tests.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,9 @@ BOOST_AUTO_TEST_CASE(logging_local_logger)
122122
LogTrace(log, "trace %s", "arg");
123123

124124
constexpr auto expected{std::to_array({
125-
"[net:error] error arg\n",
126-
"[net:warning] warning arg\n",
127-
"[net:info] info arg\n",
125+
"[error] error arg\n",
126+
"[warning] warning arg\n",
127+
"info arg\n",
128128
"[net] debug arg\n",
129129
"[net:trace] trace arg\n",
130130
})};
@@ -141,13 +141,23 @@ BOOST_FIXTURE_TEST_CASE(logging_context_args, LogSetup)
141141
LogError("error");
142142
LogWarning("warning");
143143
LogInfo("info");
144+
// LogDebug("debug"); // Not allowed because category is required!
145+
// LogTrace("trace"); // Not allowed because category is required!
144146
LogError("error %s", "arg");
145147
LogWarning("warning %s", "arg");
146148
LogInfo("info %s", "arg");
149+
// LogDebug("debug %s", "arg"); // Not allowed because category is required!
150+
// LogTrace("trace %s", "arg"); // Not allowed because category is required!
147151

148152
// Test logging with category constant arguments.
153+
// LogError(BCLog::NET, "error"); // Not allowed because category is forbidden!
154+
// LogWarning(BCLog::NET, "warning"); // Not allowed because category is forbidden!
155+
// LogInfo(BCLog::NET, "info"); // Not allowed because category is forbidden!
149156
LogDebug(BCLog::NET, "debug");
150157
LogTrace(BCLog::NET, "trace");
158+
// LogError(BCLog::NET, "error %s", "arg"); // Not allowed because category is forbidden!
159+
// LogWarning(BCLog::NET, "warning %s", "arg"); // Not allowed because category is forbidden!
160+
// LogInfo(BCLog::NET, "info %s", "arg"); // Not allowed because category is forbidden!
151161
LogDebug(BCLog::NET, "debug %s", "arg");
152162
LogTrace(BCLog::NET, "trace %s", "arg");
153163

@@ -179,15 +189,15 @@ BOOST_FIXTURE_TEST_CASE(logging_context_args, LogSetup)
179189
"[net] debug arg",
180190
"[net:trace] trace arg",
181191

182-
"[tor:error] error",
183-
"[tor:warning] warning",
184-
"[tor:info] info",
192+
"[error] error",
193+
"[warning] warning",
194+
"info",
185195
"[tor] debug",
186196
"[tor:trace] trace",
187197

188-
"[tor:error] error arg",
189-
"[tor:warning] warning arg",
190-
"[tor:info] info arg",
198+
"[error] error arg",
199+
"[warning] warning arg",
200+
"info arg",
191201
"[tor] debug arg",
192202
"[tor:trace] trace arg",
193203
})};
@@ -232,9 +242,9 @@ BOOST_FIXTURE_TEST_CASE(logging_CustomContext, LogSetup)
232242
}
233243
constexpr auto expected{std::to_array({
234244
"[validation] Custom #1 foo1: bar1",
235-
"[validation:info] Custom #2 foo2: bar2",
236-
"[validation:warning] Custom #3 foo3: bar3",
237-
"[validation:error] Custom #4 foo4: bar4",
245+
"Custom #2 foo2: bar2",
246+
"[warning] Custom #3 foo3: bar3",
247+
"[error] Custom #4 foo4: bar4",
238248
})};
239249
BOOST_CHECK_EQUAL_COLLECTIONS(log_lines.begin(), log_lines.end(), expected.begin(), expected.end());
240250
}

0 commit comments

Comments
 (0)