Problem/Motivation

We're modernizing PHP by removing procedural code. watchdog_exception is an example of that. It provides a simple wrapper around \Drupal::logger()->log() with a bit of exception decoding.

Proposed resolution

  • Deprecate watchdog_exception
  • Replace uses of watchdog_exception() with \Drupal\Core\Utility\Error::logException($logger, $e)

Remaining tasks

API changes

The function watchdog_exception() has been deprecated.

CommentFileSizeAuthor
#197 2932518-197-interdiff.txt2.3 KBkim.pepper
#197 2932518-197.patch60.51 KBkim.pepper
#193 2932518-193-interdiff.txt1.71 KBkim.pepper
#193 2932518-193.patch60.77 KBkim.pepper
#192 2932518-192-interdiff.txt579 byteskim.pepper
#192 2932518-192.patch61.32 KBkim.pepper
#191 2932518-191-interdiff.txt4.84 KBkim.pepper
#191 2932518-191.patch61.27 KBkim.pepper
#188 2932518-188-interdiff.txt1.13 KBkim.pepper
#188 2932518-188.patch57.8 KBkim.pepper
#186 2932518-186-interdiff.txt1.67 KBkim.pepper
#186 2932518-186.patch58.57 KBkim.pepper
#180 2932518-180-interdiff.txt2 KBkim.pepper
#180 2932518-180.patch58.76 KBkim.pepper
#179 2932518-178-interdiff.txt3.48 KBkim.pepper
#179 2932518-178.patch56.76 KBkim.pepper
#177 2932518-177-interdiff.txt1.25 KBkim.pepper
#177 2932518-177.patch55.72 KBkim.pepper
#176 2932518-176-interdiff.txt14 KBkim.pepper
#176 2932518-176.patch55.61 KBkim.pepper
#170 2932518-170-interdiff.txt2.38 KBkim.pepper
#170 2932518-170.patch54.37 KBkim.pepper
#168 2932518-168-interdiff.txt7.01 KBkim.pepper
#168 2932518-168.patch51.99 KBkim.pepper
#166 2932518-166-interdiff.txt9.44 KBkim.pepper
#166 2932518-166.patch44.98 KBkim.pepper
#163 2932518-163.patch38.29 KBandypost
#163 interdiff.txt17.7 KBandypost
#161 2932518-161-interdiff.txt817 byteskim.pepper
#161 2932518-161.patch39.58 KBkim.pepper
#159 2932518-159-interdiff.txt5.93 KBkim.pepper
#159 2932518-159.patch39.38 KBkim.pepper
#157 2932518-157-interdiff.txt1.82 KBkim.pepper
#157 2932518-157.patch34.17 KBkim.pepper
#156 2932518-156.patch33.96 KBkim.pepper
#154 2932518-9.5.x.patch18.81 KBbradjones1
#150 2932518.patch20.22 KBbradjones1
#148 watchdog-9.5.x.patch.txt6.35 KBbradjones1
#89 2932518-89.patch21 KBneclimdul
#87 2932518-watchdog-exception-87.patch20.81 KBnwom
#73 2932518-watchdog-exception-73.patch20.62 KBhardik_patel_12
#68 2932518-watchdog-exception-68.patch20.62 KBcliddell
#65 2932518-65-interdiff.txt870 bytescliddell
#65 2932518-watchdog-exception-65.patch20.63 KBcliddell
#63 2932518-63-interdiff.txt919 bytescliddell
#63 2932518-watchdog-exception-63.patch20.76 KBcliddell
#62 2932518-62-interdiff.txt15.06 KBkim.pepper
#62 2932518-62.patch19.61 KBkim.pepper
#58 2932518-58-interdiff.txt5.75 KBgapple
#58 2932518-watchdog-exception-58.patch14.8 KBgapple
#56 2932518-56-interdiff.txt3.7 KBgapple
#56 2932518-watchdog-exception-56.patch13.12 KBgapple
#53 2932518-watchdog-exception-53.patch9.04 KBgapple
#39 2932518-39-interdiff.txt33.37 KBkim.pepper
#39 2932518-39.patch53.16 KBkim.pepper
#37 2932518-37-interdiff.txt13.41 KBkim.pepper
#37 2932518-37.patch25.9 KBkim.pepper
#33 2932518-33.patch21.58 KBkim.pepper
#33 2932518-33-interdiff.txt1.57 KBkim.pepper
#29 2932518-watchdog-29.patch21.08 KBkim.pepper
#29 2932518-watchdog-29-interdiff.txt5.96 KBkim.pepper
#22 drupal_2932518_22.patch19.84 KBxano
#22 interdiff.txt10.37 KBxano
#19 drupal_2932518_19.patch10.28 KBxano
#8 2932518-watchdog-exception-8-interdiff.txt2.22 KBkim.pepper
#8 2932518-watchdog-exception-8.patch4.3 KBkim.pepper
#3 2932518-watchdog-exception-3.patch4.22 KBkim.pepper

Issue fork drupal-2932518

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

kim.pepper created an issue. See original summary.

kim.pepper’s picture

Title: watchdog_exception to a method on \Drupal » Replace watchdog_exception with a method on \Drupal
kim.pepper’s picture

Status: Active » Needs review
StatusFileSize
new4.22 KB

Here's what I am thinking...

jibran’s picture

Issue tags: +Needs change record
kim.pepper’s picture

Issue tags: -Needs change record

Added draft change record https://www.drupal.org/node/2932520

kim.pepper’s picture

If we don't want this on \Drupal we could add it as a static method on \Drupal\Core\Utility\Error

Thoughts?

jibran’s picture

I think \Drupal makes sense.

