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.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mfb created an issue. See original summary.

Status: Needs review » Needs work

The last submitted patch, exception.patch, failed testing. View results

mfb’s picture

Status: Needs work » Needs review
FileSize
5.38 KB

Unset exception in various places where backtrace is also unset.

Status: Needs review » Needs work

The last submitted patch, 3: 2949419-exception.patch, failed testing. View results

mfb’s picture

mfb’s picture

Status: Needs work » Needs review
dawehner’s picture

Status: Needs review » Reviewed & tested by the community
+++ b/core/includes/update.inc
@@ -186,7 +186,7 @@ function update_do_one($module, $number, $dependency_map, &$context) {
       $variables = Error::decodeException($e);
-      unset($variables['backtrace']);
+      unset($variables['backtrace'], $variables['exception']);
       $ret['#abort'] = ['success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables)];

Oh 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.

mfb’s picture

Yes, 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.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 5: 2949419-exception-5.patch, failed testing. View results

dawehner’s picture

Issue tags: +Needs change record

Yeah let's do that :)

mfb’s picture

Issue tags: -Needs change record
mfb’s picture

Status: Needs work » Needs review
dawehner’s picture

Category: Feature request » Task
Status: Needs review » Reviewed & tested by the community

Thank you @mfb!

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/includes/errors.inc
    @@ -142,9 +143,10 @@ function error_displayable($error = NULL) {
     function _drupal_log_error($error, $fatal = FALSE) {
    

    The docs for the $error param need updating.

  2. +++ b/core/includes/errors.inc
    @@ -160,7 +162,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
           // Provide the PHP backtrace to logger implementations.
    

    This comment looks like it needs updating.

  3. +++ b/core/includes/errors.inc
    @@ -160,7 +162,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
    -      \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace]);
    +      \Drupal::logger('php')->log($error['severity_level'], '%type: @message in %function (line %line of %file) @backtrace_string.', $error + ['backtrace' => $backtrace, 'exception' => $exception]);
    

    Could do with some test coverage of this change.

mfb’s picture

Fixed docs and comment; have to figure out where the test coverage for this belongs..

jibran’s picture

Title: pass the raw exception to logger implementations » Pass the raw exception to logger implementations
Related issues: +#2954126: The return value of Error::decodeException should never be passed to LoggerInterface::log.
mfb’s picture

ok I didn't find any test coverage of _drupal_log_error() - anyone know where this should go?

mfb’s picture

This would also resolve #2351099: watchdog_exception() hides the exception from logger implementations since the "decoded exception" will include the raw exception.

mfb’s picture

Status: Needs work » Needs review

Setting to needs review to see if anyone has a suggestion for where I could add more test coverage.

Sam152’s picture

I 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.

Sam152’s picture

Spitballing a possible approach to #20, perhaps an ErrorAwareLoggerInterface with a signature like:

    /**
     * Logs with an arbitrary level and additional error context.
     *
     * @param mixed  $level
     * @param string $message
     * @param array  $context
     * @param array  $backtrace
     * @param mixed $exception
     *
     * @return void
     */
    public function log($level, $message, array $context = array(), $backtrace = [], $exception = NULL);
dawehner’s picture

Given 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?

Sam152’s picture

In core we're expecting monolog to normalize the full output of \Exception::getTrace, that's what we're calling in Error::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).

mfb’s picture

It 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()...

dawehner’s picture

@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?

mfb’s picture

@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]

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

nterbogt’s picture

I'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.

mfb’s picture

According to https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logg...

Every method accepts an array as context data. This is meant to hold any extraneous information that does not fit well in a string. The array can contain anything. Implementors MUST ensure they treat context data with as much lenience as possible. A given value in the context MUST NOT throw an exception nor raise any php error, warning or notice.

If an Exception object is passed in the context data, it MUST be in the 'exception' key. Logging exceptions is a common pattern and this allows implementors to extract a stack trace from the exception when the log backend supports it. Implementors MUST still verify that the 'exception' key is actually an Exception before using it as such, as it MAY contain anything.

The context array can contain arbitrary data, the only assumption that can be made by implementors is that if an Exception instance is given to produce a stack trace, it MUST be in a key named "exception".

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.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

gapple’s picture

I closed #2351099: watchdog_exception() hides the exception from logger implementations as a duplicate of this issue, since this one has a more extensive patch.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

andypost’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll
mfb’s picture

Status: Needs review » Needs work

The last submitted patch, 34: 2949419-exception-34.patch, failed testing. View results

mfb’s picture

Status: Needs work » Needs review
FileSize
7.34 KB

I 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() :)

