Patch to commit is in #9.
Problem/Motivation
Software has bugs, so errors are there and will be logged.
Sadly by default Drupal just puts the line of the broken code, which is already helpful, but on the other hand often now really helpful, because you need a full backtrace to figure something out.
Proposed resolution
Log a backtrace on top of the actual error message.
This will just make the backtrace available in the variables section, but won't be displayed as part of the watchdog site.
Remaining tasks
User interface changes
API changes
Data model changes
Comments
Comment #2
dawehnerHere is a patch for that.
Comment #4
cilefen commentedThe backtrace is an array of arrays. I think it is the output of debug_backtrace() so something must be done to make it plain text.
Comment #5
cilefen commentedComment #6
dawehner@cilefen++
Ideally we would remove maybe one or two functions from the stack, given that there is
_drupal_log_errorand_drupal_exception_handlerand
_drupal_error_handler_real()Comment #7
cilefen commented@dawehner
I am not sure you can do that with an exception object. An alternative is a backtrace array to string formatting function.
Comment #9
cilefen commentedLet's see if I can get this green while we think about ideas.
Comment #10
dawehnerYeah you made a good point. I don't think its that important to get the perfect backtrace.
Comment #11
xanoThis is a useful improvement! We may need to remove the first few items off the trace, as those are internal error handling calls. Their presence in the trace is unnecessary and can be confusing.
Comment #12
dawehnerWell, sure we could, on the other hand, there are different ways how this method is called.
In general making the available data a bit nicer is like the most unimportant thing compared to fixing the actual problem.
Comment #13
xanoI understood your argument from #10. The way I saw it, was that this is not necessarily about presentation, but about making the data itself less confusing for people who may not be entirely familiar with backtraces and Drupal's error handling.
_drupal_log_error()is an internal function. Though it would be somewhat difficult to determine the exact number of calls to remove from the stack, it would not be impossible.Comment #14
fabianx commentedRTBC for me - looks like a great improvement. I am using such a patch sometimes locally.
"Perfect is the enemy of good enough."
Lets fix any problems with the backtrace (too many, too less, etc.) in a follow-up.
Comment #15
dawehnerOpened up a follow up for that: #2652044: Correct backtrace on drupal_log_error()
Comment #16
cilefen commentedIs there a reason not to backport this to 7?
Comment #17
dawehnerIf anyone cares about it ... sure.
Comment #18
alexpottWhy don't we just use
$error['@backtrace'] = Error::formatBacktrace($error['backtrace'])Then we don't have to generate the backtrace twice.Comment #19
cilefen commentedTotal ignorance that Error::formatBacktrace() exists.
We may not need that follow-up if this is to everyone's liking.
Comment #20
dawehner@alexpott
Good question. Well I don't really care which one, just that we have one.
Comment #21
dawehnerI'm sorry, I simply haven't see #19, or rather my browser haven't refreshed in a long while.
Comment #22
alexpottSo thinking about this some more I'm a bit concerned about this resulting in massive log files. It might be nice to make this optional. Not sure.
Comment #23
dawehnerWell, what is the reason of creating log entires when you cannot make any sense about them in the first place. If you have massive log files, due to huge problems on your site,
IMHO its important to get that information ASAP, a huge log file is than probably the least problem.
Comment #24
catchfwiw I agree with dawehner - if the log entries are big, that's less of a problem than having them in the first place and the backtrace provides more chance of fixing them.
Also at this level there really shouldn't be a massive number being logged - most would go into a proper logging handler (which we already log backtraces for) - unless the site down more or less.
Moving back to Alex for a second look.
Comment #25
dawehnerYou would think so, right?
See #2646786: Add a backtrace for logged exceptions for that
Comment #26
alexpottHmmm... actually the comment
Hints as to why we're not doing this...
However I think the above code protects us because of...
So we're not serializing non-scalar arguments.
We have to be certain that adding the backtrace will not cause errors that hide the real error as that would make things worse.
Also I'm not sure why the patch attached to #19 is not showing in the issue summary.
Comment #27
alexpottSetting back to "needs review" so that other people can confirm that my thoughts in #26 are correct.
Comment #28
dawehner@alexpott
Right, this have been pretty much my train of thought when I created the patch.
If generating that BT causes issues, it would be an issue on other exception pages as well. I thought we consider them as fully working though.
Comment #30
dawehnerWell, this really adds just a string, so what should go wrong? At least in
{watchdog}the variable column is blob.Comment #31
fabianx commentedI like adding the backtrace, however Error::formatBacktrace() might indeed do too much.
I personally prefer the backtrace I get from PHP exceptions itself:
That is:
I now use this exclusively for creating backtraces in my own debugging and we can directly also Xss::filter() the whole string - as there is no HTML output to preserve.
Also those backtraces never get too large.
In fact I think we should even deprecate our backtrace formatting and replace with that method.
Comment #32
dawehnerSee #2638140-18: Error logging should log a backtrace consistently ... some earlier patches did exactly that.
Comment #33
fabianx commented#32 I think we should get back to that. internal backtrace generation is so much faster than all the string manipulation in user code - unless Alex strongly disagrees, e.g. even if we generate it twice as its generated by PHP it is still faster.
And not every error is an Exception in PHP 5.x - IIRC.
In fact, my RTBC for #9 still stands.
Comment #34
dawehnerIMHO error handling should in general not care about performance but about safety. Doing something not in userland adds safety, IMHO.
Comment #35
fabianx commentedI agree with #34, too.
Can we re-RTBC #9 or should we re-upload os its clearer?
We probably would need a comment explaining why we create a new Exception to get the backtrace().
Also marking for backport to D7 as we just discussed adding that to make specially the missing module errors easier to find, but for D7 it would of course be opt-in.
Comment #36
catchI use $e = new Exception(); $e->getTraceAsString() for my own debugging too, so happy with a version that does that.
Comment #37
dawehnerAlright, we all agree on #9
Comment #38
fabianx commentedComment #39
xjmThe issue doesn't have a current patch, so NW for that. (@dawehner says #9 but all the files are hidden.)
Do these backtraces ever show up in watchdog? If so we should have screenshots since it will impact site admins. If not let's document that here.
Thanks! This would be valuable for developers.
Comment #40
dawehnerWell sure, let's reupload the patch.
Well yeah this is really just in the variables at the moment, let me document that in the issue.
Comment #41
fabianx commented#40 How would you access the backtrace then?
Comment #42
dawehnerWell, it would be part of the variables, just not shown.
You know, I tried to keep the patch as simple as possible.
Comment #43
fabianx commentedBack to RTBC then, you can at least do a DB query or write a contrib module.
Comment #44
dawehnerJust another usecase for this patch: Debugging errors which happened, maybe just once, in cron.
Comment #45
alexpottOne thing that is weird is that these backtraces will be different.
No need to unset now
Comment #46
dawehnerIsn't the problem that this backtrace, which contains objects, might be not serializable and end up somewhere? It also seems a bit out of scope of this issue to change that.
Well, one is a string and the other one is an actual array representation. There are potential different usecases for them :)
Comment #47
alexpottAh I missed the
@re the unset.What I meant by different is this... the backtrace is created in
_drupal_error_handler_real()and the string version in_drupal_log_error()- I think they should be created in the same place. However interestingly the only other call to_drupal_log_error()is_drupal_log_error(Error::decodeException($exception), TRUE);in_drupal_exception_handler(). This already creates an string backtrace and we should definitely use that one.Comment #48
alexpottBC layer... if we think we need one.
Comment #50
fabianx commentedRTBC - New approach looks great to me!
@backstrace_string makes a lot of sense.
Might be a good idea to write a quick CR for this?
Comment #51
dawehnerCan't we use
$exception->getTraceAsString()for some form of consistency?Comment #52
alexpott@dawehner that's what happens inside Error::decodeException($exception); - in drupal 9 we can just set the return to have a key
@backtrace_stringinstead ofbacktrace_string.Comment #53
dawehnerNope. Its using its own scheme of encoding the exception.
Comment #54
alexpottThis is the return from \Drupal\Core\Utility\Error::decodeException() - I don;t understand what is meant by
Comment #55
dawehnerI was simply wrong, sorry alex
Comment #56
xjmThis causes a bit of squinting and WTF; maybe an inline comment?
Comment #57
xjmComment #58
xjmAlso could still use that CR. Thanks!
Comment #59
xjmI think this would be an okay addition during beta; it's task-ish and BC.
Comment #60
dawehnerI would kinda say its bugish :)
Comment #61
dawehnerI hope this helps ...
Comment #62
dawehnerWe have a change record now as well.
Comment #63
fabianx commentednit - in the logging == in the logs?
--
Back to RTBC
Comment #64
xjmThanks @dawehner and @Fabianx; that helps.
It's still not entirely clear to me. I'd suggest something like:
The 'backtrace_string' key contains the raw backtrace from \Drupal\Utility\Whatever\Error::decodeException(). Store it to '@backtrace_string' for use with a string placeholder in _drupal_error_log(), and unset the old placeholder.Or something. What's confusing to me is that we are not making this API addition to Error as well and handling the BC there.
Comment #65
dawehnerBecause that would be really out of scope :)
Opened a new follow up: #2775803: Move parts of _drupal_error_handler_real partially to Error / related issue.
Fair point. There are already all kind of placeholder variables, so let's just use
@backtrace_stringthere and call it a day.Comment #66
alexpottI don't think we should be changing the return array of Error::decodeException() even in a minor release.
I don't think we should be removing this tiny bc layer either.
@xjm / #64 The reason this change is not made in Error::decodeException is our BC policy.
In my opinion we should go back to #64
Or here is a potentially cleaner way of handling this. Well at least putting all of the BC logic in one place.
Comment #67
alexpottRemoving a (now) unnecessary change.
Comment #68
dawehnerWell, then we should simply return both entries?
Comment #69
alexpott@dawehner I guess we could. Seems messy but I guess we could do & to not have two copies lying around.
Comment #70
alexpottHere it is
Comment #72
alexpottThe test fail has nothing to do with this patch - https://www.drupal.org/pift-ci-job/396395 - it looks like l.d.o was unavailable for a second for some reason.
Comment #73
fabianx commentedThat is pretty neat way of solving that and keeping BC at the same time, +1 from me.
Back to RTBC
Comment #74
xjmI said add a key, not change the key. Adding a key to the associative array return value is a totally acceptable API addition.
Comment #75
alexpottWell I'm not exactly wild about these changes. it makes it really weird for anything that was manipulating the 'backtrace_string' before - which one should you manipulate now? In fact it does not matter... which is quite funky...
A bit special :)
Comment #76
dawehnerDo we now try to seriously overthink stuff?
Comment #77
xjmI actually kinda agree with @alexpott that I would avoid saving it by reference; that tends to lead to Bad Things Happen™.
Still not sure why this would be out of scope:
Then we could just use
@backtrace_stringeverywhere in core but leavebacktrace_stringfor BC. The only thing that seems kind of out of scope is fixing the return value documentation forError::decodeException()(it's backwards for something in Utility to refer to a procedural function for docs; should be the other way around).Comment #78
xjmGoing to set at NR until we sort this one way or the other -- sorry @dawehner. :(
Comment #79
alexpott@xjm the reason I made it by reference is that backtraces are not small. Especially in some deeply nested rendering situation like threaded comments. We're already in a error situation and running out of memory for BC seemed pointless.
This is yet another reason I didn't want to actually make any changes to the Error class and just handle all the BC in _drupal_log_error() which why my preference has always been for #67. In my opinion we should go back to that and be done. Having multiple places where we handle BC also feels more fragile.
Reuploading the patch from #67.
Comment #80
fabianx commentedComment #81
dawehnerAll I want is to be able to get reasonable backtraces from errors, especially from cron errors.
RTBC+1
Comment #82
alexpottDiscussed the BC at length with @xjm. Salient points:
An error in the format expected by _drupal_log_error().- public API depending on _drupal_log_error?backtrace_stringwas only introduced in 8.0.5 by #2646786: Add a backtrace for logged exceptionsDecided that the BC layer and difficulties implementing something simple means that we should just change the key and be done. I've updated the CR to reflect this.
Comment #83
alexpottComment #84
dawehnerNice! This is the simplest approach one could make
Comment #85
xjmUpdating issue credit.
Comment #86
xjmComment #87
xjmComment #89
xjmCommitted dd1c1ce and pushed to 8.2.x. Thanks!
This issue is tagged for D7 backport, so according to our current backport policy, a separate issue to discuss that backport should be created and then we can mark this issue fixed.
Comment #90
dawehnerWhat is the policy with committing it to 8.1.x now at that point in time?
Comment #91
fabianx commented#90: If its a bug, then it could be pushed to 8.1.x (the new description makes it look like a bug), as FR => no go.
Also:
YEAH!
Comment #92
xjm@dawehner I think probably we should not backport it to 8.1.x in this case, particularly given the internal API addition/internal BC break. At this point 8.1.x is in commit freeze for 8.1.8 anyway, so there is only one more bugfix release for 8.1.x that will include any changes we make from this point forward, and we are very conservative for the month before the final patch release.
Comment #93
fabianx commentedD7 issue is here: https://www.drupal.org/node/2777955, Thanks all!