+++ b/core/includes/bootstrap.inc
@@ -398,21 +398,13 @@ function drupal_validate_utf8($text) {
+ * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0.
+ *   Use \Drupal::logException() instead.

I think we have to mention change notice link here as well.

kim.pepper’s picture

Thanks @jibran!

jibran’s picture

Status: Needs review » Reviewed & tested by the community

I can't find anything to nitpick so RTBC.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
+++ b/core/includes/bootstrap.inc
@@ -398,21 +398,13 @@ function drupal_validate_utf8($text) {
+  @trigger_error('watchdog_exception() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal::logException() instead. See https://www.drupal.org/node/2932520', E_USER_DEPRECATED);

+++ b/core/tests/Drupal/Tests/Listeners/DeprecationListenerTrait.php
@@ -123,6 +123,7 @@ public static function getSkippedDeprecations() {
+      'watchdog_exception() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal::logException() instead. See https://www.drupal.org/node/2932520',

Let's do this when we've removed all usages in the follow-up.

I'm not 100% sure that putting this on \Drupal is the best place for it. Really, this function does two things,

  1. massage some variables and an exception suitable for use with the logger
  2. Do the logging.

In OO code, I'd expect you'd inject a logger, so you really only need the first bit, but then we already have Error::decodeException for this.

I'd be interested to see how many OO calls to watchdog_exception passed the optional variables.

If not many, then those calls should be changed to just log after calling Error::decodeException and this can live on \Drupal for non OO code sake. I.e I suspect we shouldn't need this from OO code, but would like to see the stats first. If there are a few calls to it from OO code that do pass the additional variables, then there might be merit in splitting this into two separate functions as per the two things it does.

OO code would only need the first half

dawehner’s picture

I'd be interested to see how many OO calls to watchdog_exception passed the optional variables.

When I looked at the code I have locally (some contrib modules + core) there was just the search API module using this functionality:

modules/contrib/search_api_autocomplete/src/Controller/AutocompleteController.php
130:      watchdog_exception('search_api_autocomplete', $e, '%type while retrieving autocomplete suggestions: !message in %function (line %line of %file).');

modules/contrib/search_api_solr/search_api_solr.module
78:        watchdog_exception('search_api_solr', $e, '%type while optimizing Solr server @server: @message in %function (line %line of %file).', array('@server' => $server->label()));

modules/contrib/search_api_solr/src/Form/SolrConfigForm.php
74:      watchdog_exception('search_api_solr', $e, '%type while retrieving config files of Solr server @server: @message in %function (line %line of %file).', array('@server' => $search_api_server->label()));

modules/contrib/search_api_solr/src/Plugin/search_api/backend/SearchApiSolrBackend.php
648:      watchdog_exception('search_api_solr', $e, "%type while indexing: @message in %function (line %line of %file).");
1989:        watchdog_exception('search_api_solr', $e, "%type during autocomplete Solr query: @message in %function (line %line of %file).", array(), WATCHDOG_WARNING);

, but even then, those usecases still fundamentally set the same error message. I think its fair to assume that all these parameters got inherited from watchdog() in Drupal 7.

For me there are a two possiblities:

  1. Accept that adding an additional Error::decodeException() is not that bad actually. Code which cares about logging exceptions probably have some other logger injected already as well. All other customizations could be applied on top of that
  2. Provide a new ExceptionLoggerInterface which has something like logException as its own method. Internally it could wrap the exception as wanted and maybe provide $exception, $message = NULL as possible parameters. For using it could be constructed from an existing logger, so maybe: ExceptionLogger::create($this->logger)->logException($exception)
  3. We agree that a static method isn't too bad. Even in this case there is no need for adding it to \Drupal. It could happily live somewhere in \Drupal\Core\Logger for example.
kim.pepper’s picture

While I like the OOP approach to 2) it seems a bit unnecessary to have to inject 2 services, one if you are just logging info, and another if you are logging an exception.

I'm more in favour of 1).

dawehner’s picture

Note: At least in my head 2) Wouldn't really have any additional service. watchdog_exception was not swappable, nor does ExceptionLogger::create() | ExceptionLogger::logException($logger, $exception). Does that make a little bit more sense?

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

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

borisson_’s picture

While it is true that this is "more OO", I don't feel that's a solid justification for this change. It doesn't increase testability, it doesn't make code less coupled.

I don't have a solid answer to #13 either.

dawehner’s picture

@kim.pepper Do you have a question about #13?

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.

xano’s picture

Yay! OOP! I agree with @dawehner that we can probably come up with a better place to put this than on \Drupal. That's really just a bunch of helpers for code that's procedural or has trouble with DI. The assessments that this primarily prepares values for LoggerInterface is a good one. What about we put this on a tiny helper class somewhere (I wish we did function autoloading too) as a autoloadable, non-injectable helper? The function can return a tuple-style array so calling code can call LoggerInterface itself, or the helper function also takes a severity (defaults to error?) and LoggerInterface and logs the parsed exception for you? This would save us the trouble of using another container service, unless anyone sees a need for swapping exception log message formatting application-wide.

xano’s picture

StatusFileSize
new10.28 KB

This illustrates one of the approaches I suggested (the decorator, as PHP/Drupal folks may not be too familiar with tuples). I opted to mimic \Psr\Log\LoggerInterface, which explains the change in parameters from the previous patch, which was more closely based on watchdog_exception(), but that is more for convenience and not essentially required. This patch also includes two conversions, with one procedural (being watchdog_exception() itself) and another OOP inside ModuleInstaller.

xano’s picture

I almost wonder if it's worth seeing if we can revise the logging PSR to accept not just strings, but anything that can be cast to a string. Then we can just decorate exceptions in objects that implement ::__toString() and pass them on to any old LoggerInterface directly.

Status: Needs review » Needs work

The last submitted patch, 19: drupal_2932518_19.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

xano’s picture

Status: Needs work » Needs review
StatusFileSize
new10.37 KB
new19.84 KB

This patch brings back the concept of a convenience method on \Drupal that @kim.pepper originally introduced to provide a much cleaner procedural alternative to watchdog_exception() than what #19 allowed us to do. If we want, we can keep track of exception loggers per log channel inside \Drupal to limit the number objects.

dawehner’s picture

Title: Replace watchdog_exception with a method on \Drupal » Replace watchdog_exception with a ExceptionLogger
Issue summary: View changes

Updating the issue summary. I do like the patch right now.

+++ b/core/lib/Drupal.php
@@ -708,6 +709,20 @@ public static function logger($channel) {
+  /**
+   * Gets an exception logger.
+   *
+   * @param string $channel
+   *   The name of the channel. Can be any string, but the general practice is
+   *   to use the name of the subsystem calling this.
+   *
+   * @return \Drupal\Core\Logger\ExceptionLogger
+   *   The exception logger.
+   */
+  public static function exceptionLogger($channel) {
+    return new ExceptionLogger(static::logger($channel));
+  }
+

May I ask whether it would be possible to use ExceptionLogger::create() instead and skip the method on \Drupal altogether.

xano’s picture

Thanks for the review! :) The reason this is on \Drupal is that there's where most of the 'procedural-to-OOP' shortcuts live. It would also allow us to statically cache exception loggers if we want to (maybe not....yet?). It also makes it as similar to regular loggers as possible, although I'm fine with making this whole thing more minimal and look less like LoggerInterface. What would be the benefit of ExceptionLoger::create() over ::__construct()?

dawehner’s picture

) The reason this is on \Drupal is that there's where most of the 'procedural-to-OOP' shortcuts live.

Yeah I guess it seems totally fine.

It would also allow us to statically cache exception loggers if we want to (maybe not....yet?).

I think optimizing for the exception path seems like a bad optimization strategy :)

What would be the benefit of ExceptionLoger::create() over ::__construct()?

I think the first is more readable, but also enables future changes of the constructor, your caching if needed.

gapple’s picture

Issue summary: View changes

Small cleanup to issue summary

gapple’s picture

From #2351099: watchdog_exception() hides the exception from logger implementations, the exception should be passed as a value in the context array of the log call.

kim.pepper’s picture

Issue tags: +Kill includes
kim.pepper’s picture

StatusFileSize
new5.96 KB
new21.08 KB

Re-roll, usage in \Drupal\Core\Extension\ModuleInstaller and code style fix ups.

kim.pepper’s picture

I'm considering #27 out of scope especially since it's still being debated and looks like #2949419: Pass the raw exception to logger implementations might be an alternative.

Status: Needs review » Needs work

The last submitted patch, 29: 2932518-watchdog-29.patch, failed testing. View results

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.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new1.57 KB
new21.58 KB

Re-roll. Also updates deprecation message and adds a deprecation test.

Status: Needs review » Needs work

The last submitted patch, 33: 2932518-33.patch, failed testing. View results

dawehner’s picture

+++ b/core/lib/Drupal/Core/Cron.php
@@ -236,7 +236,7 @@ protected function invokeCronHandlers() {
-        watchdog_exception('cron', $e);
+        \Drupal::exceptionLogger('cron')->error($e);
       }

May I ask a question about the API? Is there ever a usecase for doing ExceptionLogger::info() for example? If ExceptionLogger doesn't actually implement the logger interface anyway, wouldn't it be enough to have ExceptionLogger::logException and not have any of the additional methods?

kim.pepper’s picture

May I ask a question about the API? Is there ever a usecase for doing ExceptionLogger::info() for example? If ExceptionLogger doesn't actually implement the logger interface anyway, wouldn't it be enough to have ExceptionLogger::logException and not have any of the additional methods?

I agree.

It's been a while since I looked at this issue, and I think there could be some re-evaluation of the design.

Rather than static methods, I'd prefer defining an interface: ExceptionLoggerInterface with one method logException($e) which we can then inject as a service where ever we currently need it, and where we can't inject it, we can use the existing \Drupal::service() method approach.

However, we'd also need to take into account how the log factory currently works. This might mean we also need a ExceptionLoggerFactory that wraps the LoggerFactory.

