Skip to content

kernel, logging: Pass Logger instances to kernel objects#30342

Draft
ryanofsky wants to merge 15 commits intobitcoin:masterfrom
ryanofsky:pr/gklog
Draft

kernel, logging: Pass Logger instances to kernel objects#30342
ryanofsky wants to merge 15 commits intobitcoin:masterfrom
ryanofsky:pr/gklog

Conversation

@ryanofsky
Copy link
Contributor

@ryanofsky ryanofsky commented Jun 26, 2024

Pass Logger instances to BlockManager, CCoinsViewDB, CDBWrapper, Chainstate, ChainstateManager, CoinsViews, and CTxMemPool classes so libbitcoinkernel applications and tests have the option to control where log output goes instead of having all output sent to the global logger.

This PR is an alternative to #30338. It is more limited because it only changes kernel code while leaving other parts of the codebase alone. It also takes the opportunity to migrate legacy LogPrint / LogPrintf calls to the new log macros introduced in #28318.


This is based on #34778 + #29256 + #33847. The non-base commits are:

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 26, 2024

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/30342.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept ACK sedited
Approach NACK stickies-v

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #34775 (kernel: make logging callback global by stickies-v)
  • #34730 (util/log: Combine the warning/error log levels into a single alert level by ajtowns)
  • #34729 (Reduce log noise by ajtowns)
  • #34654 ([RFC] BlockMap and CChain Concurrency Improvement by alexanderwiederin)
  • #34639 (iwyu: Document or remove some pragma: export and other improvements by hebasto)
  • #34514 (refactor: remove unnecessary std::move for trivially copyable types by l0rinc)
  • #34435 (refactor: use _MiB/_GiB consistently for byte conversions by l0rinc)
  • #34389 (net/log: standardize peer+addr log formatting via LogPeer by l0rinc)
  • #34342 (cli: Replace libevent usage with simple http client by fjahr)
  • #34320 (coins: remove redundant and confusing CCoinsViewDB::HaveCoin by l0rinc)
  • #34132 (coins: drop error catcher, centralize fatal read handling by l0rinc)
  • #34124 (validation: make CCoinsView a pure virtual interface by l0rinc)
  • #34038 (logging: replace -loglevel with -trace, various API improvements by ajtowns)
  • #33854 (fix assumevalid is ignored during reindex by Eunovo)
  • #33727 (zmq: Log bind error at Error level, abort startup on init error by isrod)
  • #33646 (log: check fclose() results and report safely in logging.cpp by cedwies)
  • #33324 (blocks: add -reobfuscate-blocks argument to enable (de)obfuscating existing blocks by l0rinc)
  • #32729 (test,refactor: extract script template helpers & widen sigop count coverage by l0rinc)
  • #32427 ((RFC) kernel: Replace leveldb-based BlockTreeDB with flat-file based store by sedited)
  • #32317 (kernel: Separate UTXO set access from validation functions by sedited)
  • #32297 (bitcoin-cli: Add -ipcconnect option by ryanofsky)
  • #31723 (qa: Facilitate debugging bitcoind inside functional tests by hodlinator)
  • #31132 (validation: fetch block inputs on parallel threads 3x faster IBD by andrewtoth)
  • #26022 (Add util::ResultPtr class by ryanofsky)
  • #25665 (refactor: Add util::Result failure types and ability to merge result values by ryanofsky)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

LLM Linter (✨ experimental)

Possible typos and grammar issues:

  • logger instances is leaked -> logger instance is leaked [subject-verb agreement: "instances" with singular verb "is" is incorrect; make singular ("instance is leaked") or plural ("instances are leaked") to be grammatical]

2026-03-09 19:02:53

@sedited
Copy link
Contributor

sedited commented Jun 26, 2024

Concept ACK.

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the
documentation.

Possibly this is due to a silent merge conflict (the changes in this pull request being
incompatible with the current code in the target branch). If so, make sure to rebase on the latest
commit of the target branch.

Leave a comment here, if you need help tracking down a confusing failure.

Debug: https://github.com/bitcoin/bitcoin/runs/26722091796

@ryanofsky
Copy link
Contributor Author

Updated 4542335 -> db1b9f7 (pr/gklog.1 -> pr/gklog.2, compare) moving code to an earlier commit to fix a "test-each-commit" test failure https://github.com/bitcoin/bitcoin/actions/runs/9684398780/job/26722073921?pr=30342, and cleaning up code to fix a clang-tidy warning https://cirrus-ci.com/task/5893151045451776

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Jul 3, 2024
Change LogInstance() function to no longer allocate (and leak) a BCLog::Logger
instance. Instead allow kernel applications to initialize their own logging
instances that can be returned by LogInstance().

This change is somewhat useful by itself, but more useful in combination with
bitcoin#30342. By itself, it gives kernel applications control over when the Logger is
created and destroyed and allows new instances to replace old ones. In
combination with bitcoin#30342 it allows multiple log instances to exist at the same
time, and different output to be sent to different instances.