neclimdul’s picture

Status: Needs review » Reviewed & tested by the community
Related issues: +#3094508: Support passing exceptions to error logger

Really 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

+++ b/core/lib/Drupal/Core/Utility/Error.php
@@ -72,6 +72,7 @@ public static function decodeException($exception) {
+      'exception' => $exception,

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.

mfb’s picture

Thanks 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?

neclimdul’s picture

It 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.

mfb’s picture

Yes 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.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

xjm’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs framework manager review, +Needs reroll

This 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!

gapple’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
6.58 KB
2.09 KB

Rerolled 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.

neclimdul’s picture

Yeah reviewed this and the real interdiff is just the simpletest TestBase.php chunk as xjm guessed :)

neclimdul’s picture

Status: Needs review » Reviewed & tested by the community

not sure why i didn't move this back to rtbc. :-/

catch’s picture

How 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?

catch’s picture

Status: Reviewed & tested by the community » Needs review
mfb’s picture

As 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.

neclimdul’s picture

Status: Needs review » Reviewed & tested by the community

So 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.

mfb’s picture

Long 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.

catch’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs reroll

OK #48/#49 is fair enough, but this now needs another 9.1.x re-roll.

Hardik_Patel_12’s picture

Status: Needs work » Needs review
FileSize
6.69 KB

Re-rolling patch against 9.1.x.

Status: Needs review » Needs work

The last submitted patch, 52: 2949419-52.patch, failed testing. View results

vsujeetkumar’s picture

Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
8.47 KB
1.66 KB

Fixed test, Please check.

Status: Needs review » Needs work

The last submitted patch, 54: 2949419_54.patch, failed testing. View results

vsujeetkumar’s picture

Status: Needs work » Needs review
FileSize
8.39 KB
991 bytes

Test Fixed, Please review.

mfb’s picture

Status: Needs review » Needs work

Hmm 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"?

mfb’s picture

Status: Needs work » Needs review
FileSize
6.98 KB
1.63 KB

Looks like the test was fine, there was just a bad re-roll.

mfb’s picture

FileSize
7.1 KB
1.19 KB

Some further cleanup - just call unset() once for all the elements we want to unset.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

mfb’s picture

Anyone have a chance to review this? Looks like tests are still passing on 9.2.x

gapple’s picture

Status: Needs review » Reviewed & tested by the community

latest patch looks good to me

mfb’s picture

Thanks. 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

This class must implement \Psr\Log\LoggerInterface.

which in turn says

The context array can contain arbitrary data. The only assumption that can be made by implementors is that if an Exception instance is given to produce a stack trace, it MUST be in a key named "exception".

so I don't think we have any further developer documentation to do...

  • catch committed 6e7fc11 on 9.2.x
    Issue #2949419 by mfb, vsujeetkumar, gapple, Hardik_Patel_12, neclimdul...
catch’s picture

Version: 9.2.x-dev » 9.1.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Committed 6e7fc11 and pushed to 9.2.x. Thanks!

Moving to 9.1.x for backport discussion.

mfb’s picture

I published the change record @ https://www.drupal.org/node/2950598 - which we can update if this ends up being backported to 9.1.x

xjm’s picture

Version: 9.1.x-dev » 9.2.x-dev
Status: Patch (to be ported) » Fixed

Setting this back to fixed now that 9.1.0 is out; it's an addition-ish.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.