I might put together a POC patch just to demonstrate what I mean.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new25.9 KB
new13.41 KB

Here's a patch with the ideas I proposed in #36. I've used the 'cron' service as an example of how this would be injected.

Thoughts?

Status: Needs review » Needs work

The last submitted patch, 37: 2932518-37.patch, failed testing. View results

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new53.16 KB
new33.37 KB

Replaced a few more usages of \Drupal::exceptionLogger() with the injected service.

Status: Needs review » Needs work

The last submitted patch, 39: 2932518-39.patch, failed testing. View results

gapple’s picture

I don't think it makes sense to introduce a new service only for logging exceptions, when the existing logging service can be improved (as in #2949419: Pass the raw exception to logger implementations), and doing so would conform to the expectation of other PSR loggers.

kim.pepper’s picture

@gapple yeah I think you are right. Sometimes I need to write code before I know whether it's the right thing to do or not.

I'd be in favour of just calling the existing logger interface and passing the exception in the 'exception' context key.

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

Issue tags: +Needs reroll
kim.pepper’s picture

Status: Needs work » Closed (won't fix)

I'm inclined to mark this closed (wont fix) and focus on #2949419: Pass the raw exception to logger implementations instead. Thoughts?

markhalliwell’s picture

Unless I'm missing something, that's just a minor feature addition to the existing procedural code.

This issue is about replacing the procedural function with a service.

How is that issue relevant to this one?

kim.pepper’s picture

There has mostly been discussion about the approach. I think the patches there are quite old.

Basically, we are using PSR LoggerInterface and already have a mechanism for passing the exception to the logs via the $context param.

e.g.

$logger = \Drupal::logger('example');
try {
...
catch(\Exception $e) {
  $logger->('Error trying foo', ['exception' -> $e]);
}
markhalliwell’s picture

Then shouldn't this issue be repurposed to still deprecate watchdog_exception() and then pass the exception once #2949419: Pass the raw exception to logger implementations lands?

In any case, either in that issue or this one, watchdog_exception() needs to be deprecated and its implementations removed, yes?

kim.pepper’s picture

True. We can mark this one as duplicate or the other.

markhalliwell’s picture

Version: 8.9.x-dev » 8.8.x-dev
Status: Closed (won't fix) » Needs work

Well, that issue is kind of separate from this one. This one specifically deals with the deprecation of watchdog_exception(). I don't think we should muddy that issue with this now that I think about it.

I'd say the current service approach just needs to be removed from the latest patch. The deprecation and replacement of the watchdog_exception() usages is still valid.

Although, this all being said, I don't think we can do this in 8.9.x... so moving to 8.8.x if it can get in. Otherwise, I suppose it'll have to be bumped to 9.0.x?

kim.pepper’s picture

Yeah, I'd say this is unlikely to get into 8.8.x given the alpha is this week.

On thing we should do is look at how \Drupal\dblog\Logger\DbLog::log() handles exceptions. At the moment it doesn't look at that context key and unsets the 'backtrace' context .

We may need to move \Drupal\Core\Utility\Error::decodeException() inside DbLog::log() and possibly _drupal_log_error() to keep backwards compatibility.

gapple’s picture

Title: Replace watchdog_exception with a ExceptionLogger » Deprecate watchdog_exception
Issue summary: View changes

Currently, DbLog stores everything passed to $context (except the removed backtrace) to the variables column of the log table.
The patch for #2949419: Pass the raw exception to logger implementations updates DbLog to also remove the exception object from being stored in the database.

gapple’s picture

Status: Needs work » Needs review
StatusFileSize
new9.04 KB

#2949419: Pass the raw exception to logger implementations also updates Error::decodeException() to include the passed exception in the variables array it returns, so the only thing really missing that watchdog_exception provides is a default message.

kim.pepper’s picture

Nice.

'%type: @message in %function (line %line of %file).'

Maybe we can make this a constant?

Status: Needs review » Needs work

The last submitted patch, 53: 2932518-watchdog-exception-53.patch, failed testing. View results

gapple’s picture

Status: Needs work » Needs review
StatusFileSize
new13.12 KB
new3.7 KB

I used a regex to find/replace instances in the last patch, so missed a couple that didn't match the same format.

kim.pepper’s picture

Lets make it a constant :-)

gapple’s picture

StatusFileSize
new14.8 KB
new5.75 KB

Some missing imports for the Error utility class where it's now used, and make use of a service's injected logger instance where available instead of using the static Drupal::logger()

gapple’s picture

Issue summary: View changes

The last submitted patch, 56: 2932518-watchdog-exception-56.patch, failed testing. View results

kim.pepper’s picture

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

This is a deprecation, and 8.8.0-alpha1 is out now, so unfortunately, this needs to be against 9.1.x now. 😭

kim.pepper’s picture

Version: 9.1.x-dev » 8.9.x-dev
StatusFileSize
new19.61 KB
new15.06 KB

Here's what I was talking about with the constant.

cliddell’s picture

Issue summary: View changes
StatusFileSize
new20.76 KB
new919 bytes
  1. Migrated a couple new instances of watchdog_exception() over to \Drupal::logger().
  2. Fixed a minor typo in the issue summary.

Also, should we really be preemptively unseting the 'exception' offset, which does not exist, in update.inc for the sake of an issue which has yet to be accepted into core? (i.e. #2949419: Pass the raw exception to logger implementations)

+++ b/core/includes/update.inc
@@ -188,11 +188,11 @@ 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(Error::DEFAULT_ERROR_MESSAGE, $variables)];
 
@@ -255,13 +255,13 @@ function update_invoke_post_update($function, &$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),
+        'query' => t(Error::DEFAULT_ERROR_MESSAGE, $variables),
       ];
gapple’s picture

The replacement recommendation should be updated to use Error::DEFAULT_ERROR_MESSAGE and Error::decodeException, as is done in the rest of the patch.

#2949419: Pass the raw exception to logger implementations already includes the changes with unset to remove the exception variable, so those could be removed from this patch.

cliddell’s picture

StatusFileSize
new20.63 KB
new870 bytes

Okay, the replacement I recommended in the previous patch already makes use of Error::DEFAULT_ERROR_MESSAGE and Error::decodeException().

I removed unseting of the aforementioned exception offset, since the change is already included in #2949419: Pass the raw exception to logger implementations.

neclimdul’s picture

Status: Needs review » Needs work
+++ b/core/includes/bootstrap.inc
@@ -430,12 +430,18 @@ function drupal_validate_utf8($text) {
+ * @deprecated in drupal:8.8.0 and will be removed from drupal:9.0.0. Use
...
+  @trigger_error('watchdog_exception() is deprecated in drupal:8.8.0. It will be removed from drupal:9.0.0. Use \Drupal::logger() and pass the exception as a variable instead. See https://www.drupal.org/node/2932520', E_USER_DEPRECATED);

I assume this should be updated to 8.9 now? Probably 9 -> 10 too? I don't remember off the top of my head but I would wager we can't remove this in 9 at this point.

neclimdul’s picture

actually, no there's a 8.9 beta out so without special consideration this is 9.x and 10.x.

cliddell’s picture

Version: 8.9.x-dev » 9.1.x-dev
Status: Needs work » Needs review
StatusFileSize
new20.62 KB

Updated patch and change record to target 9.1.x and 10.0.x due to there being both a 8.9 and 9.0 beta out as of this comment.

It is a bit frustrating that this will have to be postponed until 9.1.x due to the lifetime of this ticket and it not being disruptive to my knowledge 😞. Though I do understand the necessity of not introducing new patches late in the release cycle.

neclimdul’s picture

I'm with you. I mostly keep coming back here like "oh, you still can't do that? this never got committed? How could that be?"

The first bot failure was github being down but all the requeues I've tried failed to apply. I tried every possible method I know of applying patches and they all worked so I feel like this is a testbot failure but I'm feeling a bit out of the loop and/or noobish because everything I've been posting the last couple days seems to have weird mysterious failures I can't figure out.

cliddell’s picture

That was my bad, I was still targeting 8.9.x for the test even though I had updated the ticket and patch to target 9.1.x 😅. I just added a test to target 9.1.x.

neslee canil pinto’s picture

Status: Needs review » Needs work
cliddell’s picture

Status: Needs work » Needs review

Transitioning back into "Needs review" since it passed the 9.1.x test.

hardik_patel_12’s picture

StatusFileSize
new20.62 KB

Re-roll for 9.1.x.

kim.pepper’s picture

Status: Needs review » Reviewed & tested by the community

I think this is ready.

xjm’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: -Needs reroll

Thanks for working on cleaning up our legacy code! A few points of review:

  1. +++ b/core/includes/errors.inc
    @@ -185,7 +185,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
    -      $response->setContent(html_entity_decode(strip_tags(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error))) . "\n");
    +      $response->setContent(html_entity_decode(strip_tags(new FormattableMarkup(Error::DEFAULT_ERROR_MESSAGE, $error))) . "\n");
    
    @@ -196,7 +196,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
    -        $response->setContent(new FormattableMarkup('%type: @message in %function (line %line of %file).', $error));
    +        $response->setContent(new FormattableMarkup(Error::DEFAULT_ERROR_MESSAGE, $error));
    
    @@ -236,7 +236,7 @@ function _drupal_log_error($error, $fatal = FALSE) {
    -        $message = new FormattableMarkup('%type: @message in %function (line %line of %file).', $error);
    +        $message = new FormattableMarkup(Error::DEFAULT_ERROR_MESSAGE, $error);
    
    +++ b/core/lib/Drupal/Core/Utility/Error.php
    @@ -18,6 +18,11 @@ class Error {
    +  /**
    +   * The the default message for logging errors.
    +   */
    +  const DEFAULT_ERROR_MESSAGE = '%type: @message in %function (line %line of %file).';
    +
    

    Can we call this DEFAULT_ERROR_MESSAGE_TEMPLATE or something? I got really confused when we were passing a constant to FormattableMarkup.

    I'm also wondering if a defaultError(array $error_variables) method would be a better way to handle a default message format, rather than a constant?

  2. +++ b/core/includes/update.inc
    @@ -220,11 +220,11 @@ function update_do_one($module, $number, $dependency_map, &$context) {
    -      $ret['#abort'] = ['success' => FALSE, 'query' => t('%type: @message in %function (line %line of %file).', $variables)];
    +      $ret['#abort'] = ['success' => FALSE, 'query' => t(Error::DEFAULT_ERROR_MESSAGE, $variables)];
    
    @@ -287,13 +287,13 @@ function update_invoke_post_update($function, &$context) {
    -        'query' => t('%type: @message in %function (line %line of %file).', $variables),
    +        'query' => t(Error::DEFAULT_ERROR_MESSAGE, $variables),
    

    t() can't translate a constant, which is another reason against using a constant. We could have translatedDefaultError() and untranslatedDefaultError(), or something. There might be better names.

  3. +++ b/core/lib/Drupal/Core/Database/database.api.php
    @@ -188,7 +188,7 @@
    - *     watchdog_exception('type', $e);
    + *     \Drupal::logger('type')->error('%type: @message in %function (line %line of %file).', Error::decodeException($e));
    

    This seems to not have the constant (that I think should be a method).

kim.pepper’s picture

Status: Needs review » Needs work
  1. I like the idea of hiding the constant in place of Error::formatMessage($error)
  2. I don't see the point of using t() in error messages. Pretty sure we aren't doing this anywhere else in core. Why here?
  3. See 1.
shaktik’s picture

neclimdul’s picture

I don't see the point of using t() in error messages. Pretty sure we aren't doing this anywhere else in core. Why here?

I don't think it is being used for "errors." Like we normally would. It looks like this is trying to pass something to the update UI. I can't find any direct usage of ['#abort'] other than to check its existence though so maybe previous reviews couldn't figure it out and left it alone instead of converting to FormatMarkup? Or maybe the message is completely dead? update.inc is really hard to follow :( :(

dawehner wrote this for 8.0 in #2538108: Add hook_post_update_NAME() for data value updates to reliably run after data format updates. Maybe he remembers something...

abhijeet.kumar2107’s picture

Assigned: Unassigned » abhijeet.kumar2107
cliddell’s picture

@neclimdul You're right, it's not being used as a watchdog error message; though the message is still in use. See lines 457-460 of DbUpdateController.php for reference.

$messages[] = [
  '#wrapper_attributes' => ['class' => ['failure']],
  '#markup' => '<strong>' . $this->t('Failed:') . '</strong> ' . $query['query'],
];

$query contains the value of $ret['#abort']. When an update fails, a translated version of the message being logged is displayed to the user stating where the update failed at.

I agree with @kim.pepper that this seems a bit unnecessary since error messages being logged to watchdog currently aren't being translated.

Hope this helps!

cliddell’s picture

Assigned: abhijeet.kumar2107 » Unassigned

@abhijeet.kumar2107 Thank you for expressing interest in this ticket! I'm unassigning this ticket in accordance with this entry from the Drupal.org community wiki:

Contributors to Drupal Core are discouraged from using the "assigned to" field unless they have a very good reason to use it. In most cases it is more appropriate to create a comment indicating that you are working on the issue and specifying which part of the issue you are working on. Then update the issue from time to time with reports on any progress made or roadblocks encountered.

cliddell’s picture

@xjm @kim.pepper in response to #75 and #76, could we potentially leverage the already existing Error::renderExceptionSafe() method which is being used to accomplish a very similar task and just make inclusion of a backtrace optional using a second argument as a flag? Something like:

  public static function renderExceptionSafe(\Throwable $exception, bool $include_backtrace = TRUE) {
    $decode = static::decodeException($exception);
    $error_message_format = self::DEFAULT_ERROR_MESSAGE;
    if ($include_backtrace) {
      $error_message_format .= ' <pre class="backtrace">@backtrace</pre>';
      $backtrace = $decode['backtrace'];
      // Remove 'main()'.
      array_shift($backtrace);
      $decode['@backtrace'] = Error::formatBacktrace($backtrace);
    }
    // Remove unnecessary replacement variables.
    unset($decode['severity_level'], $decode['backtrace'], $decode['@backtrace_string']);
    return new FormattableMarkup($error_message_format, $decode);
  }

By making the second argument optional and default to TRUE, we can maintain backwards compatibility with existing calls to Error::renderExceptionSafe() and consolidate the implementations, rather than creating another method to accomplish the same task only without a backtrace.

kim.pepper’s picture

Looking back, I don't think passing the $error argument to format a message is going to work. We need to let the logger do that.

t() can't translate a constant

Let's just convert the constant to a static function then, as @xjm suggested.

cliddell’s picture

Okay, just a question for clarity then: If we create a translatedDefaultError() method should we call t() a second time at the call site or pass the substitutions directly to the new static method.

Using the first example from update.inc:

// Original
$ret['#abort'] = ['success' => FALSE, 'query' => t(Error::DEFAULT_ERROR_MESSAGE, $variables)];
// Without passing substitutions
$ret['#abort'] = ['success' => FALSE, 'query' => t(Error::translatedDefaultError(), $variables)];
// Passing substitutions
$ret['#abort'] = ['success' => FALSE, 'query' => Error::translatedDefaultError($variables)];
neclimdul’s picture

I believe the suggestion is to just use the return from the method without another call. The problem wouldn't be that t the function can't translate the input, At runtime the constant will go into it as a string like any other which is why tests pass. The problem would have to be in how we generate translations and that static analysis I guess doesn't handle constants.

I think that's kind of a weird arbitrary limitation but that might be beside the point. Something feels kinda off about that sort of method specifically for the installer. At the very least translating errors is something we never do with this one weird exception. Why does this exception exist? If I remember correctly we don't translate errors so you can google them consistently regardless of your language. I don't know why the installer would be an exception to this rule and maybe it shouldn't be.

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.

nwom’s picture

StatusFileSize
new20.81 KB

#73 no longer applies to D9.1.x. Here is a re-roll.

daffie’s picture

Status: Needs work » Needs review
neclimdul’s picture

StatusFileSize
new21 KB

Doesn't look like that re-roll worked. Here's another attempt. Most of the conflicts where from #2807743: Switch from deprecation notice to warning for non-standard placeholders in FormattableMarkup::placeholderFormat() and #2949419: Pass the raw exception to logger implementations which where also testing error/watchdog stuff. Additionally I think a small use conflict somewhere.

Also bumped the version in the deprecation to 9.2.x since that's where this is at.

neclimdul’s picture

Status: Needs review » Needs work

Looking back, xjm's feedback in #75 hasn't been addressed.

I did some quick digging into the "do we translate errors elsewhere" open question. Seems yes but its complicated.

https://localize.drupal.org/translate/languages/hu/translate?project=dru...

From what I can tell, anything going through the logger also gets translated? Does this also not support constants/variables? That would be pretty frustrating and easy to mess up. How do we answer that? I don't know how translatable strings are parsed out of core anymore.

eelkeblok’s picture

Just to clarify on that last question, the issue is exactly what you were saying in #85; the guideline to only enter the "raw" English text in translation function is because of the way these originals are extracted from source. I am not intimate with the code responsible (e.g. in the PotX project) but I think "static analysis" is giving it more credit than it deserves :) I think it is basically just doing a regex match on a handful of patterns. One of those is log entries, because DB log (?) will translate error messages. I'm not actually positive it is DB log where that happens, but it would seem a logical place and I do know for a fact my Dutch sites all have translated log entries in the recent logs :)

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

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

voleger made their first commit to this issue’s fork.

voleger’s picture

Rerolled #89 against 9.3.x
Updated deprecation message
Added more replacements

andypost’s picture

Looks like new loggers (services) could use separate issue

neclimdul’s picture

@andypost which services? Been a while since I've looked at this but I don't see any new services, this is just replacing existing calls really.

andypost’s picture

Status: Needs work » Needs review

Sorry, probably I did read wrong patch

btw How to restart test? the failure looks unrelated

voleger’s picture

Please, review the MR. The test based on the MR was passed.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

daffie’s picture

Status: Needs review » Needs work

To "needs work" for the unresolved thread on the MR.

voleger’s picture

Version: 9.4.x-dev » 10.0.x-dev
voleger’s picture

Status: Needs work » Needs review

Addressed #101

daffie’s picture

Issue summary: View changes
Status: Needs review » Needs work

All code changes look good to me.
The only thing I am now missing is a deprecation message test for the deprecated function. Sorry, I did not check for it earlier.
After that it is RTBC for me.

kim.pepper’s picture

Status: Needs work » Needs review

Adds a deprecation test.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

All code changes look good to me.
The Is and the CR are in order.
For me it is RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

The

9.4.0 or 10.0.0 and deprecated in Drupal 11

is not correct. I've chatted with the release managers and confirmed that we can deprecate in Drupal 9.4 and remove in Drupal 10.

Let's do that here - it means we have one whole release less carrying around deprecated code. We should only deprecate for Drupal 11 now if something is super-disruptive. This change is not. You can remove the usages already in 9.3.x in contrib and custom code because everything you need already exists.

Can we get one patch deprecating in 9.4.x and one patch removing everything for 10.0.x. Thanks!

berdir’s picture

Hm. It might not be complicated to replace, but it's a function that is used a lot. Not just in contrib (38 pages of results on http://grep.xnddx.ru/search?text=watchdog_exception&filename=), it's also going to be used a lot in custom code as it's the default thing to do in a try/catch.

And all it costs us to keep is a 15loc function in a file that we won't be able to remove in D10 anyway.

And last, it's going to cause more logger channels to be injected in a way that's bad for performance (factory services or calling getLogger(), which is bad for performance. FWIW, the current patch doesn't actually use DI for logger/logger factory in any place that still used this.

So IMHO, we should not push this.

kim.pepper’s picture

And last, it's going to cause more logger channels to be injected in a way that's bad for performance (factory services or calling getLogger(), which is bad for performance.

watchdog_exception() is already calling \Drupal::logger($type)->log($severity, $message, $variables); but are you saying we'd be moving away from lazy-loading the logging service and that would be bad for performance?

berdir’s picture

You don't have to, you can just inject the logger factory and get the channel only when needed. But it's a pretty common pattern to do that already in the constructor as it looks cleaner. Or define a service with a factory which is pretty common in core and copied a lot. Most of those logs are triggered very rarely, so anything we do already upfront is a tiny to a little bit more expensive, yes.

I'm not saying that means we shouldn't do it, just that there is IMHO not a good reason to force this for a D10 removal, there's very little benefit (and even a tiny bit of extra cost depending on how it's done) to force everyone to remove it in the next few months.

Also, it's actually not really backwards compatible to do now, not if you want to use that new constant?

mfb’s picture

Yes it'd be nice to wait until the new constant is available to ask module developers to update their exception logging code (the replacement code is fairly verbose even w/ the new constant :)

alexpott’s picture

You don't have to, you can just inject the logger factory and get the channel only when needed. But it's a pretty common pattern to do that already in the constructor as it looks cleaner. Or define a service with a factory which is pretty common in core and copied a lot. Most of those logs are triggered very rarely, so anything we do already upfront is a tiny to a little bit more expensive, yes.

I think we need make it easier to do the correct thing with injecting loggers. It should be possible to inject '@logger.channel.php' and it not be unnecessarily costly if it is not used.

kim.pepper’s picture

So to move forward on this issue, are we going to deprecate in 9.4.x and remove in 11.0.x?

alexpott’s picture

@kim.pepper - I'm not sure - I think we might need to re-evaluate the logging system architecture. It feels as if the architecture is wrong if injecting a logger is viewed as something that is expensive.

kim.pepper’s picture

Is that from performance analysis or just a hunch?

mfb’s picture

In my testing, there is some minor memory and time savings if you can complete a request without any loggers/logger channels being initialized. But I'd assume there is lower hanging fruit as far as performance fixes go. :)

With database logging as the only enabled logger, injecting the first logger channel takes 8376 bytes memory and ~178 microseconds. These numbers increase with each logger module that is enabled.

Injecting an additional logger channel was another 616 bytes of memory and ~9 microseconds, so these are very low impact.

mfb’s picture

As far as when to commit this, I was thinking it would be excellent if the new constant could go in ASAP - like, a 9.3.x point release, if that's allowed? That way developers have the option to start fixing their error logging code now, even before the function is deprecated.

kim.pepper’s picture

I created an issue #3260044: Add a constant for watchdog_exception message if that helps to get this in.

kim.pepper’s picture

daffie’s picture

Issue tags: +Needs reroll
joachim’s picture

Removing procedural code is good, but watchdog_exception() was nice DX. We're replacing a single call with a fair amount of boilerplate.

Couldn't we have something like:

\Drupal::logger('update')->logException($exception);
kim.pepper’s picture

re: #121

Couldn't we have something like:
\Drupal::logger('update')->logException($exception);

Were are using the PSR-3 Logger interface, so we can just add new methods to it.

We need an issue summary update for the pros and cons of the different approaches, as we've been through this discussion already.

anybody’s picture

Re #121, #122 @joachim and @kim.pepper: Just had EXACTLY the same idea! That would be really a great DX solution! +++1 :)

The proposed solution from https://www.drupal.org/node/2932520 is a drawback.
My hope was that
\Drupal::logger('update')->logException($exception); was already existing...

joachim’s picture

> Were are using the PSR-3 Logger interface, so we can just add new methods to it.

I can't tell whether that is a typo or not -- should it say "we CAN'T add new methods" or that we can?

If we can't, then we can still make the DX better, by using the splat operator to unpack the arguments to error(), like this:

\Drupal::logger('update')->error(...Error::decodeException($exception));

// decodeException changes to return an array of all the params required by error()

That makes it a 1-liner and no need for the constant for the message string.

mfb’s picture

Seems like changing the decodeException return array that way would be an unpleasant BC break (although it is of course allowed in a major version); a new method seems less prone to cause problems (code that is compatible across versions can check if method exists just as it would check if the constant exists).

joachim’s picture

I couldn't remember if decodeException() was new in this issue or already existed. Another method, in the latter case. The use of the splat with a helper method which provides all the parameters to error() is the main thing :)

kim.pepper’s picture

I'm +1 to a method that can be passed to a logger with a splat. I think it would need to be a new method though.

andregp made their first commit to this issue’s fork.

andregp’s picture

Status: Needs work » Needs review

I did a reroll on the MR to make it mergeable again, then created a new method (#127) to pass the error using the splat operator (as suggested in #124 and #126)

joachim’s picture

Status: Needs review » Needs work

I've rebased this but not looked at the test failure.

bradjones1 made their first commit to this issue’s fork.

bradjones1’s picture

Version: 10.0.x-dev » 10.1.x-dev

We're against 10.1.x at this point; new MR squashing the prior changes and updating the dangling unreconciled diff.

bradjones1’s picture

Status: Needs work » Needs review
bradjones1’s picture

I'm not quite familiar enough with the testing environment to know how to fix the phpstan error:

Instantiated class Psr\Log\Test\TestLogger not found.

joachim’s picture

> We're against 10.1.x at this point; new MR squashing the prior changes and updating the dangling unreconciled diff.

Please don't do squashes of commits. It loses history, and it makes it harder to do future rebases.

I'll clean up the rebase conflict as soon as I have time.

joachim’s picture

Status: Needs review » Needs work

I've fixed the stray merge conflict and pushed to branch 2932518-deprecate-watchdogexception. That's still on 10.0.x, for the benefit of anyone wanting to patch their site.

I've then made a new branch rebased onto 10.1.x. The correct way to do this without using squash is:

> git rebase --onto 10.1.x 10.0.x

The new branch is 2932518-deprecate-watchdogexception-10.1. I've made a new MR for that.

Could the MR with the squashed code be closed?

Setting to needs work based on review comments.

Please make new commits on 2932518-deprecate-watchdogexception-10.1 (and optionally cherry-pick to 2932518-deprecate-watchdogexception).

joachim’s picture

ARGH I am *sure* I'd cleaned up those merge conflict markers that got left in!!!

Just cleaned up and pushed.

I looked at the test failures. One was the conflict markers.

The other is this, which I don't understand:

------ ----------------------------------------------------------------------
Line core/tests/Drupal/KernelTests/Core/Bootstrap/LegacyBootstrapTest.php
------ ----------------------------------------------------------------------
27 Instantiated class Psr\Log\Test\TestLogger not found.

I can see the class Psr\Log\Test\TestLogger in the codebase!

rpayanm made their first commit to this issue’s fork.

rpayanm’s picture

I rerolled the last patch.

bradjones1’s picture

Doing a "re-roll" on to a new MR is not helpful. Addressing the issues raised in #140 would be a positive contribution.

rpayanm’s picture

 ------ ---------------------------------------------------------------------- 
  Line   core/tests/Drupal/KernelTests/Core/Bootstrap/LegacyBootstrapTest.php  
 ------ ---------------------------------------------------------------------- 
  27     Instantiated class Psr\Log\Test\TestLogger not found.                 
         💡 Learn more at https://phpstan.org/user-guide/discovering-symbols   
 ------ ---------------------------------------------------------------------- 

@joachim I asked @xjm and told me that class was removed from the psr/log version that Drupal is using (PSR-4), so that's why the error.

rpayanm’s picture

joachim’s picture

bradjones1’s picture

StatusFileSize
new6.35 KB

Current MR diff for 9.5.x, if you're like me and not yet on D10 and can't use the MR diff for your project yet.

bradjones1’s picture

Sigh... that's not right at all.

bradjones1’s picture

StatusFileSize
new20.22 KB

This is for 9.5.x users.

xjm’s picture

We don't deprecate new things in 9.5.x anymore, so hiding the patch files. Also closed the redundant MRs for clarity. Thanks!

We don't need to add a test dependency, I don't think; we can construct our own test logger. Edit: although the related issue for auto updates seems to express a different opinion.

bradjones1’s picture

StatusFileSize
new18.81 KB

Updated for latest 9.5.x, not for review but reference.

kim.pepper’s picture

#3321905: Add colinodell/psr-test-logger to core's dev dependencies was committed so I think this unblocks the test logger issues.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new33.96 KB

Revisiting this issue after a long while and found it difficult to work out what MRs to use etc. Added a bit more dependency injection of the logger, and switched to the new test logger.

kim.pepper’s picture

StatusFileSize
new34.17 KB
new1.82 KB

Code style fixes.

smustgrave’s picture

Status: Needs review » Needs work

#122 you tagged for issue summary update that still needed?

Patch #157 appears to have some failures.

The service "drupal.proxy_original_service.module_installer" has a dependen
20:35:49 cy on a non-existent service "logger.channel.system". Did you mean one of t
20:35:49 hese: "logger.channel.default", "logger.channel.php", "logger.channel.image
20:35:49 ", "logger.channel.cron", "logger.channel.file", "logger.channel.form", "lo
20:35:49 gger.channel.security"?

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new39.38 KB
new5.93 KB

Looks like the module installer has undeclared dependency on the 'system' logger channel. I've changed this to 'default' as this causes issues with the proxy.

Also reworked \Drupal\Tests\update\Unit\UpdateFetcherTest to use the new test logger which greatly simplifies this test.

Status: Needs review » Needs work

The last submitted patch, 159: 2932518-159.patch, failed testing. View results

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new39.58 KB
new817 bytes

Fixes `workspaces.association` service definition.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: -Needs reroll

Assuming this no longer needs a reroll.

Only think I saw was that there are 2 instances of

is deprecated in drupal:10.0.0

But the others are deprecation in 10.1

Shouldn't they be the same?

andypost’s picture

Status: Needs work » Needs review
StatusFileSize
new17.7 KB
new38.29 KB

Patch cleaning up DI and fix #162

  1. +++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
    @@ -87,7 +88,7 @@ protected function menuLinksRebuild() {
    -        watchdog_exception('menu', $e);
    +        \Drupal::logger('menu')->error(...Error::decodeExceptionWithMessage($e));
    

    I think logger needs injection here

  2. +++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php
    @@ -152,7 +153,7 @@ public function dump(array $options = []): string {
    -      watchdog_exception('Routing', $e);
    +      \Drupal::logger('Routing')->error(...Error::decodeExceptionWithMessage($e));
    

    may need DI too

  3. +++ b/core/modules/system/src/SecurityAdvisories/SecurityAdvisoriesFetcher.php
    @@ -321,7 +322,7 @@ protected function doRequest(int $timeout): string {
    -        watchdog_exception('system', $exception);
    +        \Drupal::logger('system')->error(...Error::decodeExceptionWithMessage($exception));
    

    already using system logger

  4. +++ b/core/modules/update/src/ProjectSecurityData.php
    @@ -224,11 +225,9 @@ private function getAdditionalSecurityCoveredMinors($security_covered_version) {
    +        \Drupal::logger('update')->error(
    

    no DI found

smustgrave’s picture

Status: Needs review » Needs work

@andypost not sure if you meant to leave in Review for the points you mentioned.

andypost’s picture

I set NR for my changes, also fixed #163-3

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new44.98 KB
new9.44 KB

Fixes for #163 1,2 & 3.

4. This is a value object, and doesn't need DI IMO.

Status: Needs review » Needs work

The last submitted patch, 166: 2932518-166.patch, failed testing. View results

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new51.99 KB
new7.01 KB

Fixed the RouteProviderTest which needed to pass the logger to the constructor.

I have changed the order of arguments in \Drupal\Core\Routing\MatcherDumper::__construct() so the $table = 'router' param is last. This is because we want the logger to be required in 10.0.x but a required param can't come after an optional one. This $table param is only for tests as far as I can tell. Is there a better way to handle this?

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs Review Queue Initiative

Can we remove the IS update tag?

Also applied patch #168 and there were 2 instances of watchdog_exception I found that may need replacing.

In SecurityAdvisoriesFetcherTest

  /**
   * The log messages from watchdog_exception.
   *
   * @var string[]
   */
  protected $watchdogExceptionMessages = [];

Then in CronSuspendQueueDelayTest
// Capture logs to watchdog_exception().

Everything else looked good to me.

kim.pepper’s picture

Status: Needs work » Needs review
Issue tags: -Needs issue summary update
StatusFileSize
new54.37 KB
new2.38 KB

#169 Replaced comment instances of watchdog_exception and removed IS update tag.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Thanks @kim.pepper!

dagmar’s picture

The patch looks good, but in my opinion the change record needs to be a bit more verbose on other ways the watchdog_exception needs to be replaced:

Only the 2 first parameters of this function are covered:

function watchdog_exception($type, Exception $exception, $message = NULL, $variables = [], $severity = RfcLogLevel::ERROR, $link = NULL)

There is no example of how to use $link and other errors than RfcLogLevel::ERROR.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/lib/Drupal/Core/Utility/Error.php
@@ -75,6 +75,25 @@ public static function decodeException($exception) {
+  /**
+   * Decodes an exception and retrieves the correct caller with a message.
+   *
+   * @param \Exception|\Throwable $exception
+   *   The exception object that was thrown.
+   * @param string $message
+   *   A custom error message.
+   *
+   * @return array
+   *   An error in the format expected by Drupal::logger()->error().
+   */
+  public static function decodeExceptionWithMessage($exception, $message = Error::DEFAULT_ERROR_MESSAGE) {
+    $decode = static::decodeException($exception);

+++ b/core/modules/system/src/SecurityAdvisories/SecurityAdvisoriesFetcher.php
@@ -152,7 +153,7 @@ public function getSecurityAdvisories(bool $allow_outgoing_request = TRUE, int $
+        $this->logger->error('Invalid security advisory format: ' . Json::encode($advisory_data), Error::decodeException($unexpected_value_exception));

There's no cases of ever passing a $message in core so it seems this is unnecessary. Everywhere it could be used we see: $this->logger->error('Invalid security advisory format: ' . Json::encode($advisory_data), Error::decodeException($unexpected_value_exception));

So perhaps this method that returns a tuple is unnecessary.

Did we consider adding Error::logException($logger, \Exception|\Throwable $exception, string $message = Error::DEFAULT_ERROR_MESSAGE, array $additional_variables = []) :void

The implementation could then be:

$logger->error($message, static::decodeException($exception) + $additional_variables);
berdir’s picture

  1. +++ b/core/lib/Drupal/Core/Extension/ModuleInstaller.php
    @@ -302,7 +310,9 @@ public function install(array $module_list, $enable_dependencies = TRUE) {
                     }
                     catch (EntityStorageException $e) {
    -                  watchdog_exception('system', $e, 'An error occurred while notifying the creation of the @name field storage definition: "@message" in %function (line %line of %file).', ['@name' => $storage_definition->getName(), '@message' => $e->getMessage()]);
    +                  $variables = Error::decodeException($e);
    +                  $variables['@name'] = $storage_definition->getName();
    +                  $this->logger->error('An error occurred while notifying the creation of the @name field storage definition: "!message" in %function (line %line of %file).', $variables);
                     }
    

    This is reverting back to a !placeholder, that doesn't exist and is wrong. Not sure why that's not failing, not tested?

  2. +++ b/core/modules/system/src/SecurityAdvisories/SecurityAdvisoriesFetcher.php
    @@ -152,7 +153,7 @@ public function getSecurityAdvisories(bool $allow_outgoing_request = TRUE, int $
             // this is highly unlikely we should still display the items that are
             // in the correct format.
    -        watchdog_exception('system', $unexpected_value_exception, 'Invalid security advisory format: ' . Json::encode($advisory_data));
    +        $this->logger->error('Invalid security advisory format: ' . Json::encode($advisory_data), Error::decodeException($unexpected_value_exception));
             continue;
    

    this was not introduced here, but this usage is wrong. the message must never by dynamic. The messsage is passed to t(), every unique combination will result in a separate, translatable string and fill up the locale tables. the advisory data needs to be put in the variables and passed together with the decoded exception.

kim.pepper’s picture

Assigned: Unassigned » kim.pepper

I'll look at implementing #173

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new55.61 KB
new14 KB

Thanks for the reviews.

Implemented Error::logException() as suggested in #173 and made a few conversions that simplify the code. I removed the decodeExceptionWithMessage() function. Great improvement!

Fixed issues with #174 1 & 2.

kim.pepper’s picture

StatusFileSize
new55.72 KB
new1.25 KB

I updated the CR to address feedback in #172 and realised we had hard-coded the log level to ERROR where previously this was an option. I've added an extra param to Error::logException() to take a LogLevel defaulting to LogLevel::ERROR.

The last submitted patch, 176: 2932518-176.patch, failed testing. View results

kim.pepper’s picture

StatusFileSize
new56.76 KB
new3.48 KB

Missed a couple of things which are now fixed:

  1. watchdog_exception takes a RfcLogLevel $severity (int) while our new Error::logException() takes a PsrLevel $level (string). Added a conversion because I assume we want to use PSR level with our PSR logger interface to be consistent.
  2. I converted watchdog_exception() to use Error::logException() too and updated the deprecation message
kim.pepper’s picture

StatusFileSize
new58.76 KB
new2 KB

#3340712: Add Single Directory Components as a new experimental module Added a new call to watchdog_exception. Fixing that.

kim.pepper’s picture

Issue summary: View changes

The last submitted patch, 177: 2932518-177.patch, failed testing. View results

The last submitted patch, 179: 2932518-178.patch, failed testing. View results

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Wow quick turnaround on the new sdc module.

These ones are sometimes above my head but from what I can tell all the points 173 + 174 have been addressed. So don't mind remarking.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/includes/bootstrap.inc
    @@ -124,21 +125,37 @@ function t($string, array $args = [], array $options = []) {
    +  $variables = [];
    

    This is a breaking change - we should not be setting $variables to an empty array here.

  2. +++ b/core/lib/Drupal/Core/Database/database.api.php
    @@ -202,7 +202,7 @@
    - *     watchdog_exception('type', $e);
    + *     \Drupal::logger('type')->error(...Error::decodeExceptionWithMessage($e));
    

    Let's change this to use the new Error::logException() too.

  3. +++ b/core/modules/sdc/sdc.services.yml
    @@ -8,6 +8,7 @@ services:
       Drupal\sdc\Twig\TwigComponentLoader:
         arguments:
           - '@plugin.manager.sdc'
    +      - '@logger.channel.sdc'
         tags:
           - { name: twig.loader, priority: 5 }
     
    @@ -53,3 +54,8 @@ services:
    
    @@ -53,3 +54,8 @@ services:
       Drupal\sdc\Component\ComponentValidator:
         calls:
           - [setValidator, []]
    +
    +  logger.channel.sdc:
    +    class: Drupal\Core\Logger\LoggerChannel
    +    factory: [ '@logger.factory', 'get' ]
    +    arguments: [ 'sdc' ]
    

    This module will be added to the core library and will not be a module. I think we should take this opportunity to chose an existing logger channel. I think this message should end up in the default logger channel. Ie. logger.channel.default.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new58.57 KB
new1.67 KB

Re: #185 Thanks for the review.

  1. Fixed
  2. Fixed
  3. Fixed
alexpott’s picture

Status: Needs review » Needs work
+++ b/core/includes/bootstrap.inc
@@ -136,9 +143,18 @@ function watchdog_exception($type, Exception $exception, $message = NULL, $varia
-  $variables += Error::decodeException($exception);
-
-  \Drupal::logger($type)->log($severity, $message, $variables);
+  $level = match ($severity) {
+    RfcLogLevel::EMERGENCY => LogLevel::EMERGENCY,
+    RfcLogLevel::ALERT => LogLevel::ALERT,
+    RfcLogLevel::CRITICAL => LogLevel::CRITICAL,
+    RfcLogLevel::WARNING => LogLevel::WARNING,
+    RfcLogLevel::NOTICE => LogLevel::NOTICE,
+    RfcLogLevel::INFO => LogLevel::INFO,
+    RfcLogLevel::DEBUG => LogLevel::DEBUG,
+    default => LogLevel::ERROR,
+  };
+
+  Error::logException(\Drupal::logger($type), $exception, $message, $variables, $level);

I was thinking about asking for a test about passing variables into the watchdog_exception() function because we broke it... and this has lead me to wondering why are we changing the deprecated code? I think in general we should only do this when we have to and I can't see a reason why we have to here.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new57.8 KB
new1.13 KB

re: #187 rolled back changes to watchdog_exception().

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

From what I can tell #185 + #187 have been addressed. Don't mind moving this forward.

larowlan’s picture

Status: Reviewed & tested by the community » Needs work
  1. +++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php
    @@ -51,13 +53,18 @@ class MatcherDumper implements MatcherDumperInterface {
    -  public function __construct(Connection $connection, StateInterface $state, $table = 'router') {
    +  public function __construct(Connection $connection, StateInterface $state, protected ?LoggerInterface $logger = NULL, $table = 'router') {
         $this->connection = $connection;
         $this->state = $state;
    -
    +    if ($this->logger === NULL) {
    

    This won't work how we're expecting here.

    If someone is extending it, their third param will be a string (The table name) and they will get a fatal here rather than a deprecation error.

    I think we need to remove the type hint and use an instanceof check too.

    There are four scenarios here:
    * two params
    * three params, where third is a string
    * three params, where third is a logger
    * four params

    We're missing handling for the second case

  2. +++ b/core/lib/Drupal/Core/Utility/Error.php
    @@ -75,6 +77,24 @@ public static function decodeException($exception) {
    +  public static function logException(LoggerInterface $logger, \Exception | \Throwable $exception, string $message = Error::DEFAULT_ERROR_MESSAGE, array $additional_variables = [], string $level = LogLevel::ERROR): void {
    

    Exception extends Throwable, any reason we needed the union here instead of just using Throwable?

Unrelated, but I'm not too keen on the use of colinodell/psr-test-logger when we already had \Symfony\Component\Debug\BufferingLogger - I commented in #3321905: Add colinodell/psr-test-logger to core's dev dependencies to clarify why we need both.

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new61.27 KB
new4.84 KB

Thanks for the feedback in #190

  1. Fixed the logic of the constructor and added a legacy deprecation test for the 4 different scenarios.
  2. Replaced with just \Throwable and added typehint.

Had a quick look at \Symfony\Component\Debug\BufferingLogger and doesn't provide the ability to read back the logs AFAIK. It does log to error_log on __destruct() so maybe that's useful?

kim.pepper’s picture

StatusFileSize
new61.32 KB
new579 bytes

Oops. Forgot to actually set the logger.

kim.pepper’s picture

StatusFileSize
new60.77 KB
new1.71 KB

Replaced typehint on decodeException() instead of logException()

The last submitted patch, 192: 2932518-192.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 193: 2932518-193.patch, failed testing. View results

mstrelan’s picture

Couple nits and questions but NW on the comment about $table. Otherwise LGTM.

+++ b/core/includes/bootstrap.inc
@@ -7,9 +7,9 @@
+use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\Core\Test\TestDatabase;
 use Drupal\Core\Utility\Error;
-use Drupal\Core\StringTranslation\TranslatableMarkup;

@@ -124,8 +124,14 @@ function t($string, array $args = [], array $options = []) {
+ * @see https://www.drupal.org/node/2932520

+++ b/core/lib/Drupal/Core/EventSubscriber/MenuRouterRebuildSubscriber.php
@@ -52,12 +54,18 @@ class MenuRouterRebuildSubscriber implements EventSubscriberInterface {
+      $this->logger = \Drupal::service('logger.channel.menu');

Doesn't bother me but re-ordering this is technically out of scope.

  1. +++ b/core/lib/Drupal/Core/Routing/MatcherDumper.php
    @@ -51,14 +60,25 @@ class MatcherDumper implements MatcherDumperInterface {
    +    if (!is_null($table)) {
    +      $this->tableName = $table;
    +    }
    

    $table is not nullable so this condition is unreachable

  2. +++ b/core/lib/Drupal/Core/Utility/Error.php
    @@ -75,6 +77,24 @@ public static function decodeException($exception) {
    +  /**
    +   * Log a formatted exception message to the provided logger.
    +   *
    +   * @param \Psr\Log\LoggerInterface $logger
    +   *   The logger.
    +   * @param \Exception|\Throwable $exception
    +   *   The exception.
    +   * @param string $message
    +   *   (optional) The message.
    +   * @param array $additional_variables
    +   *   (optional) Any additional variables.
    +   * @param string $level
    +   *   The log level.
    +   */
    +  public static function logException(LoggerInterface $logger, \Exception | \Throwable $exception, string $message = Error::DEFAULT_ERROR_MESSAGE, array $additional_variables = [], string $level = LogLevel::ERROR): void {
    +    $logger->log($level, $message, static::decodeException($exception) + $additional_variables);
    +  }
    

    Do we need \Exception here since it extends \Throwable?

  3. +++ b/core/lib/Drupal/Core/Utility/Error.php
    @@ -75,6 +77,24 @@ public static function decodeException($exception) {
    +   * @param string $level
    +   *   The log level.
    

    Is it worth mentioning that this should be one of the levels defined in \Psr\Log\LogLevel?

kim.pepper’s picture

Status: Needs work » Needs review
StatusFileSize
new60.51 KB
new2.3 KB

Thanks for the review @mstrelan! RE: #196:

Rolled back the import re-ordering.

  1. Fixed the logic to check for a null $this->tableName instead.
  2. Fixed in #193
  3. Updated to mention the PSR LogLevel.

Also fixed incorrect namespace for \Drupal\KernelTests\Core\Routing\LegacyMatcherDumperTest after moving it.

larowlan’s picture

FWIW \Symfony\Component\Debug\BufferingLogger::cleanLogs is how you read them back. But regardless, out of scope here - the horse has bolted - the discussion can happen elsewhere

mstrelan’s picture

Status: Needs review » Reviewed & tested by the community

All feedback since #190 has been addressed, marking RTBC.

larowlan’s picture

Saving issue credits.

  • larowlan committed f9bf0787 on 10.1.x
    Issue #2932518 by kim.pepper, joachim, bradjones1, voleger, cliddell,...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed to 10.1.x and published the change record.

kim.pepper’s picture

Status: Fixed » Closed (fixed)

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