fix(Logger): keep running remaining handlers after one returns false - #10560
Alexandros-Pallis wants to merge 1 commit into
Conversation
|
Hi there, Alexandros-Pallis! 👋 Thank you for sending this PR! We expect the following in all Pull Requests (PRs).
Important We expect all code changes or bug-fixes to be accompanied by one or more tests added to our test suite to prove the code works. If pull requests do not comply with the above, they will likely be closed. Since we are a team of volunteers, we don't have any more time to work See https://github.com/codeigniter4/CodeIgniter4/blob/develop/contributing/pull_request.md Sincerely, the mergeable bot 🤖 |
d4c3289 to
5d2635e
Compare
Logger::log() stopped executing any subsequent handlers as soon as one handler's handle() returned false. In practice every shipped handler (FileHandler, ErrorlogHandler) returns false purely to signal a write failure, not a deliberate "stop the chain" request. This meant a single FileHandler failure (e.g. bad file permissions) silently swallowed the log message for every handler configured after it, such as an ErrorlogHandler fallback. None of the shipped handlers rely on the early-exit behavior, so drop it: every configured, level-matching handler now runs regardless of what earlier handlers returned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GQ1FHzXX7To34G4mwQGN8U
5d2635e to
6ee0e62
Compare
michalsn
left a comment
There was a problem hiding this comment.
I understand why this behavior was surprising. However, HandlerInterface::handle() explicitly documents that returning false stops subsequent handlers, so this changes the existing API contract rather than fixes a bug. We can consider changing that behavior, but it should be proposed for the 4.8 branch, with backward compatibility and migration guidance addressed.
I saw that this behavior is documented, i am not fully grasping the reason of this behavior. I can bypass my issue by rearranging the $handlers inside the config and putting FileHandler last. If you decide that this behavior should change, let me know if i can do anything additional to help towards this direction. Thanks for the feedback! |
|
Let's see what others think. |
|
If we'll target 4.8 for a breaking change, what if we change the boolean return to an int return? Then, any nonzero exit can be decided whether the next handlers in queue to continue logging or abort completely. Like 1 is continue and 2 is terminate. |
That would be a big change, but probably acceptable, as not many people use their own handlers. |
|
@Alexandros-Pallis, if you're interested in implementing this the way Paul described, you're welcome to send a PR targeting the |
As a user of the framework this seems like a bug to me, i had 2 handlers defined in my $handlers config ( FileHandler and ErrorLogHandler ). The file handler failed due to file permissions on my system and the ErrorLogHandler was not working at all which confused me.
I've read that you prefer a PR rather than an Issue on the contributing docs and decided to open a PR instead.
I used AI for the fix but i have manually checked the generated code and run the tests.
Let me know if you don't consider this a bug or if i should change anything in order to be acceptable.
Description
Logger::log()stops iterating handlers as soon as one handler'shandle()call returnsfalse:In practice, every shipped handler (
FileHandler,ErrorlogHandler) returnsfalsepurely as a write-failure signal, not as a deliberate "stop the chain" request. This means a singleFileHandlerfailure (e.g. bad file permissions on the log directory) silently swallows the log message for every handler configured after it — for example anErrorlogHandlerfallback never runs, and the log entry is lost with no indication why.I ran into this directly:
FileHandler+ErrorlogHandlerconfigured in that order,FileHandlerfailed due to permissions, andErrorlogHandlernever got a chance to log anything.Fix
Drop the early-exit: every configured handler that can handle the given level now runs, regardless of what earlier handlers returned. No shipped handler relies on the early-exit behavior (confirmed by checking
FileHandler,ErrorlogHandler, andChromeLoggerHandler— none use their return value to intentionally halt the chain), so this only changes behavior for the failure case, not any documented feature.HandlerInterface::handle()'s docblock is updated to reflect the new contract.Testing
LoggerTest::testLogRunsRemainingHandlersWhenAnEarlierHandlerReturnsFalse(), using an inline anonymous handler that always returnsfalse, configured beforeTestHandler, to assert the second handler still receives the log message.tests/system/Log/suite passes (44 tests, 78 assertions).phpstan analyseandphp-cs-fixer --dry-runclean on all touched files.Checklist
develop🤖 Generated with Claude Code