8000 log, refactor: Allow log macros to accept context arguments by ryanofsky · Pull Request #29256 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

log, refactor: Allow log macros to accept context arguments #29256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

ryanofsky
Copy link
Contributor
@ryanofsky ryanofsky commented Jan 16, 2024

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:

  • #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.

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

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


Note: Originally this PR also removed some restrictions around passing category constants to log macros to try to make them more consistent, but these changes were too controversial and have been dropped.

@DrahtBot
Copy link
Contributor
DrahtBot commented Jan 16, 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/29256.

Reviews

See the guideline for information on the review process.

Type Reviewers
Concept NACK ajtowns, hodlinator
Concept ACK TheCharlatan, jonatack

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #32604 (log: Mitigate disk filling attacks by rate limiting LogPrintf, LogInfo, LogWarning, LogError by Crypt-iQ)
  • #29641 (scripted-diff: Use LogInfo over LogPrintf [WIP, NOMERGE, DRAFT] by maflcko)

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.

@ajtowns
Copy link
Contributor
ajtowns commented Jan 16, 2024

Concept NACK from me, this seems much uglier and trickier to work with. I've already written in depth about why the current approach makes sense, so I'll be brief here.

Make them always accept log categories to make it possible to only log messages from a particular component.

Being able to avoid logging critical messages is adding a bug, not a feature.

Make them less verbose by not requiring BCLog category constants to be specified in individual log prints

"+841 -626" and adding a dummy parameter to most calls does not make things less verbose, and it's also much harder to search for net related logging when the individual log statements just say m_log rather than BCLog::NET.

Make them compatible with wallet logging, which includes the wallet name in log messages

The only thing needed here is renaming from Printf to Info...

@ryanofsky
Copy link
Contributor Author
ryanofsky commented Jan 17, 2024

Hi AJ, this is a draft, and there will be some more changes which may address your concerns.

Make them always accept log categories to make it possible to only log messages from a particular component.

Being able to avoid logging critical messages is adding a bug, not a feature.

Agree, but the idea here is not to discard log messages, the idea just is to attach meaningful metadata to log messages so they can be filtered by component.

Make them less verbose by not requiring BCLog category constants to be specified in individual log prints

"+841 -626" and adding a dummy parameter to most calls does not make things less verbose, and it's also much harder to search for net related logging when the individual log statements just say m_log rather than BCLog::NET.

I can probably make the description clearer, but the idea here is that this will make the code less noisy:

-LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n");
+LogWarning(log, "getsockname failed\n");

It is true this sacrifices ability to grep by category constants in the source code, and maybe in your judgement that is an unacceptable cost? But in my opinion, if we ever using same category constants in disparate parts of source code, or different category constants in the same part of the source code, it means source code is badly organized and we should fix that instead of resorting to a coping mechanism of searching for constants instead of searching by source path. In my experience, I've haven't used logging frameworks that have encouraged category constants to be scattered and mixed like this, and I don't think it is a good idea.

The "+841 -626" refactoring is definitely 💩-y and I plan to drop it from this PR. The change to the log macros is meant to facilitate that refactoring, not the other way around. I do think we should stop relying on the global logging instance for libbitcoinkernel code, but probably can keep using it elsewhere.

Make them compatible with wallet logging, which includes the wallet name in log messages

The only thing needed here is renaming from Printf to Info...

Probably something is lost in communication here, but the idea is to let wallet code use same LogDebug(), LogTrace(), LogInfo(), LogWarning(), LogError() macros other code uses. It just adds a little formatting hook so the wallet name is automatically included when the log source is the wallet. This way full functionality of #29256 is available to the wallet and we can drop WalletLogPrintf and not have dedicated wallet logging functions.

@DrahtBot
Copy link
< 8000 /div>
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/20586125687

@ajtowns
Copy link
Contributor
ajtowns commented Jan 19, 2024

Agree, but the idea here is not to discard log messages, the idea just is to attach meaningful metadata to log messages so they can be filtered by component.

