logging: better use of log::Entry internally #34865

pull stickies-v wants to merge 5 commits into bitcoin:master from stickies-v:2026-03/logging-use-entry changing 4 files +62 −67
  1. stickies-v commented at 12:47 PM on March 19, 2026: contributor

    Preparatory work to enable struct-based logging for kernel (#34374), but I think it's a mild improvement by itself too.

    #34465 introduced util::log::Entry. This PR updates some internal functions and structs to make better use of that new plumbing.

    Mostly a refactor, except for very minor timing differences for timestamp and mocktime which are now uniformly captured at Entry generation.

  2. DrahtBot commented at 12:47 PM on March 19, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline for information on the review process.

    Type Reviewers
    ACK maflcko, ryanofsky, sedited

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

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #34778 (logging: rewrite macros to add ratelimit option, avoid unused strprintf, clarify confusing errors by ryanofsky)
    • #34730 (util/log: Combine the warning/error log levels into a single alert level by ajtowns)
    • #34514 (refactor: remove unnecessary std::move for trivially copyable types by l0rinc)
    • #30343 (wallet, logging: Replace WalletLogPrintf() with LogInfo() by ryanofsky)
    • #29256 (log, refactor: Allow log macros to accept context arguments 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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  3. DrahtBot added the label Needs rebase on Mar 24, 2026
  4. stickies-v force-pushed on Mar 24, 2026
  5. DrahtBot removed the label Needs rebase on Mar 24, 2026
  6. in src/logging.h:188 in 05a89da0d4
     183 | @@ -185,9 +184,8 @@ namespace BCLog {
     184 |          fs::path m_file_path;
     185 |          std::atomic<bool> m_reopen_file{false};
     186 |  
     187 | -        /** Send a string to the log output */
     188 | -        void LogPrintStr(std::string_view str, SourceLocation&& source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit)
     189 | -            EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
     190 | +        /** Send an entry to the log output */
     191 | +        void LogPrintStr(util::log::Entry&& log_entry) EXCLUSIVE_LOCKS_REQUIRED(!m_cs);
    


    ryanofsky commented at 8:23 PM on April 8, 2026:

    In commit "logging: pass log::Entry through to logging functions" (05a89da0d41b28d8ede51f8252f14b099ae8afe2)

    Since this commit is changing signatures of LogPrintStr / LogPrintStr_ methods and updating all call sites already, it seems like it would be to good choose a better name for them. When LogPrintStr was originally written it just took a single string argument so the name made sense. But over time more parameters were added and now it takes an entry struct instead of a string argument. Would suggest just calling these methods Log / Log_ but I think anything without Str would be an improvement.


    stickies-v commented at 3:10 PM on April 10, 2026:

    Updated, thanks. I opted for LogPrint{_} to disambiguate from util::log::Log() and keep the name familiar.

  7. in src/logging.cpp:437 in 05a89da0d4
     435 | +    return LogPrintStr_(std::move(entry));
     436 |  }
     437 |  
     438 |  // NOLINTNEXTLINE(misc-no-recursion)
     439 | -void BCLog::Logger::LogPrintStr_(std::string_view str, SourceLocation&& source_loc, BCLog::LogFlags category, BCLog::Level level, bool should_ratelimit)
     440 | +void BCLog::Logger::LogPrintStr_(util::log::Entry&& entry)
    


    ryanofsky commented at 8:39 PM on April 8, 2026:

    In commit "logging: pass log::Entry through to logging functions" (05a89da0d41b28d8ede51f8252f14b099ae8afe2)

    It's a little atypical for c++ functions to require their arguments to be rvalues like this. Would suggest just util::log::Entry entry so this can be called naturally and callers can decide whether to move or copy.


    stickies-v commented at 3:13 PM on April 10, 2026:

    You're right, changed to passing by value. I thought for an internal function it makes sense to make it more explicit that no copies can happen here, but it's probably not worth the awkwardness.

  8. in src/logging.cpp:413 in b94475aecd outdated
     408 | @@ -410,21 +409,23 @@ BCLog::LogRateLimiter::Status BCLog::LogRateLimiter::Consume(
     409 |      return status;
     410 |  }
     411 |  
     412 | -void BCLog::Logger::FormatLogStrInPlace(std::string& str, BCLog::LogFlags category, BCLog::Level level, const SourceLocation& source_loc, std::string_view threadname, SystemClock::time_point now, std::chrono::seconds mocktime) const
    


    ryanofsky commented at 8:55 PM on April 8, 2026:

    In commit "logging: replace FormatLogStrInPlace with Format" (b94475aecd75414f61a2a3a7f0302fcad907d12e)

    Note (in case it helps other reviewers): I was trying to figure out why FormatLogStrInPlace was written in this unusual way, taking a mutable string parameter, and building the string in reverse, adding prefixes instead of appending.

    It seems like there wasn't a real reason for this other than trying to minimize differences with the previous code when this method was introduced in 558df5c733d31456faf856d44f7037f41981d797 from #30386

  9. ryanofsky approved
  10. ryanofsky commented at 9:01 PM on April 8, 2026: contributor

    Light code review ACK b94475aecd75414f61a2a3a7f0302fcad907d12e. Changes seem reasonable and make code more readable. I do want to review more closely a second time to be more confident there aren't mistakes.

    I don't think anything including #34374 which is cited in the description actually depends on these changes, but they were originally made in #34374 and seem like good cleanups.

  11. in src/logging.h:131 in ebb0a14ace outdated
     127 | @@ -128,12 +128,8 @@ namespace BCLog {
     128 |      {
     129 |      public:
     130 |          struct BufferedLog {
     131 | -            SystemClock::time_point now;
     132 | +            util::log::Entry entry;
    


    ryanofsky commented at 9:51 PM on April 8, 2026:

    In commit "logging: make BufferedLog a log::Entry wrapper" (ebb0a14acee83a719d640002d7461938979e4342)

    One drawback of this change is that BufferedLog now contains an entry.should_ratelimit field that is unused and ignored.

    This is a minor issue and could be fixed in a pretty clean way with #34778 by passing the util::log::Options struct added in that PR alongside util::log::Entry to logging functions and then dropping the util::log::Entry::should_ratelimit field.

  12. in src/logging.cpp:463 in b94475aecd
     459 | @@ -461,10 +460,10 @@ void BCLog::Logger::LogPrintStr_(util::log::Entry&& entry)
     460 |          return;
     461 |      }
     462 |  
     463 | -    FormatLogStrInPlace(str_prefixed, static_cast<LogFlags>(entry.category), entry.level, entry.source_loc, entry.thread_name, entry.timestamp, GetMockTime());
     464 | +    std::string log_msg{Format(entry, GetMockTime())};
    


    ryanofsky commented at 10:02 PM on April 8, 2026:

    In commit "logging: replace FormatLogStrInPlace with Format" (b94475aecd75414f61a2a3a7f0302fcad907d12e)

    Diff would be a little smaller without renaming str_prefixed to log_msg, and new name does not seem obviously better to me than the old one, but no strong opinion.


    stickies-v commented at 3:00 PM on April 10, 2026:

    I don't think str_prefixed is clear, but perhaps log_msg doesn't sufficiently differentiate from Entry::message, so perhaps e.g. fmt_log_line would be best. That said, I agree it's touching a lot of new lines, so I've reverted it to str_prefixed.

  13. ryanofsky approved
  14. ryanofsky commented at 10:19 PM on April 8, 2026: contributor

    Code review ACK b94475aecd75414f61a2a3a7f0302fcad907d12e

  15. stickies-v force-pushed on Apr 10, 2026
  16. stickies-v commented at 6:06 PM on April 10, 2026: contributor

    Force-pushed to address review comments from @ryanofsky - thanks for the review!

    • reverted str_prefixed -> log_msg rename
    • renamed LogPrintStr{_} to LogPrint{_}
    • LogPrint{_} now take by value instead of rvalue ref

    I don't think anything including #34374 which is cited in the description actually depends on these changes,

    Without a Dispatcher class (which is the approach I'm pursuing for #34374, not yet pushed), it makes it a lot cleaner to have the Format function as it is here so kernel can also access the functionality in some intermediate commits while it's still coupled to BCLog::Logger. None of these commits are strictly speaking necessary for #34374 indeed, but they do make the subsequent changes easier to review, and (as it seems you agree) have merit on their own.

  17. ryanofsky commented at 12:38 PM on April 13, 2026: contributor

    Code review ACK e9123b39d4ae8f35bc8ae526122db2390633fe05. Just some name changes since last review.

    Without a Dispatcher class (which is the approach I'm pursuing for #34374, not yet pushed),

    Thanks for clarifying. Seems fine to drop the dispatcher class, although when the PR is rebased after #34465 it becomes pretty simple and is basically just a list of callbacks with a connection count. It's actually very similar to the new btcsignals::signal class and could be replaced by a signal.

  18. in src/logging.cpp:82 in 984206585f
      74 | @@ -75,7 +75,13 @@ bool BCLog::Logger::StartLogging()
      75 |      // dump buffered messages from before we opened the log
      76 |      m_buffering = false;
      77 |      if (m_buffer_lines_discarded > 0) {
      78 | -        LogPrintStr_(strprintf("Early logging buffer overflowed, %d log lines discarded.\n", m_buffer_lines_discarded), SourceLocation{__func__}, BCLog::ALL, Level::Info, /*should_ratelimit=*/false);
      79 | +        LogPrint_({
      80 | +            .category = BCLog::ALL,
      81 | +            .level = Level::Info,
      82 | +            .should_ratelimit = false,
      83 | +            .source_loc = __func__,
    


    maflcko commented at 3:39 PM on April 16, 2026:

    nit in 984206585f9f1e27897e3e55b3ddbf7bdf30b5c1: This is my fault for not making SourceLocation explicit, but I think this should remain SourceLocation{__func__}, like it was before. This way, it is clear that this is not just a single string literal pointer, but a more complex struct that captures the current source location.

    Maybe add the explicit in the prior commit? https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-explicit


    stickies-v commented at 1:03 PM on April 17, 2026:

    Will include in next force push, thanks.

  19. in src/logging.cpp:451 in 984206585f
     456 | +                .str = std::move(str_prefixed),
     457 | +                .threadname = std::move(entry.thread_name),
     458 | +                .source_loc = entry.source_loc,
     459 | +                .category = static_cast<LogFlags>(entry.category),
     460 | +                .level = entry.level,
     461 |              };
    


    maflcko commented at 3:56 PM on April 16, 2026:

    nit in 9842065: Would be nice to mark entry as moved? (void)std::move(entry) in the next line, because clang-tidy does not track partial moves, IIUC? https://godbolt.org/z/fEr4Y59vd


    maflcko commented at 4:35 PM on April 16, 2026:

    Also, it still seems confusing to have two almost identical structs here and copy them field-by-field.

    Why not just keep it a single blob?

    diff --git a/src/logging.cpp b/src/logging.cpp
    index abccc03abf..0de1f6030b 100644
    --- a/src/logging.cpp
    +++ b/src/logging.cpp
    @@ -87,4 +87,4 @@ bool BCLog::Logger::StartLogging()
             const auto& buflog = m_msgs_before_open.front();
    -        std::string s{buflog.str};
    -        FormatLogStrInPlace(s, buflog.category, buflog.level, buflog.source_loc, buflog.threadname, buflog.now, buflog.mocktime);
    +        std::string s{buflog.message};
    +        FormatLogStrInPlace(s, static_cast<LogFlags>(buflog.category), buflog.level, buflog.source_loc, buflog.thread_name, buflog.timestamp, buflog.mocktime);
             m_msgs_before_open.pop_front();
    @@ -374,7 +374,7 @@ std::string BCLog::Logger::GetLogPrefix(BCLog::LogFlags category, BCLog::Level l
     
    -static size_t MemUsage(const BCLog::Logger::BufferedLog& buflog)
    +static size_t MemUsage(const util::log::Entry& buflog)
     {
    -    return memusage::DynamicUsage(buflog.str) +
    -           memusage::DynamicUsage(buflog.threadname) +
    -           memusage::MallocUsage(sizeof(memusage::list_node<BCLog::Logger::BufferedLog>));
    +    return memusage::DynamicUsage(buflog.message) +
    +           memusage::DynamicUsage(buflog.thread_name) +
    +           memusage::MallocUsage(sizeof(memusage::list_node<util::log::Entry>));
     }
    @@ -442,13 +442,4 @@ void BCLog::Logger::LogPrint_(util::log::Entry entry)
             {
    -            BufferedLog buf{
    -                .now = entry.timestamp,
    -                .mocktime = GetMockTime(),
    -                .str = std::move(str_prefixed),
    -                .threadname = std::move(entry.thread_name),
    -                .source_loc = entry.source_loc,
    -                .category = static_cast<LogFlags>(entry.category),
    -                .level = entry.level,
    -            };
    -            m_cur_buffer_memusage += MemUsage(buf);
    -            m_msgs_before_open.push_back(std::move(buf));
    +            m_cur_buffer_memusage += MemUsage(entry);
    +            m_msgs_before_open.push_back(std::move(entry));
             }
    diff --git a/src/logging.h b/src/logging.h
    index b7d4a5f43b..bcd620d8ba 100644
    --- a/src/logging.h
    +++ b/src/logging.h
    @@ -130,12 +130,2 @@ namespace BCLog {
         {
    -    public:
    -        struct BufferedLog {
    -            SystemClock::time_point now;
    -            std::chrono::seconds mocktime;
    -            std::string str, threadname;
    -            SourceLocation source_loc;
    -            LogFlags category;
    -            Level level;
    -        };
    -
         private:
    @@ -144,3 +134,3 @@ namespace BCLog {
             FILE* m_fileout GUARDED_BY(m_cs) = nullptr;
    -        std::list<BufferedLog> m_msgs_before_open GUARDED_BY(m_cs);
    +        std::list<util::log::Entry> m_msgs_before_open GUARDED_BY(m_cs);
             bool m_buffering GUARDED_BY(m_cs) = true; //!< Buffer messages before logging can be started.
    diff --git a/src/util/log.h b/src/util/log.h
    index edf387e98b..c05751bf76 100644
    --- a/src/util/log.h
    +++ b/src/util/log.h
    @@ -57,2 +57,3 @@ struct Entry {
         SystemClock::time_point timestamp{SystemClock::now()};
    +    std::chrono::seconds mocktime{GetMockTime()};
         std::string thread_name{util::ThreadGetInternalName()};
    

    And then going further and passing it to FormatLogStrInPlace?


    stickies-v commented at 1:18 PM on April 17, 2026:

    nit in 9842065: Would be nice to mark entry as moved?

    Included in next force push, thanks. (note: it's removed again in the next commit, which already marked all of entry as moved)

    Also, it still seems confusing to have two almost identical structs here and copy them field-by-field. Why not just keep it a single blob?

    Did you see commit 23fd3690be8d0d6c6b6a0c12bd32958030d82b93? It does almost exactly what you propose, except for keeping mocktime separate as I think that's a node concern. I'm not vehemently opposed to wholesale combining both structs (and initially implemented it that way), but I do think the separation makes sense?

    And then going further and passing it to FormatLogStrInPlace?

    That's done in commit e9123b39d4ae8f35bc8ae526122db2390633fe05


    maflcko commented at 1:34 PM on April 17, 2026:

    ok, I guess I should have looked at all the commits 😅

    Maybe add to the commit message. "This will copy the entry fields to BufferedLog, but the next commit cleans this up." (or so). But up to you.


    stickies-v commented at 3:12 PM on April 17, 2026:

    Left the message as-is, I try to minimize references to other (future) commits because it makes rebasing/reordering a lot more painful and is easy to miss resulting in stale messages.

  20. in src/util/log.h:57 in d6cf7ff1b1 outdated
      53 | @@ -52,6 +54,8 @@ struct Entry {
      54 |      Category category;
      55 |      Level level;
      56 |      bool should_ratelimit{false}; //!< Hint for consumers if this entry should be ratelimited
      57 | +    SystemClock::time_point timestamp{SystemClock::now()};
    


    maflcko commented at 4:17 PM on April 16, 2026:

    nit/question: This seems a bit arbitrary, why take the time here, but not the mocktime?

    diff --git a/src/util/log.h b/src/util/log.h
    index edf387e98b..c05751bf76 100644
    --- a/src/util/log.h
    +++ b/src/util/log.h
    @@ -57,2 +57,3 @@ struct Entry {
         SystemClock::time_point timestamp{SystemClock::now()};
    +    std::chrono::seconds mocktime{GetMockTime()};
         std::string thread_name{util::ThreadGetInternalName()};
    

    stickies-v commented at 1:21 PM on April 17, 2026:

    I kept mocktime separate as I think that's a node concern rather than util (e.g. not relevant for kernel iiuc)?


    ryanofsky commented at 2:00 PM on April 17, 2026:

    re: #34865 (review)

    I kept mocktime separate as I think that's a node concern rather than util (e.g. not relevant for kernel iiuc)?

    No strong opinion, but I agree with Marco it would seem simpler to treat time and mock time the same, and I don't think I see a downside to the suggestion since this struct is not exposed by the kernel API. Anything seems fine, though


    stickies-v commented at 3:17 PM on April 17, 2026:

    Alright I've moved everything in util::log::Entry as it simplifies things and it seems there's preference for that approach. Thanks for the feedback!

  21. maflcko commented at 4:37 PM on April 16, 2026: member

    left some nits/questions

  22. util: make SourceLocation constructor explicit
    Follow CppCoreGuidelines C.46:
    https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rc-explicit
    8a55b17751
  23. util: add timestamp and thread_name to log::Entry b414913c73
  24. logging: pass log::Entry through to logging functions
    Use the entry's timestamp and thread_name, captured at the call
    site, instead of re-capturing them in the logger.
    8115001cd4
  25. logging: replace BufferedLog with log::Entry
    Avoids duplication and ensures timestamp and mocktime are captured
    at the same time.
    3b92ec2036
  26. logging: replace FormatLogStrInPlace with Format
    Return a new string built left-to-right instead of mutating one
    with repeated insert(0, ...). Take a const log::Entry ref since
    both call sites now have an Entry available. Also move
    LogEscapeMessage inside so callers don't need to pre-escape.
    c5ec2d5313
  27. stickies-v force-pushed on Apr 17, 2026
  28. stickies-v commented at 3:15 PM on April 17, 2026: contributor

    Force-pushed to address review comments from @maflcko:

    • removed BufferedLog altogether by adding mocktime to util::log::Entry, which also ensures both timestamps are captured at the same time
    • made SourceLocation explicit
    • (temporarily) marked entry as moved (undone in next commit)
  29. maflcko commented at 4:19 PM on April 17, 2026: member

    review ACK c5ec2d53130e80ce19daa98f15a688fbc6b28a3f 🚐

    <details><summary>Show signature</summary>

    Signature:

    untrusted comment: signature from minisign secret key on empty file; verify via: minisign -Vm "${path_to_any_empty_file}" -P RWTRmVTMeKV5noAMqVlsMugDDCyyTSbA3Re5AkUrhvLVln0tSaFWglOw -x "${path_to_this_whole_four_line_signature_blob}"
    RUTRmVTMeKV5npGrKx1nqXCw5zeVHdtdYURB/KlyA/LMFgpNCs+SkW9a8N95d+U4AP1RJMi+krxU1A3Yux4bpwZNLvVBKy0wLgM=
    trusted comment: review ACK c5ec2d53130e80ce19daa98f15a688fbc6b28a3f 🚐
    McxE+ssr7THhOWvCrP/iv3vxnNwJX50+aCkNjAmeeZX/0jolUtOffsP3vmvIuHZKgRUwfPjfuUSUdxMcXdEGBw==
    

    </details>

  30. DrahtBot requested review from ryanofsky on Apr 17, 2026
  31. ryanofsky approved
  32. ryanofsky commented at 9:31 PM on April 17, 2026: contributor

    Code review ACK c5ec2d53130e80ce19daa98f15a688fbc6b28a3f. Just suggested changes since last review: adding (void) std::move, making SourceLocation constructor explicit, dropping BufferedLog struct. Overall PR is a nice code simplification, and I don't think it has downsides other than buffered logs now containing ignored should_ratelimit fields, which can be fixed in a followup by separating log entries from log options.

  33. sedited approved
  34. sedited commented at 10:24 AM on April 23, 2026: contributor

    ACK c5ec2d53130e80ce19daa98f15a688fbc6b28a3f

  35. sedited merged this on Apr 23, 2026
  36. sedited closed this on Apr 23, 2026

  37. stickies-v deleted the branch on Apr 23, 2026

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-05-01 00:12 UTC

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