This commit is built on top of bitcoin#30141 since it simplifies the implementation
somewhat.
@DrahtBot
Copy link
Contributor

DrahtBot commented Feb 9, 2026

🚧 At least one of the CI tasks failed.
Task iwyu: https://github.com/bitcoin/bitcoin/actions/runs/21830259022/job/62995439943
LLM reason (✨ experimental): CI failure caused by an IWYU (Include-What-You-Use) check failing.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

Copy link
Contributor

@stickies-v stickies-v left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've finally started reviewing this PR in more detail, after my recent related work on kernel logging. I'm currently leaning Concept ACK, Approach NACK.

Concept ACK because:

  • a large part of the bitcoinkernel interface is contextualized, e.g. btck_ChainstateManager is initialized with a btck_Context, so having contextualized logging seems like a natural fit
  • bitcoinkernel is a library, we should be minimally opinionated on how it's used, so long as it's safe. If users have good use cases for e.g. multiple ChainstateManager's, being able to separate log streams would be good

Approach NACK because I think the below downsides are bigger than the above upsides:

  • logging interface becomes more cumbersome, i.e. caller has to figure out if a logger is in scope and if it should be used. It is easy to misuse (e.g. forget to log to context when it exists, which seems hard to enforce.)
  • non-trivial code change / review cost, lots of merge conflicts, ...

I think this change might become more desirable if/when we have more demand for this feature (i.e. more actual use cases for contextualized logging) or when it becomes a less invasive code change to do so (e.g. if/when kernel is a separate codebase, or when it becomes feasible to use an interface like m_log.info(...)).

I've asked the kernel working group for use cases and motivations for contextualized logging, and so far I've not heard much (but I hope people will chime in here rather than me speaking on their behalf). If you have any, perhaps opening a tracking issue for that might be useful so we can better decide when this feature becomes worth it given the complexity? I'm definitely not against the idea itself, I just pragmatically think it's not worth the current cost.

@ryanofsky
Copy link
Contributor Author

Thanks for the review!

  • logging interface becomes more cumbersome, i.e. caller has to figure out if a logger is in scope and if it should be used. It is easy to misuse (e.g. forget to log to context when it exists, which seems hard to enforce.)

I think it's straightforward to enforce if there are places where we want to require log contexts. For example if we want to enforce it in kernel files it could look like:

diff