Filtering is discarding.

If you just want to find log messages, that what's grep is for, and if you want to make it more fine-grained than "hey this is an important log message/warning/error", that's what -logsourcelocations is for.

If it's any help, I think it's better to think of the sections as not related to which section of the code they appear in so much (again, that's what -logsourcelocations is for), but as a subset of the "debug" part, in that they let you filter/discard various parts of the full debug log that you would get with -debug=1.

-LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n");
+LogWarning(log, "getsockname failed\n");

The current code there is noisy because it uses LogPrintLevel. The change you're actually making is:

-LogWarning("getsockname failed\n");
+LogWarning(log, "getsockname failed\n");

which is adding characters, without adding any information to the reader.

It is true this sacrifices ability to grep by category constants in the source code, and maybe in your judgement that is an unacceptable cost?

I think LogDebug(BCLog::NET, "oh no, it all went wrong\n") is better than LogDebug(m_log, "oh no, it all went wrong). All the characters in the former are doing something useful; m_log in the latter isn't (if it weren't for macro magic, we'd write m_log.Debug("...") instead, which would be okay at least). I don't think moving the category away from the line of code it's affecting is very useful; and if you want to group all the logs from an individual file together (the way putting m_log{BCLog::NET} at the top of the file/class does), then, again, that's what logsourcelocations is for...

But in my opinion, if we ever using same category constants in disparate parts of source code, or different category constants in the same part of the source code, it means source code is badly organized and we should fix that instead of resorting to a coping mechanism of searching for constants instead of searching by source path.

That's something that already happens: BCLog::NET is in timedata and headersync and banman and net and net_processing; BCLog::VALIDATION is in validation and validationinterface and flatfile (thought there was a pr to fix that one) and signet; BCLog::MEMPOOL is in txmempool, validation and net_processing. It doesn't mean the source code is badly organised, it means different parts of the source are generating related log messages.

In my experience, I've haven't used logging frameworks that have encouraged category constants to be scattered and mixed like this, and I don't think it is a good idea.

I don't think there is anything here "encouraging" these categories to be scattered or mixed; most of them are specific to a small set of related files: TOR:torcontrol, HTTP:httpserver, ZMQ:zmq/, WALLETDB:wallet/, ESTIMATEFEE:policy/fees.cpp, SELECTCOINS:wallet/coinselection, REINDEX:validation, CMPCTBLOCK:blockencodings, RAND:random.cpp, etc.

And you obviously have worked with code designed this way: that's how LogPrint has worked in this codebase since it was implemented in #3009.

Probably something is lost in communication here, but the idea is to let wallet code use same LogDebug(), LogTrace(), LogInfo(), LogWarning(), LogError() macros other code uses.

Updating TraceSqlCallback to call LogTrace rather than LogPrintf would be an improvement as those messages would get categorised properly; but otherwise this seems like massive overkill just to avoid writing "[%s] ...", walletname, ....

It just adds a little formatting hook so the wallet name is automatically included when the log source is the wallet. This way full functionality of #29256 is available to the wallet and we can drop WalletLogPrintf and not have dedicated wallet logging functions.

A +190 -109 diff because you don't like a one-line wrapper function seems crazy to me.

Do you have some reason to think there's large amounts of additional wallet debug logs that would be introduced if it didn't involve writing [%s], walletname or are there a bunch of the current unconditional WalletLogPrintf calls that should be silenced and only available when debugging is enabled?

ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Aug 7, 2024
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Aug 7, 2024
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Dec 9, 2024
@ryanofsky ryanofsky force-pushed the pr/bclog branch 2 times, most recently from 48b3c95 to 5db1958 Compare December 9, 2024 20:29
@ryanofsky
Copy link
Contributor Author

Rebased 46f5e27 -> 48b3c95 (pr/bclog.20 -> pr/bclog.21, compare) due to conflicts with with #31174 and other PRs.
Updated 48b3c95 -> 5db1958 (pr/bclog.21 -> pr/bclog.22, compare) to fix MSVC error C3495 https://github.com/bitcoin/bitcoin/actions/runs/12242607762/job/34150305407?pr=29256

If reviewers agree with previous feedback that allowing category arguments to be passed to LogError/LogWarning/LogInfo macros is dangerous because it encourages developers to add log spam and encourages users to ignore important messages, I can add commit 0b5cbf9 here so specifying categories will remain forbidden. Would prefer not to do this because providing a consistent API and providing context with log messages seem like tangible benefits to me, while the current restrictions seem like an indirect and ineffective solution to log spam, but I could live with the restrictions if having them unblocks #30342 and #30343.

Copy link
Contributor
@hodlinator hodlinator left a comment

Choose a reason for hiding this comment

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

Code reviewed 5db1958 + 0b5cbf9

PR style

I think it would be clearer to introduce the logging_source_args-test by itself in an initial commit without any behavioral changes to the logging system, to then have following commits demonstrate how the output changes.

Subjective title

The PR title (and commit messages) is/are misleading/heavily subjective:
"Improve new LogDebug/Trace/Info/Warning/Error Macros"
More objective:
"Make LogInfo/Warning/Error accept category and stop requiring it for LogDebug/Trace"

But that title doesn't manage to also squeeze in what I think is the undisputedly useful part here - the log-source concept (required by child PRs #30342 and #30343). I wish that would be introduced without the other API changes.

Suggested change

0b5cbf9 disallowing categories for high priority levels does not go far enough, it is even more important that categories still be required for Debug/Trace IMO, to prevent spamming across categories. Took a stab at implementing that in additional commit 86c7b57 cebc9e2. (It also resets the category for high priority levels).

With my change the PR title could more or less be changed to "Add log source and non-global logging support".

To me it still feels like we should live with the consciously made API decisions from #28318 a bit longer without having to change them.

ryanofsky and others added 4 commits April 3, 2025 05:54
Test will be extended in next commit to cover support for context objects.
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 <172445034+hodlinator@users.noreply.github.com>
< 6D40 input type="hidden" name="disable_live_updates" value="false" autocomplete="off" data-targets="batch-deferred-content.inputs" />
Disallow passing BCLog category constants to LogInfo(), LogWarning(), and
LogError() macros, and disallow omitting BCLog categories when calling
LogDebug() and LogTrace() macros.

These restrictions have existed since the logging macros were added in bitcoin#28318
and not technically neccessary, but are believed to be useful to prevent log
spam and prevent users from filtering out important messages based on category.

Co-Authored-By: Hodlinator <172445034+hodlinator@users.noreply.github.com>
@ryanofsky ryanofsky changed the title Improve new LogDebug/Trace/Info/Warning/Error Macros log, refactor: Allow log macros to accept context arguments Apr 3, 2025
@ryanofsky ryanofsky force-pushed the pr/bclog branch 2 times, most recently from 07295ff to 2ebcc54 Compare April 3, 2025 17:36
@DrahtBot
Copy link
Contributor
DrahtBot commented Apr 3, 2025

🚧 At least one of the CI tasks failed.
Debug: https://github.com/bitcoin/bitcoin/runs/39937528078

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 Author
@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Rebased 5db1958 -> 07295ff (pr/bclog.22 -> pr/bclog.23, compare) with suggested changes adding back #28318 restrictions.
Updated 07295ff -> 2ebcc54 (pr/bclog.23 -> pr/bclog.24, compare) to fix CI "no return statement in function" errors from GCC
Updated 2ebcc54 -> 176ad35 (pr/bclog.24 -> pr/bclog.25, compare) to fix CI errors from older clang versions and add new unit test in separate commit as suggested.
Updated 176ad35 -> 093d58e (pr/bclog.25 -> pr/bclog.26, compare) with minor tweaks to declarations and commit messages.


re: #29256 (review)

Thanks @hodlinator. I really appreciate you figuring out how to implement the restrictions from #28318. Even though I don't like them, I don't think they add a very big cost, so I've incorporated them to help narrow the scope of this PR and unblock it.

I'm still unable to wrap my head around the appeal of a logging framework with an inconsistent API that yells at you if you supply too much context for important messages, and yells at you if you give too little context for less important messages. I can follow the logic which leads to the conclusion that the restrictions are good, but it's not compelling to me, and seems like it just makes the logging framework jankier for developers and users and different from logging frameworks that are well regarded anywhere else. But dropping the restrictions is not the main goal of this PR, so I'm ok with keeping them.

I think it would be clearer to introduce the logging_source_args-test by itself in an initial commit without any behavioral changes to the logging system, to then have following commits demonstrate how the output changes.

Missed this suggestion but will implement in the next push. Need to fix some issues happening in CI anyway. (UPDATE: Implemented this suggestion now).

@ryanofsky ryanofsky force-pushed the pr/bclog branch 2 times, most recently from 176ad35 to 093d58e Compare April 4, 2025 03:35
@DrahtBot DrahtBot removed the CI failed label Apr 4, 2025
Copy link
Contributor
@hodlinator hodlinator left a comment

Choose a reason for hiding this comment

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

Code reviewed 093d58e

Thank you for changing the end result of this PR to abide by the API decisions of #28318!

(Still think it's a bit odd to open up the gates in eaac991 to then return to closer to master behavior in 093d58e. But no blocker).

While the API has been "reverted", I still think there's a shift in behavior (output) - see inline comments.

(Sorry I didn't get to this sooner, somehow only registered that you had rebased before looking back at this today).

Comment on lines +99 to +101
"[net:error] error arg\n",
"[net:warning] warning arg\n",
"[net:info] info arg\n",
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. This still goes against the intent of logging: Simplify API for level based logging #28318 in my opinion, by adding categories to output of LogError/LogWarning/LogInfo when used with context.

  2. This also changes behavior insofar as adding [.*info] -prefixes for LogInfo(), which were intentionally avoided in logging: Simplify API for level based logging #28318:

LogInfo("foo8: %s\n", "bar8");
LogWarning("foo9: %s\n", "bar9");
LogError("foo10: %s\n", "bar10");
std::ifstream file{tmp_log_path};
std::vector<std::string> log_lines;
for (std::string log; std::getline(file, log);) {
log_lines.push_back(log);
}
std::vector<std::string> expected = {
"[net] foo7: bar7",
"foo8: bar8",

See other comment about suggested change to LogPrint_() to address both above points.


  1. Especially LogInfo() without any prefix would feel less "naked" if you were to import the test with CustomLogContext from fea5da1 (wallet, logging: Replace WalletLogPrintf() with LogInfo() #30343) into this PR to demonstrate usage. Doing so would also give a greater sense of completeness here IMO.

logger.PushBackCallback([&](const std::string& s) { messages.push_back(s); });

BCLog::Context log{logger, BCLog::NET};
LogError(log, "error %s\n", "arg");
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Superfluous newlines here and in the other new test case below:

Suggested change
LogError(log, "error %s\n", "arg");
LogError(log, "error %s", "arg");

const auto& func = __func__; \
BCLog::detail::Format([&](auto&& message) { \
ctx.logger.LogPrintStr(message, func, __FILE__, __LINE__, \
ctx.category, (level)); \
Copy link
Contributor

Choose a reason for hiding this comment

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

LogPrint_() - Highly suggest one remaining change to maintain output behavior for LogError/LogWarning/LogInfo when used with context:

Suggested change
ctx.category, (level)); \
take_category ? ctx.category : BCLog::LogFlags::ALL, \
(level)); \

One can still use the escape hatch of LogPrintLevel() or LogPrintStr() if one wants things like [net:info] -prefixes.

Looking back, this is a reinvention of that part of my change in cebc9e2 from #29256 (review). Curious to at least hear your thoughts. Not a definite blocker as context-support is after all something new.

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.

8 participants
0