kernel: use structured logging and simplify logging interface #34374

pull stickies-v wants to merge 10 commits into bitcoin:master from stickies-v:2026-01/kernel-logging-layering-predicate changing 50 files +837 −459
  1. stickies-v commented at 7:56 pm on January 21, 2026: contributor

    tl;dr: kernel logging is cumbersome. This PR enables structured logging and a much simplified kernel logging interface. Closes #34062.

    Motivation

    The bitcoinkernel library (#27587) exposes functionality to interface with the kernel logging. This includes registering callbacks for log statements, level/category filtering, string formatting options, and more.

    Kernel logging has a few problems:

    • callbacks operate on formatted strings, so users need to parse the string to get the timestamp, category, level, … based on which options are set. This is cumbersome, brittle, and inefficient.
    • the filtering interface is not really intuitive, requiring users to call combinations of btck_logging_set_level_category and btck_logging_enable_category when they want to produce debug or trace logs. The level/category system makes sense for node, because it directly controls what gets written to disk and stdout, and there are quite a lot more categories producing logs. Kernel doesn’t really need this - users control what happens to the logs, and can do any filtering/manipulation in the callback they provide.
    • the node logging infrastructure has quite a bit more functionality than is necessary for a library, including ratelimiting, log formatting, outputting, buffering, … This introduces unnecessary code and interface complexity.

    Approach

    Most of the logic in logging.h is not necessary for bitcoinkernel, so we first separate it into util/log.h and logging.h (common), then upgrade the bitcoinkernel logging interface, and finally remove the logging.cpp dependency from kernel entirely.

    1. Separate log generation: Add a minimal util::log::Dispatcher that is used to generate logs, and forwards structured log entries to registered callbacks. No formatting, rate-limiting, or output handling - that’s the consumer’s responsibility. Dispatcher can be configured with a filter predicate (e.g. WillLogCategoryLevel() for node, or a simpler levels-based filter for bitcoinkernel) to avoid unnecessary string formatting and other overhead.
    2. Register BCLog::Logger on Dispatcher: Register a callback on the global Dispatcher to receive entries and handle node-specific concerns (formatting, rate-limiting, file output) without changing most of its logic.
    3. Move logging macros to util/log.h: Allows the entire codebase (including kernel code) to use the same macros without depending on logging.h.
    4. Update bitcoinkernel C API: Replace the string-based callback with btck_LogEntry containing full metadata. Remove btck_LoggingOptions and category enable/disable functions. Add btck_logging_set_min_level() for early filtering.
    5. Move logging.cpp to common: remove it as a kernel dependency entirely.

    Appendix

    bitcoinkernel C logging interface

     0struct btck_LogEntry {
     1    const char* message;       //!< Log message
     2    size_t message_len;        //!< Log message length
     3    const char* file_name;     //!< Source file name
     4    size_t file_name_len;      //!< Source file name length
     5    const char* function_name; //!< Source function name
     6    size_t function_name_len;  //!< Source function name length
     7    const char* thread_name;   //!< Thread name
     8    size_t thread_name_len;    //!< Thread name length
     9    int64_t timestamp_ns;      //!< Timestamp in nanoseconds since epoch
    10    uint32_t line;             //!< Source line number
    11    btck_LogLevel level;       //!< Log level
    12    btck_LogCategory category; //!< Log category
    13};
    14
    15typedef void (*btck_LogCallback)(void* user_data, const btck_LogEntry* entry);
    16
    17BITCOINKERNEL_API void btck_logging_set_min_level(btck_LogLevel level);
    18
    19BITCOINKERNEL_API btck_LoggingConnection* BITCOINKERNEL_WARN_UNUSED_RESULT btck_logging_connection_create(
    20    btck_LogCallback log_callback,
    21    void* user_data,
    22    btck_DestroyCallback user_data_destroy_callback) BITCOINKERNEL_ARG_NONNULL(1);
    23
    24BITCOINKERNEL_API void btck_logging_connection_destroy(btck_LoggingConnection* logging_connection);
    
  2. DrahtBot added the label Validation on Jan 21, 2026
  3. DrahtBot commented at 7:56 pm on January 21, 2026: contributor

    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/34374.

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34341 (wallet: only log txid when -debug=wallet is explicit by l0rinc)
    • #34176 (wallet: crash fix, handle non-writable db directories by furszy)
    • #34038 (logging: API improvements by ajtowns)
    • #33878 (refactor, docs: Embedded ASMap [2/3]: Refactor asmap internals and add documentation by fjahr)
    • #33822 (kernel: Add block header support and validation by yuvicc)
    • #33646 (log: check fclose() results and report safely in logging.cpp by cedwies)
    • #33512 (coins: use number of dirty cache entries in flush warnings/logs by l0rinc)
    • #31644 (leveldb: show non-default options during init by l0rinc)
    • #30343 (wallet, logging: Replace WalletLogPrintf() with LogInfo() by ryanofsky)
    • #29700 (kernel, refactor: return error status on all fatal errors by ryanofsky)
    • #29256 (log, refactor: Allow log macros to accept context arguments by ryanofsky)
    • #28792 (build: Embedded ASMap [3/3]: Build binary dump header file by fjahr)
    • #28690 (build: Introduce internal kernel library by sedited)
    • #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 places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • LogPrintLevel_(BCLog::LogFlags::ALL, util::log::Level::Info, /should_ratelimit=/false, “%s%s: new best=%s height=%d version=0x%08x log2_work=%f tx=%lu date=’%s’ progress=%f cache=%.1fMiB(%utxo)%s\n”, …) in src/validation.cpp

    2026-01-26 19:57:05

  4. stickies-v force-pushed on Jan 21, 2026
  5. DrahtBot added the label CI failed on Jan 21, 2026
  6. DrahtBot commented at 8:16 pm on January 21, 2026: contributor

    🚧 At least one of the CI tasks failed. Task tidy: https://github.com/bitcoin/bitcoin/actions/runs/21223722631/job/61065673932 LLM reason (✨ experimental): Linker errors due to unresolved detail::InitLogger() from logging.cpp.o, causing the build/test script to fail.

    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.

  7. stickies-v force-pushed on Jan 21, 2026
  8. DrahtBot removed the label CI failed on Jan 21, 2026
  9. ?
    added_to_project_v2 sedited
  10. ?
    project_v2_item_status_changed github-project-automation[bot]
  11. ?
    project_v2_item_status_changed sedited
  12. stickies-v force-pushed on Jan 22, 2026
  13. in src/logging.h:317 in 0d075e3ff6
    319+ * from a raw pointer to a std::unique_ptr.
    320+ *
    321+ * This method of initialization was originally introduced in
    322+ * ee3374234c60aba2cc4c5cd5cac1c0aefc2d817c.
    323+ */
    324+inline BCLog::Logger* g_logger{InitLogger()};
    


    ajtowns commented at 10:48 am on January 23, 2026:

    This seems like a regression. With this approach you can’t assume LogInfo called during global initialization won’t be called prior to InitLogger(), as it now only needs to pass through GetDispatcher() and g_dispatcher().

    As far as I can see, g_dispatcher() should just have independent implementations for bitcoin-kernel vs bitcoind, bitcoin-qt, bitcoin-cli etc.


    stickies-v commented at 4:37 pm on January 23, 2026:

    Yes, I think that’s a much better approach, thanks for the suggestion. I’ll incorpocate that and update shortly.

    As far as I can see, g_dispatcher() should just have independent implementations for bitcoin-kernel vs bitcoind, bitcoin-qt, bitcoin-cli etc.

    I think it should suffice to declare g_dispatcher() in util/log.h, and have one implementation in bitcoinkernel.cpp, and one in logging.cpp (which just calls LogInstance().GetDispatcher()). All non-kernel binaries can (at least for now) just use the same implementation, I think? Lmk if I’m misunderstanding something.


    stickies-v commented at 8:37 pm on January 26, 2026:
    I’ve changed the approach to your suggestion, so marking this as resolved.
  14. in src/util/log.h:78 in 0d075e3ff6 outdated
    73+    //! Type for callbacks invoked for each log entry that passes filtering.
    74+    using Callback = std::function<void(const Entry&)>;
    75+    //! Type for opaque handles returned by RegisterCallback(), used to unregister.
    76+    using CallbackHandle = std::list<Callback>::iterator;
    77+    //! Type for predicates called before logging; returns true if entry should be dispatched.
    78+    using FilterFunc = std::function<bool(Level level, uint64_t category)>;
    


    ajtowns commented at 10:50 am on January 23, 2026:
    Jumping through a std::function when we could just be checking an atomic bitfield doesn’t seem like a good approach.

    stickies-v commented at 2:03 pm on January 23, 2026:

    when we could just be checking an atomic bitfield

    I think that’s only possible if we either 1) have util::log::Dispatcher expose all of BCLog::Logger’s category/level setting logic, or 2) make Dispatcher::WillLog virtual and let each logging sink create its own Dispatcher subtype. Were you thinking of another approach?

    I don’t think either approach is warranted to justify the minimal overhead that std::function incurs. 1) would just make Dispatcher’s scope way too big in my view, and 2) would require inheritance and still have some (although probably less) overhead from the virtual table lookup.

  15. DrahtBot added the label Needs rebase on Jan 23, 2026
  16. util: add log::Dispatcher for structured log dispatch
    Add a minimal, callback-based dispatcher that forwards structured log
    entries to registered callbacks. This provides the foundation for
    separating log generation from log consumption, enabling different
    consumers (node sink, kernel C API) to receive the same log entries.
    The Dispatcher handles optional filtering and message formatting, while
    consumers handle rate limiting and output.
    c30562fd6b
  17. stickies-v force-pushed on Jan 26, 2026
  18. stickies-v force-pushed on Jan 26, 2026
  19. DrahtBot added the label CI failed on Jan 26, 2026
  20. DrahtBot commented at 6:51 pm on January 26, 2026: contributor

    🚧 At least one of the CI tasks failed. Task iwyu: https://github.com/bitcoin/bitcoin/actions/runs/21368395725/job/61506234805 LLM reason (✨ experimental): IWYU reported include-what-you-use changes and caused the CI step to fail.

    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.

  21. logging: add Dispatcher to Logger and introduce global dispatcher
    Preparatory work for the next commit where the logging macros are moved
    to the util library. The macros require a single global entry point to
    dispatch log entries.
    
    Make BCLog::Logger aware of util::log::Dispatcher by giving it ownership
    of a Dispatcher instance which it can register callbacks on.
    
    Currently, only a single sink (i.e. BCLog::Logger) exists, but in future
    commits this will change as kernel removes its dependency on logging.cpp
    and introduces its own global logging sink.
    550f121c96
  22. logging: route log macros through Dispatcher
    Log macros now call util::log::g_dispatcher().Log() directly instead of
    going through LogInstance(). BCLog::Logger registers a callback on its
    Dispatcher to receive entries and handle formatting, rate limiting, and
    output.
    9e0ec4aeee
  23. kernel: add log level/category name getters
    Expose btck_log_level_get_name and btck_log_category_get_name to allow
    library users to get string representations of log levels and categories.
    
    Use constexpr lookup tables as a single source of truth for mapping
    btck types to both BCLog equivalents and string names.
    80542be0c9
  24. kernel: implement structured logging C API
    Replace the string-based logging callback with btck_LogEntry containing
    full metadata (message, source location, timestamp, level, category).
    This lets consumers format and filter logs as needed.
    
    Use the global log dispatcher instead of BCLog::Logger's buffered
    callback system. Messages are discarded when no callbacks are registered.
    
    Add WARNING/ERROR log levels and ESTIMATEFEE/TXPACKAGES categories.
    edc3c4f2a2
  25. move-only: move logging categories to separate header
    Logging categories are currently shared between node and kernel. This
    separation allows future commits to completely remove kernel's
    dependency on logging.h.
    
    Also applies clang-format suggestions to the moved code.
    
    Review with --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space
    1043cd7321
  26. logging: move LogAcceptCategory to util
    Both kernel and node logic use this function to check whether
    a log statement will actually be produced. Move it to util so kernel
    code can continue using it without requiring logging.h.
    857f8c66dd
  27. logging: move macros to util
    Move logging macros to util/log.h so the entire codebase can use the same
    macros. Since (un)conditional logging is a node concept, it is no longer
    mentioned in the util macros. BCLog::Logger still maintains its logic to
    ensure the log level never drops below Info.
    
    LogAcceptCategory is now called for all logging levels as an early-exit
    check, not just for Debug and Trace.
    06152995e2
  28. logging: use util/log.h where possible
    Preparation for a future commit where kernel's dependency
    on logging.cpp is removed completely.
    
    Replace usage of logging\.h with util/log\.h where it
    suffices, and fix wrong includes according to iwyu.
    8c85a7c94a
  29. kernel: implement levels-based logging C API
    Because logging is now structured, bitcoinkernel users can implement
    their own filtering logic using the btck_LogEntry fields.
    
    Simplify the interface by replacing the category/levels setters with
    a single global btck_logging_set_min_level function.
    
    Implements a kernel-specific g_dispatcher with the levels-based
    filtering, so kernel's dependency on logging.cpp is now removed
    entirely.
    a4e8f3c8d6
  30. stickies-v force-pushed on Jan 26, 2026
  31. DrahtBot removed the label Needs rebase on Jan 26, 2026
  32. stickies-v commented at 8:36 pm on January 26, 2026: contributor

    Force-pushed to:

    • address merge conflict from #33822
    • address @ajtowns comment: removed SetDispatcher and GetDispatcher and instead implemented different g_dispatcher functions for logging.cpp and bitcoinkernel.cpp. This required quite a bit of code re-organization, including splitting out the kernel API changes into multiple commits (one for structured logging, one for levels-based filtering) - which I think improve the overall commit organization, but will make range-diff reviews pretty hard for this force-push.
    • improved logging callback handling: checking for nullptr and catching exceptions.
    • use BCLog::Logger::Enabled() in the filter function
    • address drahtbot linter suggestions, incl typo and named args, and a couple of other small touch-ups here and there.
  33. DrahtBot removed the label CI failed on Jan 26, 2026
  34. purpleKarrot commented at 5:32 am on January 27, 2026: contributor

    I doubt that there is an actual use case for multiple logging connections. The logger is global internally and can be handled as global by clients as well (no need for userdata). Consider modelling the logging API after set_terminate in the C++ standard library:

    0typedef void (*btck_LogHandler)(btck_LogEntry const* entry);
    1
    2BITCOINKERNEL_API btck_LogHandler btck_set_log(btck_LogHandler handler);
    

    I also think that clients are able to apply their own filtering, so the btck_logging_set_min_level function can be removed. It is worth noting that this function was never designed to filter per-connection, which was leaking the implementation detail that the logger is global.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-01-27 06:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me