--- a/src/util/log.h
+++ b/src/util/log.h
@@ -156,11 +156,19 @@ void Log(Level level, bool should_ratelimit, SourceLocation&& source_loc, Contex
 #define FirstArg_Impl(arg, ...) arg
 #define FirstArg_(args) FirstArg_Impl args
 
+constexpr bool LOG_REQUIRE_CONTEXT = false;
+
+template <typename T>
+concept LogContext = requires {
+    requires std::remove_reference_t<T>::log_context;
+};
+
 //! Internal helper to conditionally log. Only evaluates arguments when needed.
 // Allow __func__ to be used in any context without warnings:
 // NOLINTBEGIN(bugprone-lambda-function-name)
 #define LogPrint_(level, should_ratelimit, take_category, ...)                                     \
     do {                                                                                           \
+        static_assert(!LOG_REQUIRE_CONTEXT || LogContext<decltype(FirstArg_((__VA_ARGS__)))>, "Log call must pass a log context as first argument"); \
         auto&& _context{util::log::detail::GetContext<take_category>(FirstArg_((__VA_ARGS__)))};   \
         if (util::log::ShouldLog(_context.logger, _context.category, (level))) {                   \
             util::log::detail::Log((level), (should_ratelimit), SourceLocation{__func__},          \
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -76,6 +76,8 @@
 #include <tuple>
 #include <utility>
 
+#define LOG_REQUIRE_CONTEXT true
+
 using kernel::CCoinsStats;
 using kernel::ChainstateRole;
 using kernel::CoinStatsHashType;

Also note that without enforcement, the consequence of a missing context argument is just the log message going to the global logging stream, which already happens now.

  • non-trivial code change / review cost, lots of merge conflicts, ...

I'd be interested to know more about what part of the code change seem difficult to review. The main change is adding a new parameter to a bunch of functions, which seems like the archetype of a change that is trivial to verify. I guess there are a lot of test updates too. Anyway, would like to know more about what specific difficulties you see here.

stickies-v and others added 14 commits March 6, 2026 20:07
Test that LogInfo/LogWarning/LogError always evaluate their arguments
even when logging is disabled.

ajtowns pointed out this behavior was important and could affect non-logging
code if changed in
bitcoin#34374 (comment)

Co-authored-by: l0rinc <[email protected]>
Add new logging test to call macros with all allowed combinations of macro
arguments.

The new test replaces a less comprehensive test that doesn't cover log
statements without format arguments. It's also moved to the top of the test
suite because it's a good illustration of what typical log prints look like,
what logging calls are allowed and disallowed, and what the resulting log
output looks like.
Previously this used to be possible through the LogPrintLevel call but now that
call is removed, this change is just an internal refactoring and has no visible
effect except in tests.
Rewrite log macros to fix a number of issues: lack of control over rate
limiting, unnecessary strprintf calls during fuzzing, confusing error messages
when macros are called with the wrong arguments.

More specifically, benefits are:

- Control over rate limiting: `LogPrintLevel_` hack used to bypass rate limiting
  is dropped. A `LogPrint` macro is added that can control rate limiting (as
  well as other options if they are added in the future).

- Unnecessary `strprintf` calls are now skipped in bitcoind when
  `-noprinttoconsole -nodebuglogfile` options are used, and skipped in tests and
  kernel applications when logging is disabled. This change should not affect
  bitcoind noticeably, but it could speed up fuzz tests calling functions that
  log.

- Clearer error messages: Differences between macros accepting and not accepting
  category arguments has been a source of confusion. Now if you pass a category
  to a macro which does not accept it, or forget to pass a category to a macro
  which requires it, you will see a clear error message telling you to add or
  remove the category instead of more confusing macro errors.

- Previously you could call `LogPrintLevel_` to bypass restrictions on passing
  category arguments. Now `LogPrint` enforces all requirements.

- Previously "always evaluate arguments" behavior at Info and higher levels
  looked accidental and was undocumented
  (bitcoin#34374 (comment)). Now
  the behavior is documented and explicit.

- There is now a single macro, `LogPrint` implementing all logging functionality
  and enforcing all restrictions, instead of different sets of macros with
  branching code paths and unexplained differences.

Co-Authored-By: Hodlinator <[email protected]>
Co-Authored-By: stickies-v <[email protected]>
Allow LogDebug(), LogTrace(), LogInfo(), LogWarning(), and LogError() macros to
accept context arguments to provide more information in log messages and more
control over logging to callers.

This functionality is used in followup PRs:

- bitcoin#30342 - To let libbitcoinkernel send
  output to specfic `BCLog::Logger` instances instead of a global instance, so
  output can be disambiguated and applications can have more control over
  logging.

- bitcoin#30343 - To replace custom
  `WalletLogPrintf` calls with standard logging calls that automatically include
  wallet names and don't log everything at info level.

This commit does not change behavior of current log prints or require them to
be updated. It includes tests and documentation covering the new functionality.

Co-Authored-By: Hodlinator <[email protected]>
Co-Authored-By: stickies-v <[email protected]>
Custom log contexts allow overridding log formatting and adding metadata, such
as request ids or wallet names to log messages, while still using standard
macros for logging. This is used to replace WalletLogPrintf() functions with
LogInfo() calls in followup PR bitcoin#30343.
Pass Logger instances to BlockManager, CCoinsViewDB, CDBWrapper,
ChainstateManager, and CoinsViews instances so libbitcoinkernel applications
and test code have the option to control where log output goes instead of
having all output sent to the global logger.

This commit just passes the logger objects without using them. The next commit
updates log print statements to use the new objects.
Add option to require context objects to be passed to log macros in a file or
block of code, so new logging calls can't forget to specify them and
accidentally log to the global logging stream.
This is a mechanical change updating kernel code that currently uses the global
log instance to log to local instances instead.
Change LogInstance() function to no longer allocate (and leak) a BCLog::Logger
instance. Instead allow kernel applications to initialize their own logging
instances that can be returned by LogInstance().

The LogInstance() function is not actually used for the majority of logging in
kernel code. Most kernel log output goes directly to BCLog::Logger instances
specified when kernel objects like ChainstateManager and CTxMemPool are
constructed, which allows applications to create multiple Logger instances and
control which log output is sent to them.

The only kernel code that does rely on LogInstance() is certain low level code in
the util library, like the RNG seeder, that is not passed a specific instance
and needs to rely on the global LogInstance() function.

Other code outside the kernel library uses LogInstance() heavily, and may
continue to do so. bitcoind and test code now just create a log instance before
the first LogInstance() call, which it returns, so all behavior is the same as
before.
@stickies-v
Copy link
Contributor

stickies-v commented Mar 8, 2026

I think it's straightforward to enforce if there are places where we want to require log contexts. For example if we want to enforce it in kernel files it could look like:

Nice, I hadn't thought of a per-file approach, that seems elegant.

I'd be interested to know more about what part of the code change seem difficult to review.

I don't think (and didn't say) that this PR is difficult to review. But I think "non-trivial code change / review cost, lots of merge conflicts, ..." is a reasonable statement for a cumulative diff of this size and 26 PR merge conflicts.

Note: I do think this is an elegant implementation, and I am not necessarily opposed to the changes, I think it would be nice if kernel eventually has contextualized logging. I just don't think it's currently important as it seems users (incl myself) don't actually need this. Just making kernel logging explicitly global (which I planned to open a PR for earlier, but I've been focused on v31 review for the past weeks opened #34775) seems like the more pragmatic change to me now. It addresses the interface weirdness with a much smaller change, and keeps the logging interface simpler.

@DrahtBot
Copy link
Contributor

🐙 This pull request conflicts with the target branch and needs rebase.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants