Third-party error tracking software - e.g. https://drupal.org/project/raven - can extract metadata from a raw PHP exception. So it would be helpful to pass the raw exception to loggers.
Comment | File | Size | Author |
---|---|---|---|
#59 | 2949419_59.patch | 7.1 KB | mfb |
Third-party error tracking software - e.g. https://drupal.org/project/raven - can extract metadata from a raw PHP exception. So it would be helpful to pass the raw exception to loggers.
Comment | File | Size | Author |
---|---|---|---|
#59 | 2949419_59.patch | 7.1 KB | mfb |
Comments
Comment #3
mfbUnset exception in various places where backtrace is also unset.
Comment #5
mfbComment #6
mfbComment #7
dawehnerOh I see, so we remove it in every place where we pass it to t(), but not when we actually do logging. This makes 100% sense.
I went through all the calls to
Error::decodeException()
and I was not able to find a case where we unset in the case I did not to see unsetting and the other way round.Comment #8
mfbYes, not very elegant but same treatment as backtrace (perhaps could be refactored with a new method if we want elegance).
This probably deserves a change record as it's an addition to the internal API.
Comment #10
dawehnerYeah let's do that :)
Comment #11
mfbDraft change record: https://www.drupal.org/node/2950598
Comment #12
mfbComment #13
dawehnerThank you @mfb!
Comment #14
alexpottThe docs for the $error param need updating.
This comment looks like it needs updating.
Could do with some test coverage of this change.
Comment #15
mfbFixed docs and comment; have to figure out where the test coverage for this belongs..
Comment #16
jibran#2954126: The return value of Error::decodeException should never be passed to LoggerInterface::log. is related.
Comment #17
mfbok I didn't find any test coverage of _drupal_log_error() - anyone know where this should go?
Comment #18
mfbThis would also resolve #2351099: watchdog_exception() hides the exception from logger implementations since the "decoded exception" will include the raw exception.
Comment #19
mfbSetting to needs review to see if anyone has a suggestion for where I could add more test coverage.
Comment #20
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedI believe the fix I proposed in #2954126: The return value of Error::decodeException should never be passed to LoggerInterface::log. was the exact opposite of this issue, provide less context to loggers :)
What I found was a PSR3 logger implementation like monolog completely blows up when huge amounts of data is sent via context. Adding extra information to the context would essentially mean less log messages are stored. The equivalent issue in the monolog queue is here: #2954130: Mitigate huge values for $context due to core adding backtrace information to logs..
I'm not sure what the solution is: some logging implementations can make use of this info, others break when it's provided.
I think it's a bit of a smell that we start adding extra information to the context in core, then immediately unset it in dblog. Who is to say other logging implementations don't need to apply the same treatment and unset these variables too? Why should dblog even have to unset this in the first place?
My current feeling on this is that passing all this extra data isn't in line with the spirit of the interface and we should provide some kind of adapter to the interface which provides a more structured approach to accessing additional metadata like exception objects and backtraces.
Comment #21
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedSpitballing a possible approach to #20, perhaps an
ErrorAwareLoggerInterface
with a signature like:Comment #22
dawehnerGiven there is PSR-3 I'm wondering how others handle that? Monolog allows you to set is up, but it is not enabled: https://github.com/Seldaek/monolog/blob/7405bb5d673df9ee05698876843e85b0...
Given that: Could we add something like a container parameter to enable that?
Comment #23
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedIn core we're expecting monolog to normalize the full output of
\Exception::getTrace
, that's what we're calling inError::decodeException
.The code linked above is slightly different because when it encounters an exception, it'll normalize it with
\Exception::getTraceAsString
which is obviously way less taxing than iterating an entire callstack and attempting to normalize the variables for every frame in that stack.It looks like, since monolog knows how to handle exceptions, but not huge megabyte arrays, maybe 'backtrace' should be removed entirely in favour of an 'exception' (which can be queried for the backtrace or backtrace as a string if that's useful).
Comment #24
mfbIt seems we agree that the raw exception should be passed to loggers?
As far as the raw backtrace, it might be good to keep this issue focused on making the exception/throwable object available to loggers :) but I would add that the raw backtrace is useful when there isn't one otherwise (e.g. a PHP error as opposed to exception), and also because drupal actually modifies the backtrace in Error::decodeException()...
Comment #25
dawehner@mfb
This would mean that logger implementations would need to distinct between the two cases, right? You as one of them, do you think that could be a problem?
Comment #26
mfb@dawehner Sorry but I'm unclear what you mean re: the two cases... but here are my thoughts in case it helps.
I would say we want to provide the backtrace array in at least those cases when there is no exception object (like regular old PHP errors), so that we have a backtrace to send to any error tracking system that accepts it.
If we remove it in the case of exceptions, and just pass the exception object, that has the downside of losing the nice "cleaned up" backtrace provided by Error::decodeException(). It's nice to send the cleaned up backtrace to the logger implementation as we are now, as this is more efficient than having core cleanup the backtrace followed by the logger doing its own cleanup.
[EDIT: fixed typo]
Comment #28
nterbogt CreditAttribution: nterbogt at Flight Centre Travel Group commentedI've found a similar issue in the Drush logger channel.
https://github.com/drush-ops/drush/issues/3683
DbLog works around the problem with an unset($context['backtrace'])... so it appears it's a systemic issue.
Comment #29
mfbAccording to https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logg...
So I think that for logger implementations, checking the context array and ignoring anything you don't want, or that could cause some additional exception or error, is par for course.
Comment #31
gappleI closed #2351099: watchdog_exception() hides the exception from logger implementations as a duplicate of this issue, since this one has a more extensive patch.
Comment #33
andypostComment #34
mfbComment #36
mfbI now need to add exception key to a test error array.
We could instead change_drupal_log_error() to
$exception = $error['exception'] ?? NULL;
but I'm not doing that because theoretically nothing outside of core should call _drupal_log_error() :)Comment #37
neclimdulReally interested in this because I just spent much longer that needed trying to track down a plugin exception in newrelic because it got the trace of the exception handler not the trace of the exception because newrelic's logger doesn't have access to it.
Opened an issue to support this in newrelic right away #3094508: Support passing exceptions to error logger
It _might_ make sense to move the backtrace and backtrace_string context down into loggers and leave the handling up to them but that's a bigger change that could be discussed in a follow up. Also deprecating an array key sounds annoying.
Just passing the exception through seems to align with the PSR and interface and be a no brainer with pretty solid wins for contrib. LGTM
I was wondering if we needed to do something to watchdog_exception to handle passing the exception through even if we're deprecating it but thinking through the patch this seems to be enough to handle it.
Comment #38
mfbThanks for reviewing! Yes it's silly to throw away the exception object, the whole point of which is to be logged..
btw in Raven module, we send that backtrace array to Sentry - maybe New Relic could do the same?
Comment #39
neclimdulIt could dump it into the message but it has a native trace area that's highlighted over the message. In addition to the trace, the exception type is used to group errors and to take advantage of that you need to pass
\Exception|\Throwable
to the extension method so this is needed to fully take advantage of its exception logging.Comment #40
mfbYes it's the same with Sentry as far as UI and grouping - I pass in the backtrace as the actual backtrace not as a string.
Comment #42
xjmThis issue has some great discussion and I like the idea of improving our logger this way. I think we probably want framework manager review to validate the decision, since it affects multiple subsystems.
Unfortunately, the latest patch does not apply to 9.1.x (probably because the bits related to simpletest are gone). So NW for a reroll.
Thanks!
Comment #43
gappleRerolled for 9.1.x by just removing the change related to simpletest.
I think the interdiff looks a little odd because some changes changed lines, but are otherwise the same as the previous patch.
Comment #44
neclimdulYeah reviewed this and the real interdiff is just the simpletest TestBase.php chunk as xjm guessed :)
Comment #45
neclimdulnot sure why i didn't move this back to rtbc. :-/
Comment #46
catchHow many of the cases where we're unsetting $error['exception'] are just for cleanliness and how many are required for things not to blow up?
#2949419-20: Pass the raw exception to logger implementations still seems relevant - i.e. will other logging implementations have to make the same change as dblog?
Comment #47
catchComment #48
mfbAs I mentioned in #29, loggers should be able to accept arbitrary context data without issue (with the exception object in the exception key being the one standardized bit of context data).
However, dblog will run serialize() on whatever context elements are passed into it, if the element key appears as "{key}" in the message. For example
watchdog_exception('lol', new Exception('wtf'), 'This is my {exception}.');
will result in serializing the exception object. We don't want to do this if non-serializable exceptions could be a thing, so it's safer to unset the exception element.Comment #49
neclimdulSo I think we're unseting defensively. More accurately, those changes are modifying the dblog implementation to better handle the PSR interface which _specifically_ calls out the exception key as containing exceptions with backtraces. If we didn't start including it, it would be prudent for us to make those changes anyways because implementations be calling the logger and passing that in.
There _are_ kinda competing concerns here. On the one hand, passing the exception with all its backtrace data _could_ cause problems for some loggers. Monolog probably will want to add the exception key to their filter list from that issue. On the other though is that loggers like NewRelic can't fully function without it.
Since the PSR specifically calls out this context key as containing exceptions it seems reasonable to expect loggers to handle this case so I think that addresses #20 well enough and core should move forward with this feature.
Comment #50
mfbLong term, dblog could be made more defensive in general here - perhaps ensuring the context element can be serialized before attempting to, rather than unsetting a few known problematic elements - but not sure that's in scope for this issue.
Comment #51
catchOK #48/#49 is fair enough, but this now needs another 9.1.x re-roll.
Comment #52
Hardik_Patel_12 CreditAttribution: Hardik_Patel_12 at QED42 for Drupal India Association commentedRe-rolling patch against 9.1.x.
Comment #54
vsujeetkumar CreditAttribution: vsujeetkumar at Srijan | A Material+ Company for Drupal India Association commentedFixed test, Please check.
Comment #56
vsujeetkumar CreditAttribution: vsujeetkumar at Srijan | A Material+ Company for Drupal India Association commentedTest Fixed, Please review.
Comment #57
mfbHmm it looks like something is broken somewhere, and your "fix" to the test makes it no longer test that "the error that the logging service is broken has been written to the error log"?
Comment #58
mfbLooks like the test was fine, there was just a bad re-roll.
Comment #59
mfbSome further cleanup - just call unset() once for all the elements we want to unset.
Comment #61
mfbAnyone have a chance to review this? Looks like tests are still passing on 9.2.x
Comment #62
gapplelatest patch looks good to me
Comment #63
mfbThanks. With regards to the change record @ https://www.drupal.org/node/2950598 - I checked the API documentation @ https://www.drupal.org/docs/8/api/logging-api/overview and it already says
which in turn says
so I don't think we have any further developer documentation to do...
Comment #65
catchCommitted 6e7fc11 and pushed to 9.2.x. Thanks!
Moving to 9.1.x for backport discussion.
Comment #66
mfbI published the change record @ https://www.drupal.org/node/2950598 - which we can update if this ends up being backported to 9.1.x
Comment #67
xjmSetting this back to fixed now that 9.1.0 is out; it's an addition-ish.