From 3fca1b195399afe261898659ace92e6335af4101 Mon Sep 17 00:00:00 2001 From: Larry Garfield <larry@garfieldtech.com> Date: Wed, 9 Jun 2021 13:18:46 -0500 Subject: [PATCH] [BUGFIX] Make logger usage PSR-3 compliant PSR-3 has specific rules around interpolation: Messages may provide placeholders like {foo} and writers should substitute these in the messages if a context array with such a key is provided. Let's use placeholders correctly. Resolves: #94315 Related: #94356 Releases: master Change-Id: I2c285e84f1832c80828861369e99af9aff6cd267 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/69425 Tested-by: core-ci <typo3@b13.com> Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> --- .../Classes/Authentication/PasswordReset.php | 11 ++-- .../File/ImageProcessController.php | 2 +- .../StandardContentPreviewRenderer.php | 11 ++-- .../Security/EmailLoginNotification.php | 12 ++-- .../Classes/Utility/BackendUtility.php | 7 ++- .../View/AuthenticationStyleInformation.php | 14 ++--- .../backend/Classes/View/PageLayoutView.php | 11 ++-- .../Authentication/PasswordResetTest.php | 33 +++++++++-- .../AbstractUserAuthentication.php | 43 +++++++++----- .../Authentication/AuthenticationService.php | 15 +++-- .../core/Classes/DataHandling/DataHandler.php | 2 +- .../core/Classes/Database/Connection.php | 4 +- .../core/Classes/Database/ReferenceIndex.php | 10 ++-- .../Domain/Repository/PageRepository.php | 7 +-- .../Error/AbstractExceptionHandler.php | 48 +++++++++++----- typo3/sysext/core/Classes/Log/LogManager.php | 12 +++- .../Classes/Log/Writer/AbstractWriter.php | 56 +++++++++++++++++++ .../Classes/Log/Writer/DatabaseWriter.php | 13 ++--- .../core/Classes/Log/Writer/FileWriter.php | 50 +++++++---------- .../Classes/Log/Writer/PhpErrorLogWriter.php | 21 +++---- .../core/Classes/Log/Writer/SyslogWriter.php | 25 +++++---- .../sysext/core/Classes/Mail/MemorySpool.php | 2 +- .../core/Classes/Mail/TransportFactory.php | 2 +- .../PageTitle/PageTitleProviderManager.php | 24 ++++---- .../core/Classes/Resource/Index/Indexer.php | 7 ++- .../Resource/ProcessedFileRepository.php | 11 ++-- .../Classes/Resource/StorageRepository.php | 10 +--- .../Classes/Session/UserSessionManager.php | 4 +- .../core/Classes/Type/File/ImageInfo.php | 2 +- .../TypoScript/Parser/TypoScriptParser.php | 4 +- .../core/Classes/Utility/GeneralUtility.php | 16 +++--- ...seProperPSR-3LoggingMessagesAndContext.rst | 37 ++++++++++++ .../Unit/Error/DebugExceptionHandlerTest.php | 23 ++++++-- .../Error/ProductionExceptionHandlerTest.php | 21 +++++-- .../Classes/Object/Container/Container.php | 12 +++- .../extbase/Classes/SignalSlot/Dispatcher.php | 13 ++--- .../Unit/Object/Container/ContainerTest.php | 45 +++++++++------ .../Classes/Utility/InstallUtility.php | 20 +++---- .../Validation/RedirectUrlValidator.php | 2 +- .../Classes/Controller/FileListController.php | 9 ++- .../FrontendUserAuthentication.php | 4 +- .../ContentObject/ContentObjectRenderer.php | 20 ++++--- .../Exception/ProductionExceptionHandler.php | 9 +-- .../TypoScriptFrontendController.php | 20 ++++--- .../Middleware/PageArgumentValidator.php | 2 +- .../Classes/Resource/FileCollector.php | 17 +++--- .../Classes/Service/LanguagePackService.php | 11 ++-- .../Classes/Task/ValidatorTask.php | 4 +- .../Classes/Service/RedirectService.php | 6 +- .../scheduler/Classes/Example/TestTask.php | 2 +- typo3/sysext/scheduler/Classes/Scheduler.php | 27 ++++++--- .../scheduler/Classes/Task/AbstractTask.php | 26 +++++---- .../Notification/StageChangeNotification.php | 11 ++-- .../Classes/Service/GridDataService.php | 6 +- 54 files changed, 523 insertions(+), 313 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Important-94315-UseProperPSR-3LoggingMessagesAndContext.rst diff --git a/typo3/sysext/backend/Classes/Authentication/PasswordReset.php b/typo3/sysext/backend/Classes/Authentication/PasswordReset.php index bd4f1492d93a..c431b8a96e41 100644 --- a/typo3/sysext/backend/Classes/Authentication/PasswordReset.php +++ b/typo3/sysext/backend/Classes/Authentication/PasswordReset.php @@ -117,7 +117,7 @@ class PasswordReset implements LoggerAwareInterface return; } if ($this->hasExceededMaximumAttemptsForReset($context, $emailAddress)) { - $this->logger->alert('Password reset requested for email "' . $emailAddress . '" . but was requested too many times.'); + $this->logger->alert('Password reset requested for email {email} but was requested too many times.', ['email' => $emailAddress]); return; } $queryBuilder = $this->getPreparedQueryBuilder(); @@ -159,7 +159,7 @@ class PasswordReset implements LoggerAwareInterface ->setTemplate('PasswordReset/AmbiguousResetRequested'); GeneralUtility::makeInstance(Mailer::class)->send($emailObject); - $this->logger->warning('Password reset sent to email address ' . $emailAddress . ' but multiple accounts found'); + $this->logger->warning('Password reset sent to email address {email} but multiple accounts found', ['email' => $emailAddress]); $this->log( 'Sent password reset email to email address %s but with multiple accounts attached.', SystemLogLoginAction::PASSWORD_RESET_REQUEST, @@ -195,7 +195,10 @@ class PasswordReset implements LoggerAwareInterface ->setTemplate('PasswordReset/ResetRequested'); GeneralUtility::makeInstance(Mailer::class)->send($emailObject); - $this->logger->info('Sent password reset email to email address ' . $emailAddress . ' for user ' . $user['username']); + $this->logger->info('Sent password reset email to email address {email} for user {username}', [ + 'email' => $emailAddress, + 'username' => $user['username'], + ]); $this->log( 'Sent password reset email to email address %s', SystemLogLoginAction::PASSWORD_RESET_REQUEST, @@ -339,7 +342,7 @@ class PasswordReset implements LoggerAwareInterface ->getConnectionForTable('be_users') ->update('be_users', ['password_reset_token' => '', 'password' => $this->getHasher()->getHashedPassword($newPassword)], ['uid' => $userId]); - $this->logger->info('Password reset successful for user ' . $userId); + $this->logger->info('Password reset successful for user {user_id)', ['user_id' => $userId]); $this->log( 'Password reset successful for user %s', SystemLogLoginAction::PASSWORD_RESET_ACCOMPLISHED, diff --git a/typo3/sysext/backend/Classes/Controller/File/ImageProcessController.php b/typo3/sysext/backend/Classes/Controller/File/ImageProcessController.php index 73bc5bbb9fd4..17adbcabbff9 100644 --- a/typo3/sysext/backend/Classes/Controller/File/ImageProcessController.php +++ b/typo3/sysext/backend/Classes/Controller/File/ImageProcessController.php @@ -58,7 +58,7 @@ class ImageProcessController implements LoggerAwareInterface ); } catch (\Throwable $e) { // Fatal error occurred, which will be responded as 404 - $this->logger->error(sprintf('Processing of file with id "%s" failed', $processedFileId), ['exception' => $e]); + $this->logger->error('Processing of file with id {processed_file} failed', ['processed_file' => $processedFileId, 'exception' => $e]); } return new HtmlResponse('', 404); diff --git a/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php b/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php index b72314fec914..507f55229887 100644 --- a/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php +++ b/typo3/sysext/backend/Classes/Preview/StandardContentPreviewRenderer.php @@ -321,12 +321,11 @@ class StandardContentPreviewRenderer implements PreviewRendererInterface, Logger } return $view->render(); } catch (\Exception $e) { - $this->logger->warning(sprintf( - 'The backend preview for content element %d can not be rendered using the Fluid template file "%s": %s', - $row['uid'], - $fluidTemplateFile, - $e->getMessage() - )); + $this->logger->warning('The backend preview for content element {uid} can not be rendered using the Fluid template file "{file}"', [ + 'uid' => $row['uid'], + 'file' => $fluidTemplateFile, + 'exception' => $e, + ]); if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] && $this->getBackendUser()->isAdmin()) { $view = GeneralUtility::makeInstance(StandaloneView::class); diff --git a/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php b/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php index 03058e9a4ad5..ca44e70b5e03 100644 --- a/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php +++ b/typo3/sysext/backend/Classes/Security/EmailLoginNotification.php @@ -103,7 +103,7 @@ class EmailLoginNotification implements LoggerAwareInterface * @param AbstractUserAuthentication $user * @param string|null $subjectPrefix */ - protected function sendEmail(string $recipient, AbstractUserAuthentication $user, string $subjectPrefix = null): void + protected function sendEmail(string $recipient, AbstractUserAuthentication $user, ?string $subjectPrefix = null): void { $headline = 'TYPO3 Backend Login notification'; $recipients = explode(',', $recipient); @@ -120,16 +120,18 @@ class EmailLoginNotification implements LoggerAwareInterface try { GeneralUtility::makeInstance(Mailer::class)->send($email); } catch (TransportException $e) { - $this->logger->warning('Could not send notification email to "' . $recipient . '" due to mailer settings error', [ + $this->logger->warning('Could not send notification email to "{recipient}" due to mailer settings error', [ + 'recipient' => $recipient, 'userId' => $user->user['uid'] ?? 0, 'recipientList' => $recipients, - 'exception' => $e + 'exception' => $e, ]); } catch (RfcComplianceException $e) { - $this->logger->warning('Could not send notification email to "' . $recipient . '" due to invalid email address', [ + $this->logger->warning('Could not send notification email to "{recipient}" due to invalid email address', [ + 'recipient' => $recipient, 'userId' => $user->user['uid'] ?? 0, 'recipientList' => $recipients, - 'exception' => $e + 'exception' => $e, ]); } } diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php index 1a80e8ecd57b..b105c8d89447 100644 --- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php +++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php @@ -987,7 +987,12 @@ class BackendUtility * The storage does not exist anymore * Log the exception message for admins as they maybe can restore the storage */ - self::getLogger()->error($e->getMessage(), ['table' => $tableName, 'fieldName' => $fieldName, 'referenceUid' => $referenceUid, 'exception' => $e]); + self::getLogger()->error($e->getMessage(), [ + 'table' => $tableName, + 'fieldName' => $fieldName, + 'referenceUid' => $referenceUid, + 'exception' => $e, + ]); } } diff --git a/typo3/sysext/backend/Classes/View/AuthenticationStyleInformation.php b/typo3/sysext/backend/Classes/View/AuthenticationStyleInformation.php index 6102b69fd150..148ef517cec5 100644 --- a/typo3/sysext/backend/Classes/View/AuthenticationStyleInformation.php +++ b/typo3/sysext/backend/Classes/View/AuthenticationStyleInformation.php @@ -48,10 +48,9 @@ class AuthenticationStyleInformation implements LoggerAwareInterface $backgroundImageUri = $this->getUriForFileName($backgroundImage); if ($backgroundImageUri === '') { - $this->logger->warning( - 'The configured TYPO3 backend login background image "' . htmlspecialchars($backgroundImageUri) . - '" can\'t be resolved. Please check if the file exists and the extension is activated.' - ); + $this->logger->warning('The configured TYPO3 backend login background image "{image_url}" can\'t be resolved. Please check if the file exists and the extension is activated.', [ + 'image_url' => $backgroundImageUri, + ]); return ''; } @@ -103,10 +102,9 @@ class AuthenticationStyleInformation implements LoggerAwareInterface } $logoUri = $this->getUriForFileName($logo); if ($logoUri === '') { - $this->logger->warning( - 'The configured TYPO3 backend login logo "' . htmlspecialchars($logoUri) . - '" can\'t be resolved. Please check if the file exists and the extension is activated.' - ); + $this->logger->warning('The configured TYPO3 backend login logo "{logo_url}" can\'t be resolved. Please check if the file exists and the extension is activated.', [ + 'logo_url' => $logoUri, + ]); return ''; } diff --git a/typo3/sysext/backend/Classes/View/PageLayoutView.php b/typo3/sysext/backend/Classes/View/PageLayoutView.php index ff4b1468d64b..a03a74dde455 100644 --- a/typo3/sysext/backend/Classes/View/PageLayoutView.php +++ b/typo3/sysext/backend/Classes/View/PageLayoutView.php @@ -1287,12 +1287,11 @@ class PageLayoutView implements LoggerAwareInterface } return $view->render(); } catch (\Exception $e) { - $this->logger->warning(sprintf( - 'The backend preview for content element %d can not be rendered using the Fluid template file "%s": %s', - $row['uid'], - $fluidTemplateFile, - $e->getMessage() - )); + $this->logger->warning('The backend preview for content element {uid} can not be rendered using the Fluid template file "{file}"', [ + 'uid' => $row['uid'], + 'file' => $fluidTemplateFile, + $e->getMessage(), + ]); if ($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] && $this->getBackendUser()->isAdmin()) { $view = GeneralUtility::makeInstance(StandaloneView::class); diff --git a/typo3/sysext/backend/Tests/Functional/Authentication/PasswordResetTest.php b/typo3/sysext/backend/Tests/Functional/Authentication/PasswordResetTest.php index 3637c25b0bb2..d82841cec116 100644 --- a/typo3/sysext/backend/Tests/Functional/Authentication/PasswordResetTest.php +++ b/typo3/sysext/backend/Tests/Functional/Authentication/PasswordResetTest.php @@ -18,6 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Backend\Tests\Functional\Authentication; use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; use TYPO3\CMS\Backend\Authentication\PasswordReset; use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Http\ServerRequest; @@ -25,6 +26,25 @@ use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase; class PasswordResetTest extends FunctionalTestCase { + protected LoggerInterface $logger; + + public function setUp(): void + { + parent::setUp(); + $this->logger = new class() implements LoggerInterface { + use LoggerTrait; + public array $records = []; + public function log($level, $message, array $context = []): void + { + $this->records[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context + ]; + } + }; + } + /** * @test */ @@ -130,12 +150,12 @@ class PasswordResetTest extends FunctionalTestCase $GLOBALS['TYPO3_CONF_VARS']['MAIL']['transport'] = 'null'; $emailAddress = 'duplicate@example.com'; $subject = new PasswordReset(); - $loggerProphecy = $this->prophesize(LoggerInterface::class); - $loggerProphecy->warning()->withArguments(['Password reset sent to email address ' . $emailAddress . ' but multiple accounts found'])->shouldBeCalled(); - $subject->setLogger($loggerProphecy->reveal()); + $subject->setLogger($this->logger); $context = new Context(); $request = new ServerRequest(); $subject->initiateReset($request, $context, $emailAddress); + self::assertEquals('warning', $this->logger->records[0]['level']); + self::assertEquals($emailAddress, $this->logger->records[0]['context']['email']); } /** @@ -150,12 +170,13 @@ class PasswordResetTest extends FunctionalTestCase $emailAddress = 'editor-with-email@example.com'; $username = 'editor-with-email'; $subject = new PasswordReset(); - $loggerProphecy = $this->prophesize(LoggerInterface::class); - $loggerProphecy->info()->withArguments(['Sent password reset email to email address ' . $emailAddress . ' for user ' . $username])->shouldBeCalled(); - $subject->setLogger($loggerProphecy->reveal()); + $subject->setLogger($this->logger); $context = new Context(); $request = new ServerRequest(); $subject->initiateReset($request, $context, $emailAddress); + self::assertEquals('info', $this->logger->records[0]['level']); + self::assertEquals($emailAddress, $this->logger->records[0]['context']['email']); + self::assertEquals($username, $this->logger->records[0]['context']['username']); } /** diff --git a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php index 9209db4e5722..e10d47f0225f 100644 --- a/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php +++ b/typo3/sysext/core/Classes/Authentication/AbstractUserAuthentication.php @@ -332,10 +332,11 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface false, $cookieSameSite ); - $this->logger->debug( - ($isRefreshTimeBasedCookie ? 'Updated Cookie: ' : 'Set Cookie: ') - . $sessionId . ($cookieDomain ? ', ' . $cookieDomain : '') - ); + $message = $isRefreshTimeBasedCookie ? 'Updated Cookie: {session}, {domain}' : 'Set Cookie: {session}, {domain}'; + $this->logger->debug($message, [ + 'session' => $sessionId, + 'domain' => $cookieDomain, + ]); } } @@ -358,7 +359,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface $match = []; $matchCnt = @preg_match($cookieDomain, GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY'), $match); if ($matchCnt === false) { - $this->logger->critical('The regular expression for the cookie domain (' . $cookieDomain . ') contains errors. The session is not shared across sub-domains.'); + $this->logger->critical('The regular expression for the cookie domain ({domain}) contains errors. The session is not shared across sub-domains.', ['domain' => $cookieDomain]); } elseif ($matchCnt) { $result = $match[0]; } @@ -424,7 +425,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface { $authConfiguration = $this->getAuthServiceConfiguration(); if (!empty($authConfiguration)) { - $this->logger->debug('Authentication Service Configuration found.', $authConfiguration); + $this->logger->debug('Authentication Service Configuration found.', ['auth_configuration' => $authConfiguration]); } // No user for now - will be searched by service below $tempuserArr = []; @@ -433,7 +434,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface $authenticated = false; // User want to login with passed login data (name/password) $activeLogin = false; - $this->logger->debug('Login type: ' . $this->loginType); + $this->logger->debug('Login type: {type}', ['type' => $this->loginType]); // The info array provide additional information for auth services $authInfo = $this->getAuthInfoArray(); // Get Login/Logout data submitted by a form or params @@ -445,7 +446,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface // $type,$action,$error,$details_nr,$details,$data,$tablename,$recuid,$recpid $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGOUT, SystemLogErrorClassification::MESSAGE, 2, 'User %s logged out', [$this->user['username']], '', 0, 0); } - $this->logger->info('User logged out. Id: ' . $this->userSession->getIdentifier()); + $this->logger->info('User logged out. Id: {session}', ['session' => $this->userSession->getIdentifier()]); $this->logoff(); } // Determine whether we need to skip session update. @@ -528,7 +529,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface if (empty($tempuserArr)) { $this->logger->debug('No user found by services'); } else { - $this->logger->debug(count($tempuserArr) . ' user records found by services'); + $this->logger->debug('{count} user records found by services', ['count' => count($tempuserArr)]); } } @@ -625,9 +626,15 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface if ($this->writeStdLog) { $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::LOGIN, SystemLogErrorClassification::MESSAGE, 1, 'User %s logged in from ###IP###', [$tempuser[$this->username_column]], '', '', ''); } - $this->logger->info('User ' . $tempuser[$this->username_column] . ' logged in from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR')); + $this->logger->info('User {username} logged in from {ip}', [ + 'username' => $tempuser[$this->username_column], + 'ip' => GeneralUtility::getIndpEnv('REMOTE_ADDR'), + ]); } else { - $this->logger->debug('User ' . $tempuser[$this->username_column] . ' authenticated from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR')); + $this->logger->debug('User {username} authenticated from {ip}', [ + 'username' => $tempuser[$this->username_column], + 'ip' => GeneralUtility::getIndpEnv('REMOTE_ADDR'), + ]); } } else { // Mark the current login attempt as failed @@ -725,7 +732,10 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface yield $serviceObj; } if (!empty($serviceChain)) { - $this->logger->debug($subType . ' auth services called: ' . implode(', ', $serviceChain)); + $this->logger->debug('{subtype} auth services called: {chain}', [ + 'subtype' => $subType, + 'chain' => $serviceChain, + ]); } } @@ -859,7 +869,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface */ public function logoff() { - $this->logger->debug('logoff: ses_id = ' . $this->userSession->getIdentifier()); + $this->logger->debug('logoff: ses_id = {session}', ['session' => $this->userSession->getIdentifier()]); $_params = []; foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'] ?? [] as $_funcRef) { @@ -982,7 +992,10 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface if (!is_array($variable)) { $variable = $this->uc; } - $this->logger->debug('writeUC: ' . $this->userid_column . '=' . (int)$this->user[$this->userid_column]); + $this->logger->debug('writeUC: {userid_column}={value}', [ + 'userid_column' => $this->userid_column, + 'value' => $this->user[$this->userid_column], + ]); GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->user_table)->update( $this->user_table, ['uc' => serialize($variable)], @@ -1089,7 +1102,7 @@ abstract class AbstractUserAuthentication implements LoggerAwareInterface public function setAndSaveSessionData($key, $data) { $this->userSession->set($key, $data); - $this->logger->debug('setAndSaveSessionData: ses_id = ' . $this->userSession->getIdentifier()); + $this->logger->debug('setAndSaveSessionData: ses_id = {session}', ['session' => $this->userSession->getIdentifier()]); $this->userSession = $this->userSessionManager->updateSession($this->userSession); } diff --git a/typo3/sysext/core/Classes/Authentication/AuthenticationService.php b/typo3/sysext/core/Classes/Authentication/AuthenticationService.php index d008373f6b7d..e4ff49abc98d 100644 --- a/typo3/sysext/core/Classes/Authentication/AuthenticationService.php +++ b/typo3/sysext/core/Classes/Authentication/AuthenticationService.php @@ -63,7 +63,10 @@ class AuthenticationService extends AbstractAuthenticationService $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::ATTEMPT, SystemLogErrorClassification::SECURITY_NOTICE, 2, 'Login-attempt from ###IP### for username \'%s\' with an empty password!', [ $this->login['uname'] ]); - $this->logger->warning(sprintf('Login-attempt from %s, for username \'%s\' with an empty password!', $this->authInfo['REMOTE_ADDR'], $this->login['uname'])); + $this->logger->warning('Login-attempt from {ip}, for username "{username}" with an empty password!', [ + 'ip' => $this->authInfo['REMOTE_ADDR'], + 'username' => $this->login['uname'], + ]); return false; } @@ -71,8 +74,9 @@ class AuthenticationService extends AbstractAuthenticationService if (!is_array($user)) { // Failed login attempt (no username found) $this->writelog(SystemLogType::LOGIN, SystemLogLoginAction::ATTEMPT, SystemLogErrorClassification::SECURITY_NOTICE, 2, 'Login-attempt from ###IP###, username \'%s\' not found!', [$this->login['uname']]); - $this->logger->info('Login-attempt from username \'' . $this->login['uname'] . '\' not found!', [ - 'REMOTE_ADDR' => $this->authInfo['REMOTE_ADDR'] + $this->logger->info('Login-attempt from username "{username}" not found!', [ + 'username' => $this->login['uname'], + 'REMOTE_ADDR' => $this->authInfo['REMOTE_ADDR'], ]); } else { $this->logger->debug('User found', [ @@ -184,7 +188,10 @@ class AuthenticationService extends AbstractAuthenticationService ['password' => $newPassword], ['uid' => $uid] ); - $this->logger->notice('Automatic password update for user record in ' . $table . ' with uid ' . $uid); + $this->logger->notice('Automatic password update for user record in {table} with uid {uid}', [ + 'table' => $table, + 'uid' => $uid, + ]); } /** diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php index f87ea3a62984..cca2176c773a 100644 --- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php +++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php @@ -5960,7 +5960,7 @@ class DataHandler implements LoggerAwareInterface $this->remapListedDBRecords_procInline($conf, $value, $uid, $table); break; default: - $this->logger->debug('Field type should not appear here: ' . $conf['type']); + $this->logger->debug('Field type should not appear here: {type}', ['type' => $conf['type']]); } } // If any fields were changed, those fields are updated! diff --git a/typo3/sysext/core/Classes/Database/Connection.php b/typo3/sysext/core/Classes/Database/Connection.php index 280d3b2043fd..a23c9bad0cba 100644 --- a/typo3/sysext/core/Classes/Database/Connection.php +++ b/typo3/sysext/core/Classes/Database/Connection.php @@ -104,8 +104,8 @@ class Connection extends \Doctrine\DBAL\Connection implements LoggerAwareInterfa } foreach ($this->prepareConnectionCommands as $command) { - if ($this->executeUpdate($command) === false) { - $this->logger->critical('Could not initialize DB connection with query "' . $command . '": ' . $this->errorInfo()); + if ($this->executeStatement($command) === false) { + $this->logger->critical('Could not initialize DB connection with query: {query}', ['query' => $command]); } } diff --git a/typo3/sysext/core/Classes/Database/ReferenceIndex.php b/typo3/sysext/core/Classes/Database/ReferenceIndex.php index db49131157cb..5caa60870526 100644 --- a/typo3/sysext/core/Classes/Database/ReferenceIndex.php +++ b/typo3/sysext/core/Classes/Database/ReferenceIndex.php @@ -928,10 +928,10 @@ class ReferenceIndex implements LoggerAwareInterface ->execute(); } catch (DBALException $e) { // Table exists in TCA but does not exist in the database - $msg = 'Table "' . - $tableName . - '" exists in TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.'; - $this->logger->error($msg, ['exception' => $e]); + $this->logger->error('Table {table_name} exists in TCA but does not exist in the database. You should run the Database Analyzer in the Install Tool to fix this.', [ + 'table_name' => $tableName, + 'exception' => $e, + ]); continue; } @@ -964,7 +964,7 @@ class ReferenceIndex implements LoggerAwareInterface // Subselect based queries only work on the same connection if ($refIndexConnectionName !== $tableConnectionName) { - $this->logger->error('Not checking table "' . $tableName . '" for lost indexes, "sys_refindex" table uses a different connection'); + $this->logger->error('Not checking table {table_name} for lost indexes, "sys_refindex" table uses a different connection', ['table_name' => $tableName]); continue; } diff --git a/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php b/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php index 74def23e1af4..99a042880f08 100644 --- a/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php +++ b/typo3/sysext/core/Classes/Domain/Repository/PageRepository.php @@ -1010,15 +1010,14 @@ class PageRepository implements LoggerAwareInterface } } // Check if short cut page was a shortcut itself, if so look up recursively: - if ($page['doktype'] == self::DOKTYPE_SHORTCUT) { + if ((int)$page['doktype'] === self::DOKTYPE_SHORTCUT) { if (!in_array($page['uid'], $pageLog) && $iteration > 0) { $pageLog[] = $page['uid']; $page = $this->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $page['uid'], $iteration - 1, $pageLog, $disableGroupCheck); } else { $pageLog[] = $page['uid']; - $message = 'Page shortcuts were looping in uids ' . implode(',', $pageLog) . '...!'; - $this->logger->error($message); - throw new \RuntimeException($message, 1294587212); + $this->logger->error('Page shortcuts were looping in uids {uids}', ['uids' => implode(', ', array_values($pageLog))]); + throw new \RuntimeException('Page shortcuts were looping in uids: ' . implode(', ', array_values($pageLog)), 1294587212); } } // Return resulting page: diff --git a/typo3/sysext/core/Classes/Error/AbstractExceptionHandler.php b/typo3/sysext/core/Classes/Error/AbstractExceptionHandler.php index 3af18f9afeb0..da0fe92936c9 100644 --- a/typo3/sysext/core/Classes/Error/AbstractExceptionHandler.php +++ b/typo3/sysext/core/Classes/Error/AbstractExceptionHandler.php @@ -67,35 +67,55 @@ abstract class AbstractExceptionHandler implements ExceptionHandlerInterface, Si * Writes exception to different logs * * @param \Throwable $exception The throwable object. - * @param string $context The context where the exception was thrown, WEB or CLI + * @param string $mode The context where the exception was thrown. + * Either self::CONTEXT_WEB or self::CONTEXT_CLI. */ - protected function writeLogEntries(\Throwable $exception, $context) + protected function writeLogEntries(\Throwable $exception, string $mode): void { // Do not write any logs for some messages to avoid filling up tables or files with illegal requests if (in_array($exception->getCode(), self::IGNORED_EXCEPTION_CODES, true)) { return; } + + // PSR-3 logging framework. + try { + if ($this->logger) { + // 'FE' if in FrontendApplication, else 'BE' (also in CLI without request object) + $applicationMode = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface + && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() + ? 'FE' + : 'BE'; + $requestUrl = $this->anonymizeToken(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')); + $this->logger->critical('Core: Exception handler ({mode}: {application_mode}): {exception_class}, code #{exception_code}, file {file}, line {line}: {message}', [ + 'mode' => $mode, + 'application_mode' => $applicationMode, + 'exception_class' => get_class($exception), + 'exception_code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'message' => $exception->getMessage(), + 'request_url' => $requestUrl, + 'exception' => $exception, + ]); + } + } catch (\Exception $exception) { + // A nested exception here was probably caused by a database failure, which means there's little + // else that can be done other than moving on and letting the system hard-fail. + } + + // Legacy logger. Remove this section eventually. $filePathAndName = $exception->getFile(); $exceptionCodeNumber = $exception->getCode() > 0 ? '#' . $exception->getCode() . ': ' : ''; - $logTitle = 'Core: Exception handler (' . $context . ')'; + $logTitle = 'Core: Exception handler (' . $mode . ')'; $logMessage = 'Uncaught TYPO3 Exception: ' . $exceptionCodeNumber . $exception->getMessage() . ' | ' . get_class($exception) . ' thrown in file ' . $filePathAndName . ' in line ' . $exception->getLine(); - if ($context === 'WEB') { + if ($mode === self::CONTEXT_WEB) { $logMessage .= '. Requested URL: ' . $this->anonymizeToken(GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL')); } // When database credentials are wrong, the exception is probably - // caused by this. Therefor we cannot do any database operation, + // caused by this. Therefore we cannot do any database operation, // otherwise this will lead into recurring exceptions. try { - if ($this->logger) { - // 'FE' if in FrontendApplication, else 'BE' (also in CLI without request object) - $applicationType = ($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface - && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() ? 'FE' : 'BE'; - $this->logger->critical($logTitle . ': ' . $logMessage, [ - 'TYPO3_MODE' => $applicationType, - 'exception' => $exception - ]); - } // Write error message to sys_log table $this->writeLog($logTitle . ': ' . $logMessage); } catch (\Exception $exception) { diff --git a/typo3/sysext/core/Classes/Log/LogManager.php b/typo3/sysext/core/Classes/Log/LogManager.php index 03c21312707e..ccccf4e29aaf 100644 --- a/typo3/sysext/core/Classes/Log/LogManager.php +++ b/typo3/sysext/core/Classes/Log/LogManager.php @@ -150,7 +150,11 @@ class LogManager implements SingletonInterface, LogManagerInterface $logWriter = GeneralUtility::makeInstance($logWriterClassName, $logWriterOptions); $logger->addWriter($severityLevel, $logWriter); } catch (InvalidArgumentException|InvalidLogWriterConfigurationException $e) { - $logger->warning('Instantiation of LogWriter "' . $logWriterClassName . '" failed for logger ' . $logger->getName() . ' (' . $e->getMessage() . ')'); + $logger->warning('Instantiation of LogWriter "{class_name}" failed for logger {name}', [ + 'class_name' => $logWriterClassName, + 'name' => $logger->getName(), + 'exception' => $e, + ]); } } } @@ -171,7 +175,11 @@ class LogManager implements SingletonInterface, LogManagerInterface $logProcessor = GeneralUtility::makeInstance($logProcessorClassName, $logProcessorOptions); $logger->addProcessor($severityLevel, $logProcessor); } catch (InvalidArgumentException|InvalidLogProcessorConfigurationException $e) { - $logger->warning('Instantiation of LogProcessor "' . $logProcessorClassName . '" failed for logger ' . $logger->getName() . ' (' . $e->getMessage() . ')'); + $logger->warning('Instantiation of LogProcessor "{class_name}" failed for logger {name}', [ + 'class_name' => $logProcessorClassName, + 'name' => $logger->getName(), + 'exception' => $e, + ]); } } } diff --git a/typo3/sysext/core/Classes/Log/Writer/AbstractWriter.php b/typo3/sysext/core/Classes/Log/Writer/AbstractWriter.php index c8c38c137286..39c864ab0f38 100644 --- a/typo3/sysext/core/Classes/Log/Writer/AbstractWriter.php +++ b/typo3/sysext/core/Classes/Log/Writer/AbstractWriter.php @@ -42,4 +42,60 @@ abstract class AbstractWriter implements WriterInterface } } } + + /** + * Interpolates context values into the message placeholders. + */ + protected function interpolate(string $message, array $context = []): string + { + // Build a replacement array with braces around the context keys. + $replace = []; + foreach ($context as $key => $val) { + if (!is_array($val) && !is_null($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = $this->formatContextValue($val); + } + } + + // Interpolate replacement values into the message and return. + return strtr($message, $replace); + } + + /** + * Escape or quote a value from the context appropriate for the output. + * + * Note: In some output cases, escaping should not be done here but later on output, + * such as if it's being written to a database for later display. + * + * @param string $value + * @return string + */ + protected function formatContextValue(string $value): string + { + return $value; + } + + /** + * Formats an exception into a string. + * + * The format here is nearly the same as just casting an exception to a string, + * but omits the full class namespace and stack trace, as those get very long. + * + * @param \Exception $ex + * @return string + */ + protected function formatException(\Exception $ex): string + { + $classname = get_class($ex); + if ($pos = strrpos($classname, '\\')) { + $classname = substr($classname, $pos + 1); + } + + return sprintf( + '- %s: %s, in file %s:%s', + $classname, + $ex->getMessage(), + $ex->getFile(), + $ex->getLine(), + ); + } } diff --git a/typo3/sysext/core/Classes/Log/Writer/DatabaseWriter.php b/typo3/sysext/core/Classes/Log/Writer/DatabaseWriter.php index b3e42f20eeea..70665341aafd 100644 --- a/typo3/sysext/core/Classes/Log/Writer/DatabaseWriter.php +++ b/typo3/sysext/core/Classes/Log/Writer/DatabaseWriter.php @@ -63,14 +63,13 @@ class DatabaseWriter extends AbstractWriter public function writeLog(LogRecord $record) { $data = ''; - $recordData = $record->getData(); - if (!empty($recordData)) { - // According to PSR3 the exception-key may hold an \Exception - // Since json_encode() does not encode an exception, we run the _toString() here - if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) { - $recordData['exception'] = (string)$recordData['exception']; + $context = $record->getData(); + if (!empty($context)) { + // Fold an exception into the message, and string-ify it into context so it can be jsonified. + if (isset($context['exception']) && $context['exception'] instanceof \Exception) { + $context['exception'] = (string)$context['exception']; } - $data = '- ' . json_encode($recordData); + $data = '- ' . json_encode($context); } $fieldValues = [ diff --git a/typo3/sysext/core/Classes/Log/Writer/FileWriter.php b/typo3/sysext/core/Classes/Log/Writer/FileWriter.php index 660022da4e36..00595937328c 100644 --- a/typo3/sysext/core/Classes/Log/Writer/FileWriter.php +++ b/typo3/sysext/core/Classes/Log/Writer/FileWriter.php @@ -28,22 +28,15 @@ class FileWriter extends AbstractWriter { /** * Log file path, relative to TYPO3's base project folder - * - * @var string */ - protected $logFile = ''; + protected string $logFile = ''; - /** - * @var string - */ - protected $logFileInfix = ''; + protected string $logFileInfix = ''; /** * Default log file path - * - * @var string */ - protected $defaultLogFileTemplate = '/log/typo3_%s.log'; + protected string $defaultLogFileTemplate = '/log/typo3_%s.log'; /** * Log file handle storage @@ -52,21 +45,19 @@ class FileWriter extends AbstractWriter * we share the file handles in a static class variable * * @static - * @var array */ - protected static $logFileHandles = []; + protected static array $logFileHandles = []; /** * Keep track of used file handles by different fileWriter instances + * * As the logger gets instantiated by class name but the resources * are shared via the static $logFileHandles we need to track usage * of file handles to avoid closing handles that are still needed * by different instances. Only if the count is zero may the file * handle be closed. - * - * @var array */ - protected static $logFileHandlesCount = []; + protected static array $logFileHandlesCount = []; /** * Constructor, opens the log file handle @@ -105,7 +96,7 @@ class FileWriter extends AbstractWriter * @return WriterInterface * @throws InvalidLogWriterConfigurationException */ - public function setLogFile($relativeLogFile) + public function setLogFile(string $relativeLogFile) { $logFile = $relativeLogFile; // Skip handling if logFile is a stream resource. This is used by unit tests with vfs:// directories @@ -126,10 +117,8 @@ class FileWriter extends AbstractWriter /** * Gets the path to the log file. - * - * @return string Path to the log file. */ - public function getLogFile() + public function getLogFile(): string { return $this->logFile; } @@ -143,26 +132,25 @@ class FileWriter extends AbstractWriter */ public function writeLog(LogRecord $record) { - $timestamp = date('r', (int)$record->getCreated()); - $levelName = strtoupper($record->getLevel()); $data = ''; - $recordData = $record->getData(); - if (!empty($recordData)) { - // According to PSR3 the exception-key may hold an \Exception - // Since json_encode() does not encode an exception, we run the _toString() here - if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) { - $recordData['exception'] = (string)$recordData['exception']; + $context = $record->getData(); + $message = $record->getMessage(); + if (!empty($context)) { + // Fold an exception into the message, and string-ify it into context so it can be jsonified. + if (isset($context['exception']) && $context['exception'] instanceof \Exception) { + $message .= $this->formatException($context['exception']); + $context['exception'] = (string)$context['exception']; } - $data = '- ' . json_encode($recordData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $data = '- ' . json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } $message = sprintf( '%s [%s] request="%s" component="%s": %s %s', - $timestamp, - $levelName, + date('r', (int)$record->getCreated()), + strtoupper($record->getLevel()), $record->getRequestId(), $record->getComponent(), - $record->getMessage(), + $this->interpolate($record->getMessage(), $context), $data ); diff --git a/typo3/sysext/core/Classes/Log/Writer/PhpErrorLogWriter.php b/typo3/sysext/core/Classes/Log/Writer/PhpErrorLogWriter.php index 0af10ed61bac..92404466dfb8 100644 --- a/typo3/sysext/core/Classes/Log/Writer/PhpErrorLogWriter.php +++ b/typo3/sysext/core/Classes/Log/Writer/PhpErrorLogWriter.php @@ -31,23 +31,24 @@ class PhpErrorLogWriter extends AbstractWriter */ public function writeLog(LogRecord $record) { - $levelName = strtoupper($record->getLevel()); $data = ''; - $recordData = $record->getData(); - if (!empty($recordData)) { - // According to PSR3 the exception-key may hold an \Exception - // Since json_encode() does not encode an exception, we run the _toString() here - if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) { - $recordData['exception'] = (string)$recordData['exception']; + $context = $record->getData(); + $message = $record->getMessage(); + if (!empty($context)) { + // Fold an exception into the message, and string-ify it into context so it can be jsonified. + if (isset($context['exception']) && $context['exception'] instanceof \Exception) { + $message .= $this->formatException($context['exception']); + $context['exception'] = (string)$context['exception']; } - $data = '- ' . json_encode($recordData); + $data = '- ' . json_encode($context); } + $message = sprintf( 'TYPO3 [%s] request="%s" component="%s": %s %s', - $levelName, + strtoupper($record->getLevel()), $record->getRequestId(), $record->getComponent(), - $record->getMessage(), + $this->interpolate($record->getMessage(), $context), $data ); if (false === error_log($message)) { diff --git a/typo3/sysext/core/Classes/Log/Writer/SyslogWriter.php b/typo3/sysext/core/Classes/Log/Writer/SyslogWriter.php index 5d71ea6d5e70..16fab5a4e04f 100644 --- a/typo3/sysext/core/Classes/Log/Writer/SyslogWriter.php +++ b/typo3/sysext/core/Classes/Log/Writer/SyslogWriter.php @@ -90,7 +90,7 @@ class SyslogWriter extends AbstractWriter * * @param string $facility Facility to use when logging. */ - public function setFacility($facility) + public function setFacility($facility): void { if (array_key_exists(strtolower($facility), $this->facilities)) { $this->facility = $this->facilities[strtolower($facility)]; @@ -103,26 +103,27 @@ class SyslogWriter extends AbstractWriter * @param LogRecord $record * @return string */ - public function getMessageForSyslog(LogRecord $record) + public function getMessageForSyslog(LogRecord $record): string { $data = ''; - $recordData = $record->getData(); - if (!empty($recordData)) { - // According to PSR3 the exception-key may hold an \Exception - // Since json_encode() does not encode an exception, we run the _toString() here - if (isset($recordData['exception']) && $recordData['exception'] instanceof \Exception) { - $recordData['exception'] = (string)$recordData['exception']; + $context = $record->getData(); + $message = $record->getMessage(); + if (!empty($context)) { + // Fold an exception into the message, and string-ify it into context so it can be jsonified. + if (isset($context['exception']) && $context['exception'] instanceof \Exception) { + $message .= $this->formatException($context['exception']); + $context['exception'] = (string)$context['exception']; } - $data = '- ' . json_encode($recordData); + $data = '- ' . json_encode($context); } - $message = sprintf( + + return sprintf( '[request="%s" component="%s"] %s %s', $record->getRequestId(), $record->getComponent(), - $record->getMessage(), + $this->interpolate($message, $context), $data ); - return $message; } /** diff --git a/typo3/sysext/core/Classes/Mail/MemorySpool.php b/typo3/sysext/core/Classes/Mail/MemorySpool.php index 2c8dda800c60..cc0016736ef7 100644 --- a/typo3/sysext/core/Classes/Mail/MemorySpool.php +++ b/typo3/sysext/core/Classes/Mail/MemorySpool.php @@ -87,7 +87,7 @@ class MemorySpool extends AbstractTransport implements SingletonInterface, Delay $this->flushQueue($mailer->getRealTransport()); } catch (TransportExceptionInterface $exception) { if ($this->logger instanceof LoggerInterface) { - $this->logger->error('An Exception occurred while flushing email queue: ' . $exception->getMessage()); + $this->logger->error('An Exception occurred while flushing email queue: {message}', ['exception' => $exception, 'message' => $exception->getMessage()]); } } } diff --git a/typo3/sysext/core/Classes/Mail/TransportFactory.php b/typo3/sysext/core/Classes/Mail/TransportFactory.php index b63554cc8169..ce601f781c05 100644 --- a/typo3/sysext/core/Classes/Mail/TransportFactory.php +++ b/typo3/sysext/core/Classes/Mail/TransportFactory.php @@ -127,7 +127,7 @@ class TransportFactory implements SingletonInterface, LoggerAwareInterface $sendmailCommand = $mailSettings['transport_sendmail_command'] ?? @ini_get('sendmail_path'); if (empty($sendmailCommand)) { $sendmailCommand = '/usr/sbin/sendmail -bs'; - $this->logger->warning('Mailer transport "sendmail" was chosen without a specific command, using "' . $sendmailCommand . '"'); + $this->logger->warning('Mailer transport "sendmail" was chosen without a specific command, using "{command}"', ['command' => $sendmailCommand]); } // Create transport $transport = new SendmailTransport( diff --git a/typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php b/typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php index 16249b75550e..e75bf035dd21 100644 --- a/typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php +++ b/typo3/sysext/core/Classes/PageTitle/PageTitleProviderManager.php @@ -49,10 +49,9 @@ class PageTitleProviderManager implements SingletonInterface, LoggerAwareInterfa $orderedTitleProviders = GeneralUtility::makeInstance(DependencyOrderingService::class) ->orderByDependencies($titleProviders); - $this->logger->debug( - 'Page title providers ordered', - ['orderedTitleProviders' => $orderedTitleProviders] - ); + $this->logger->debug('Page title providers ordered', [ + 'orderedTitleProviders' => $orderedTitleProviders, + ]); foreach ($orderedTitleProviders as $provider => $configuration) { if (class_exists($configuration['provider']) && is_subclass_of($configuration['provider'], PageTitleProviderInterface::class)) { @@ -61,17 +60,18 @@ class PageTitleProviderManager implements SingletonInterface, LoggerAwareInterfa if (($pageTitle = $titleProviderObject->getTitle()) || ($pageTitle = $this->pageTitleCache[$configuration['provider']] ?? '') !== '' ) { - $this->logger->debug( - 'Page title provider ' . $configuration['provider'] . ' used', - ['title' => $pageTitle, 'providerUsed' => $configuration['provider']] - ); + $this->logger->debug('Page title provider {provider} used on page {title}', [ + 'title' => $pageTitle, + 'provider' => $configuration['provider'], + ]); $this->pageTitleCache[$configuration['provider']] = $pageTitle; break; } - $this->logger->debug( - 'Page title provider ' . $configuration['provider'] . ' skipped', - ['name' => $provider, 'skippedProvider' => $configuration['provider']] - ); + $this->logger->debug('Page title provider {provider} skipped on page {title}', [ + 'title' => $pageTitle, + 'provider' => $configuration['provider'], + 'providerUsed' => $configuration['provider'], + ]); } } diff --git a/typo3/sysext/core/Classes/Resource/Index/Indexer.php b/typo3/sysext/core/Classes/Resource/Index/Indexer.php index c72d91d7a3ac..a49c2604eb39 100644 --- a/typo3/sysext/core/Classes/Resource/Index/Indexer.php +++ b/typo3/sysext/core/Classes/Resource/Index/Indexer.php @@ -270,9 +270,12 @@ class Indexer implements LoggerAwareInterface $this->updateIndexEntry($fileObject); } } catch (InvalidHashException $e) { - $this->logger->error('Unable to create hash for file ' . $identifier); + $this->logger->error('Unable to create hash for file: {identifier}', ['identifier' => $identifier]); } catch (\Exception $e) { - $this->logger->error('Unable to index / update file with identifier ' . $identifier . ' (Error: ' . $e->getMessage() . ')'); + $this->logger->error('Unable to index / update file with identifier {identifier}', [ + 'identifier' => $identifier, + 'exception' => $e, + ]); } } } diff --git a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php index 6f24c60657c3..3f9acd4669d6 100644 --- a/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php +++ b/typo3/sysext/core/Classes/Resource/ProcessedFileRepository.php @@ -322,12 +322,11 @@ class ProcessedFileRepository extends AbstractRepository implements LoggerAwareI $file->getStorage()->setEvaluatePermissions(false); $file->delete(true); } catch (\Exception $e) { - $this->logger->error( - 'Failed to delete file "' . $row['identifier'] . '" in storage uid ' . $row['storage'] . '.', - [ - 'exception' => $e - ] - ); + $this->logger->error('Failed to delete file {identifier} in storage uid {storage}.', [ + 'identifier' => $row['identifier'], + 'storage' => $row['storage'], + 'exception' => $e, + ]); ++$errorCount; } } diff --git a/typo3/sysext/core/Classes/Resource/StorageRepository.php b/typo3/sysext/core/Classes/Resource/StorageRepository.php index 2737004cba5b..1bf9ebe076c0 100644 --- a/typo3/sysext/core/Classes/Resource/StorageRepository.php +++ b/typo3/sysext/core/Classes/Resource/StorageRepository.php @@ -211,10 +211,7 @@ class StorageRepository implements LoggerAwareInterface if ($this->driverRegistry->driverExists($storageRow['driver'])) { $storageObjects[] = $this->getStorageObject($storageRow['uid'], $storageRow); } else { - $this->logger->warning( - sprintf('Could not instantiate storage "%s" because of missing driver.', $storageRow['name']), - $storageRow - ); + $this->logger->warning('Could not instantiate storage "{name}" because of missing driver.', ['name' => $storageRow['name']]); } } return $storageObjects; @@ -235,10 +232,7 @@ class StorageRepository implements LoggerAwareInterface if ($this->driverRegistry->driverExists($storageRow['driver'])) { $storageObjects[] = $this->getStorageObject($storageRow['uid'], $storageRow); } else { - $this->logger->warning( - sprintf('Could not instantiate storage "%s" because of missing driver.', $storageRow['name']), - $storageRow - ); + $this->logger->warning('Could not instantiate storage "{name}" because of missing driver.', ['name' => $storageRow['name']]); } } return $storageObjects; diff --git a/typo3/sysext/core/Classes/Session/UserSessionManager.php b/typo3/sysext/core/Classes/Session/UserSessionManager.php index 482aa7b8f748..506e00918ed7 100644 --- a/typo3/sysext/core/Classes/Session/UserSessionManager.php +++ b/typo3/sysext/core/Classes/Session/UserSessionManager.php @@ -122,7 +122,7 @@ class UserSessionManager implements LoggerAwareInterface */ public function createSessionFromStorage(string $sessionId): UserSession { - $this->logger->debug('Fetch session with identifier ' . $sessionId); + $this->logger->debug('Fetch session with identifier {session}', ['session' => $sessionId]); $sessionRecord = $this->sessionBackend->get($sessionId); return UserSession::createFromRecord($sessionId, $sessionRecord); } @@ -189,7 +189,7 @@ class UserSessionManager implements LoggerAwareInterface public function elevateToFixatedUserSession(UserSession $session, int $userId, bool $isPermanent = false): UserSession { $sessionId = $session->getIdentifier(); - $this->logger->debug('Create session ses_id = ' . $sessionId); + $this->logger->debug('Create session ses_id = {session}', ['session' => $sessionId]); // Delete any session entry first $this->sessionBackend->remove($sessionId); // Re-create session entry diff --git a/typo3/sysext/core/Classes/Type/File/ImageInfo.php b/typo3/sysext/core/Classes/Type/File/ImageInfo.php index 62c796c381bc..ef35044a8bae 100644 --- a/typo3/sysext/core/Classes/Type/File/ImageInfo.php +++ b/typo3/sysext/core/Classes/Type/File/ImageInfo.php @@ -102,7 +102,7 @@ class ImageInfo extends FileInfo implements LoggerAwareInterface // In case the image size could not be retrieved, log the incident as a warning. if (empty($this->imageSizes)) { - $this->logger->warning('I could not retrieve the image size for file ' . $this->getPathname()); + $this->logger->warning('I could not retrieve the image size for file {file}', ['file' => $this->getPathname()]); $this->imageSizes = [0, 0]; } } diff --git a/typo3/sysext/core/Classes/TypoScript/Parser/TypoScriptParser.php b/typo3/sysext/core/Classes/TypoScript/Parser/TypoScriptParser.php index 5ffb61db1e97..c025ed7a1718 100644 --- a/typo3/sysext/core/Classes/TypoScript/Parser/TypoScriptParser.php +++ b/typo3/sysext/core/Classes/TypoScript/Parser/TypoScriptParser.php @@ -491,7 +491,9 @@ class TypoScriptParser $fakeThis = null; $newValue = GeneralUtility::callUserFunction($hookMethod, $params, $fakeThis); } else { - self::getLogger()->warning('Missing function definition for ' . $modifierName . ' on TypoScript'); + self::getLogger()->warning('Missing function definition for {modifier_name} on TypoScript', [ + 'modifier_name' => $modifierName, + ]); } } return $newValue; diff --git a/typo3/sysext/core/Classes/Utility/GeneralUtility.php b/typo3/sysext/core/Classes/Utility/GeneralUtility.php index 089e0c866038..4e396e017898 100644 --- a/typo3/sysext/core/Classes/Utility/GeneralUtility.php +++ b/typo3/sysext/core/Classes/Utility/GeneralUtility.php @@ -1662,10 +1662,9 @@ class GeneralUtility $parameters = ['script' => $script]; $script = static::callUserFunction($hookMethod, $parameters, $fakeThis); } catch (\Exception $e) { - $errorMessage = 'Error minifying java script: ' . $e->getMessage(); - $error .= $errorMessage; - static::getLogger()->warning($errorMessage, [ - 'JavaScript' => $script, + $error .= 'Error minifying Javascript: ' . $e->getMessage(); + static::getLogger()->warning('Error minifying Javascript: {file}, hook: {hook}', [ + 'file' => $script, 'hook' => $hookMethod, 'exception' => $e, ]); @@ -2952,7 +2951,7 @@ class GeneralUtility } } if (!empty($url) && empty($sanitizedUrl)) { - static::getLogger()->notice('The URL "' . $url . '" is not considered to be local and was denied.'); + static::getLogger()->notice('The URL "{url}" is not considered to be local and was denied.', ['url' => $url]); } return $sanitizedUrl; } @@ -3541,8 +3540,11 @@ class GeneralUtility $info = array_merge($info, $requestInfo); $obj = self::makeInstance($info['className']); if (is_object($obj)) { - if (!@is_callable([$obj, 'init'])) { - self::getLogger()->error('Requested service ' . $info['className'] . ' has no init() method.', ['service' => $info]); + if (!is_callable([$obj, 'init'])) { + self::getLogger()->error('Requested service {class} has no init() method.', [ + 'class' => $info['className'], + 'service' => $info, + ]); throw new \RuntimeException('Broken service: ' . $info['className'], 1568119209); } $obj->info = $info; diff --git a/typo3/sysext/core/Documentation/Changelog/master/Important-94315-UseProperPSR-3LoggingMessagesAndContext.rst b/typo3/sysext/core/Documentation/Changelog/master/Important-94315-UseProperPSR-3LoggingMessagesAndContext.rst new file mode 100644 index 000000000000..43c11e6ae5e5 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Important-94315-UseProperPSR-3LoggingMessagesAndContext.rst @@ -0,0 +1,37 @@ +.. include:: ../../Includes.txt + +================================================================= +Important: #94315 - Use proper PSR-3 logging messages and context +================================================================= + +See :issue:`94315` + +Description +=========== + +The v11 core is looking into proper PSR-3 Logging implementation again. When +analyzing the current situation, we realized many core logging calls were +using messages that violated the PSR-3 +`placeholder specification <https://www.php-fig.org/psr/psr-3/>`__. + +The v11 core fixed all places, but it's likely extensions have this issue, +too. Extension developers should have a look at their logger calls and adapt +them if necessary. + +Typical call before: + +.. code-block:: php + + $this->logger->alert('Password reset requested for email "' . $emailAddress . '" . but was requested too many times.'); + +Correct call: + +.. code-block:: php + + $this->logger->alert('Password reset requested for email {email} but was requested too many times.', ['email' => $emailAddress]); + +First argument is 'message', second (optional) argument is 'context'. A message can +use :php:`{placehlders}`. All core provided log writers will substitute placeholders +in the message with data from the context array, if a context array key with same name exists. + +.. index:: PHP-API, ext:core diff --git a/typo3/sysext/core/Tests/Unit/Error/DebugExceptionHandlerTest.php b/typo3/sysext/core/Tests/Unit/Error/DebugExceptionHandlerTest.php index eb3c2aeeab78..f07c901f26c3 100644 --- a/typo3/sysext/core/Tests/Unit/Error/DebugExceptionHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/Error/DebugExceptionHandlerTest.php @@ -15,8 +15,8 @@ namespace TYPO3\CMS\Core\Tests\Unit\Error; -use Prophecy\Argument; use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; use TYPO3\CMS\Core\Error\DebugExceptionHandler; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\TestingFramework\Core\Unit\UnitTestCase; @@ -98,9 +98,21 @@ class DebugExceptionHandlerTest extends UnitTestCase public function logEntriesContainAnonymousTokens(string $originalUrl, string $expectedUrl) { $subject = new DebugExceptionHandler(); - $logger = $this->prophesize(LoggerInterface::class); - $logger->critical(Argument::containingString($expectedUrl), Argument::cetera())->shouldBeCalled(); - $subject->setLogger($logger->reveal()); + + $logger = new class() implements LoggerInterface { + use LoggerTrait; + public array $records = []; + public function log($level, $message, array $context = []): void + { + $this->records[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context + ]; + } + }; + + $subject->setLogger($logger); GeneralUtility::setIndpEnv('TYPO3_REQUEST_URL', $originalUrl); @@ -109,5 +121,8 @@ class DebugExceptionHandlerTest extends UnitTestCase $subject->echoExceptionWeb($exception); // output is caught, so it does not pollute the test run ob_end_clean(); + + self::assertEquals('critical', $logger->records[0]['level']); + self::assertEquals($expectedUrl, $logger->records[0]['context']['request_url']); } } diff --git a/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php b/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php index e5242562c015..db383f023d4a 100644 --- a/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/Error/ProductionExceptionHandlerTest.php @@ -15,8 +15,8 @@ namespace TYPO3\CMS\Core\Tests\Unit\Error; -use Prophecy\Argument; use Psr\Log\LoggerInterface; +use Psr\Log\LoggerTrait; use TYPO3\CMS\Core\Error\ProductionExceptionHandler; use TYPO3\CMS\Core\Information\Typo3Information; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -131,9 +131,19 @@ class ProductionExceptionHandlerTest extends UnitTestCase $typo3InformationProphecy->getCopyrightYear()->willReturn('1999-20XX'); GeneralUtility::addInstance(Typo3Information::class, $typo3InformationProphecy->reveal()); $subject = new ProductionExceptionHandler(); - $logger = $this->prophesize(LoggerInterface::class); - $logger->critical(Argument::containingString($expectedUrl), Argument::cetera())->shouldBeCalled(); - $subject->setLogger($logger->reveal()); + $logger = new class() implements LoggerInterface { + use LoggerTrait; + public array $records = []; + public function log($level, $message, array $context = []): void + { + $this->records[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context + ]; + } + }; + $subject->setLogger($logger); GeneralUtility::setIndpEnv('TYPO3_REQUEST_URL', $originalUrl); $GLOBALS['BE_USER'] = null; @@ -143,5 +153,8 @@ class ProductionExceptionHandlerTest extends UnitTestCase $subject->echoExceptionWeb($exception); // output is caught, so it does not pollute the test run ob_end_clean(); + + self::assertEquals('critical', $logger->records[0]['level']); + self::assertEquals($expectedUrl, $logger->records[0]['context']['request_url']); } } diff --git a/typo3/sysext/extbase/Classes/Object/Container/Container.php b/typo3/sysext/extbase/Classes/Object/Container/Container.php index b835f9c00497..876aeacaf5e6 100644 --- a/typo3/sysext/extbase/Classes/Object/Container/Container.php +++ b/typo3/sysext/extbase/Classes/Object/Container/Container.php @@ -215,7 +215,10 @@ class Container implements SingletonInterface, LoggerAwareInterface } $instanceToInject = $this->getInstanceInternal($classNameToInject); if ($classSchema->isSingleton() && !$instanceToInject instanceof SingletonInterface) { - $this->logger->notice('The singleton "' . $classSchema->getClassName() . '" needs a prototype in "' . $injectMethodName . '". This is often a bad code smell; often you rather want to inject a singleton.'); + $this->logger->notice('The singleton "{class}" needs a prototype in "{method}". This is often a bad code smell; often you rather want to inject a singleton.', [ + 'class' => $classSchema->getClassName(), + 'method' => $injectMethodName, + ]); } if (is_callable([$instance, $injectMethodName])) { $instance->{$injectMethodName}($instanceToInject); @@ -228,7 +231,10 @@ class Container implements SingletonInterface, LoggerAwareInterface $instanceToInject = $this->getInstanceInternal($classNameToInject); if ($classSchema->isSingleton() && !$instanceToInject instanceof SingletonInterface) { - $this->logger->notice('The singleton "' . $classSchema->getClassName() . '" needs a prototype in "' . $injectPropertyName . '". This is often a bad code smell; often you rather want to inject a singleton.'); + $this->logger->notice('The singleton "{class}" needs a prototype in "{method}". This is often a bad code smell; often you rather want to inject a singleton.', [ + 'class' => $classSchema->getClassName(), + 'method' => $injectPropertyName, + ]); } $instance->{$injectPropertyName} = $instanceToInject; @@ -291,7 +297,7 @@ class Container implements SingletonInterface, LoggerAwareInterface if ($methodParameter->getDependency() !== null && !$methodParameter->hasDefaultValue()) { $argument = $this->getInstanceInternal($methodParameter->getDependency()); if ($classSchema->isSingleton() && !$argument instanceof SingletonInterface) { - $this->logger->notice('The singleton "' . $classSchema->getClassName() . '" needs a prototype in the constructor. This is often a bad code smell; often you rather want to inject a singleton.'); + $this->logger->notice('The singleton "{class_name}" needs a prototype in the constructor. This is often a bad code smell; often you rather want to inject a singleton.', ['class_name' => $classSchema->getClassName()]); // todo: the whole injection is flawed anyway, why would we care about injecting prototypes? so, wayne? // todo: btw: if this is important, we can already detect this case in the class schema. } diff --git a/typo3/sysext/extbase/Classes/SignalSlot/Dispatcher.php b/typo3/sysext/extbase/Classes/SignalSlot/Dispatcher.php index 724d2620aab1..a6130ee61013 100644 --- a/typo3/sysext/extbase/Classes/SignalSlot/Dispatcher.php +++ b/typo3/sysext/extbase/Classes/SignalSlot/Dispatcher.php @@ -111,14 +111,11 @@ class Dispatcher implements SingletonInterface */ public function dispatch(string $signalClassName, string $signalName, array $signalArguments = []) { - $this->logger->debug( - 'Triggered signal ' . $signalClassName . ' ' . $signalName, - [ - 'signalClassName' => $signalClassName, - 'signalName' => $signalName, - 'signalArguments' => $signalArguments, - ] - ); + $this->logger->debug('Triggered signal {signal_class} {signal_name} ', [ + 'signal_class' => $signalClassName, + 'signal_name' => $signalName, + 'signalArguments' => $signalArguments, + ]); if (!isset($this->slots[$signalClassName][$signalName])) { return $signalArguments; } diff --git a/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php b/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php index 68280909a1ef..c6e4f4eb0411 100644 --- a/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php +++ b/typo3/sysext/extbase/Tests/Unit/Object/Container/ContainerTest.php @@ -18,7 +18,7 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Object\Container; use Psr\Container\ContainerInterface; use Psr\Container\NotFoundExceptionInterface; use Psr\Log\LoggerInterface; -use TYPO3\CMS\Core\Log\Logger; +use Psr\Log\LoggerTrait; use TYPO3\CMS\Extbase\Object\Container\Container; use TYPO3\CMS\Extbase\Object\Exception; use TYPO3\CMS\Extbase\Object\Exception\CannotBuildObjectException; @@ -48,20 +48,26 @@ class ContainerTest extends UnitTestCase /** * @var Container */ - protected $subject; + protected Container $subject; - /** - * @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - protected $logger; + protected LoggerInterface $logger; protected function setUp(): void { parent::setUp(); - $this->logger = $this->getMockBuilder(Logger::class) - ->setMethods(['notice']) - ->disableOriginalConstructor() - ->getMock(); + $this->logger = new class() implements LoggerInterface { + use LoggerTrait; + public array $records = []; + public function log($level, $message, array $context = []): void + { + $this->records[] = [ + 'level' => $level, + 'message' => $message, + 'context' => $context + ]; + } + }; + $reflectionService = new ReflectionService(); $notFoundException = new class() extends \Exception implements NotFoundExceptionInterface { @@ -293,9 +299,11 @@ class ContainerTest extends UnitTestCase */ public function singletonWhichRequiresPrototypeViaSetterInjectionWorksAndAddsDebugMessage() { - $this->logger->expects(self::once())->method('notice')->with('The singleton "t3lib_object_singletonNeedsPrototype" needs a prototype in "injectDependency". This is often a bad code smell; often you rather want to inject a singleton.'); $object = $this->subject->getInstance('t3lib_object_singletonNeedsPrototype'); self::assertInstanceOf('t3lib_object_prototype', $object->dependency); + self::assertEquals('notice', $this->logger->records[0]['level']); + self::assertEquals('t3lib_object_singletonNeedsPrototype', $this->logger->records[0]['context']['class']); + self::assertEquals('injectDependency', $this->logger->records[0]['context']['method']); } /** @@ -303,9 +311,9 @@ class ContainerTest extends UnitTestCase */ public function singletonWhichRequiresSingletonViaSetterInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_singletonNeedsSingleton'); self::assertInstanceOf('t3lib_object_singleton', $object->dependency); + self::assertEmpty($this->logger->records); } /** @@ -313,9 +321,9 @@ class ContainerTest extends UnitTestCase */ public function prototypeWhichRequiresPrototypeViaSetterInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_prototypeNeedsPrototype'); self::assertInstanceOf('t3lib_object_prototype', $object->dependency); + self::assertEmpty($this->logger->records); } /** @@ -323,9 +331,9 @@ class ContainerTest extends UnitTestCase */ public function prototypeWhichRequiresSingletonViaSetterInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_prototypeNeedsSingleton'); self::assertInstanceOf('t3lib_object_singleton', $object->dependency); + self::assertEmpty($this->logger->records); } /** @@ -333,9 +341,10 @@ class ContainerTest extends UnitTestCase */ public function singletonWhichRequiresPrototypeViaConstructorInjectionWorksAndAddsDebugMessage() { - $this->logger->expects(self::once())->method('notice')->with('The singleton "t3lib_object_singletonNeedsPrototypeInConstructor" needs a prototype in the constructor. This is often a bad code smell; often you rather want to inject a singleton.'); $object = $this->subject->getInstance('t3lib_object_singletonNeedsPrototypeInConstructor'); self::assertInstanceOf('t3lib_object_prototype', $object->dependency); + self::assertEquals('notice', $this->logger->records[0]['level']); + self::assertEquals('t3lib_object_singletonNeedsPrototypeInConstructor', $this->logger->records[0]['context']['class_name']); } /** @@ -343,9 +352,9 @@ class ContainerTest extends UnitTestCase */ public function singletonWhichRequiresSingletonViaConstructorInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_singletonNeedsSingletonInConstructor'); self::assertInstanceOf('t3lib_object_singleton', $object->dependency); + self::assertEmpty($this->logger->records); } /** @@ -353,9 +362,9 @@ class ContainerTest extends UnitTestCase */ public function prototypeWhichRequiresPrototypeViaConstructorInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_prototypeNeedsPrototypeInConstructor'); self::assertInstanceOf('t3lib_object_prototype', $object->dependency); + self::assertEmpty($this->logger->records); } /** @@ -363,9 +372,9 @@ class ContainerTest extends UnitTestCase */ public function prototypeWhichRequiresSingletonViaConstructorInjectionWorks() { - $this->logger->expects(self::never())->method('notice'); $object = $this->subject->getInstance('t3lib_object_prototypeNeedsSingletonInConstructor'); self::assertInstanceOf('t3lib_object_singleton', $object->dependency); + self::assertEmpty($this->logger->records); } /************************************************ diff --git a/typo3/sysext/extensionmanager/Classes/Utility/InstallUtility.php b/typo3/sysext/extensionmanager/Classes/Utility/InstallUtility.php index 28bf87aa3543..25fee3e4072e 100644 --- a/typo3/sysext/extensionmanager/Classes/Utility/InstallUtility.php +++ b/typo3/sysext/extensionmanager/Classes/Utility/InstallUtility.php @@ -556,13 +556,10 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface foreach ($finder as $siteConfigDirectory) { $siteIdentifier = $siteConfigDirectory->getBasename(); if (isset($existingSites[$siteIdentifier])) { - $this->logger->warning( - sprintf( - 'Skipped importing site configuration from %s due to existing site identifier %s', - $extensionKey, - $siteIdentifier - ) - ); + $this->logger->warning('Skipped importing site configuration from {key} due to existing site identifier {site}', [ + 'key' => $extensionKey, + 'site' => $siteIdentifier, + ]); continue; } $targetDir = $destinationFolder . '/' . $siteIdentifier; @@ -588,12 +585,9 @@ class InstallUtility implements SingletonInterface, LoggerAwareInterface $newSiteIdentifierList[] = $siteIdentifier; $importedPageId = $importedPages[$exportedPageId] ?? null; if ($importedPageId === null) { - $this->logger->warning( - sprintf( - 'Imported site configuration with identifier %s could not be mapped to imported page id', - $siteIdentifier - ) - ); + $this->logger->warning('Imported site configuration with identifier {site} could not be mapped to imported page id', [ + 'site' => $siteIdentifier, + ]); continue; } $configuration = $siteConfiguration->load($siteIdentifier); diff --git a/typo3/sysext/felogin/Classes/Validation/RedirectUrlValidator.php b/typo3/sysext/felogin/Classes/Validation/RedirectUrlValidator.php index c7b910879cd4..f35a45b412a0 100644 --- a/typo3/sysext/felogin/Classes/Validation/RedirectUrlValidator.php +++ b/typo3/sysext/felogin/Classes/Validation/RedirectUrlValidator.php @@ -61,7 +61,7 @@ class RedirectUrlValidator implements LoggerAwareInterface return true; } // URL is not allowed - $this->logger->warning('Url "' . $value . '" was not accepted.'); + $this->logger->warning('Url "{url}" was not accepted.', ['url' => $value]); return false; } diff --git a/typo3/sysext/filelist/Classes/Controller/FileListController.php b/typo3/sysext/filelist/Classes/Controller/FileListController.php index 6be72cba476f..1285d55a1780 100644 --- a/typo3/sysext/filelist/Classes/Controller/FileListController.php +++ b/typo3/sysext/filelist/Classes/Controller/FileListController.php @@ -503,11 +503,10 @@ class FileListController extends ActionController implements LoggerAwareInterfac self::UPLOAD_ACTION_RENAME, self::UPLOAD_ACTION_SKIP ], true)) { - $this->logger->warning(sprintf( - 'TSConfig: options.file_list.uploader.defaultAction contains an invalid value ("%s"), fallback to default value: "%s"', - $defaultAction, - self::UPLOAD_ACTION_SKIP - )); + $this->logger->warning('TSConfig: options.file_list.uploader.defaultAction contains an invalid value ("{value}"), fallback to default value: "{default}"', [ + 'value' => $defaultAction, + 'default' => self::UPLOAD_ACTION_SKIP + ]); $defaultAction = self::UPLOAD_ACTION_SKIP; } return $defaultAction; diff --git a/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php b/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php index 0d5134334e79..359adb211c62 100644 --- a/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php +++ b/typo3/sysext/frontend/Classes/Authentication/FrontendUserAuthentication.php @@ -288,7 +288,7 @@ class FrontendUserAuthentication extends AbstractUserAuthentication if (empty($groupDataArr)) { $this->logger->debug('No usergroups found'); } else { - $this->logger->debug(count($groupDataArr) . ' usergroup records found'); + $this->logger->debug('{count} usergroup records found', ['count' => count($groupDataArr)]); } foreach ($groupDataArr as $groupData) { $groupId = (int)$groupData['uid']; @@ -345,7 +345,7 @@ class FrontendUserAuthentication extends AbstractUserAuthentication $this->updateOnlineTimestamp(); } - $this->logger->debug('Valid frontend usergroups: ' . implode(',', $userGroups)); + $this->logger->debug('Valid frontend usergroups: {groups}', ['groups' => $userGroups]); return GeneralUtility::makeInstance(UserAspect::class, $this, $userGroups); } /** diff --git a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php index 15e0a94c6c8d..393cbaaa2be6 100644 --- a/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php +++ b/typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php @@ -2972,7 +2972,7 @@ class ContentObjectRenderer implements LoggerAwareInterface )'; $splittedContent = preg_split('%' . $tagsRegEx . '%xs', $content, -1, PREG_SPLIT_DELIM_CAPTURE); if ($splittedContent === false) { - $this->logger->debug('Unable to split "' . $content . '" into tags.'); + $this->logger->debug('Unable to split "{content}" into tags.', ['content' => $content]); $splittedContent = []; } // Reverse array if we are cropping from right. @@ -3463,7 +3463,7 @@ class ContentObjectRenderer implements LoggerAwareInterface // If the previous tag was set to strip NewLines in the beginning of the next data-chunk. $data = preg_replace('/^[ ]*' . CR . '?' . LF . '/', '', $data); if ($data === null) { - $this->logger->debug('Stripping new lines failed for "' . $data . '"'); + $this->logger->debug('Stripping new lines failed for "{data}"', ['data' => $data]); $data = ''; } } @@ -3970,7 +3970,10 @@ class ContentObjectRenderer implements LoggerAwareInterface $fileObject = $this->getResourceFactory()->retrieveFileOrFolderObject($file); } } catch (Exception $exception) { - $this->logger->warning('The image "' . $file . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]); + $this->logger->warning('The image "{file}" could not be found and won\'t be included in frontend output', [ + 'file' => $file, + 'exception' => $exception, + ]); return null; } } @@ -4375,7 +4378,7 @@ class ContentObjectRenderer implements LoggerAwareInterface try { $retVal = ArrayUtility::getValueByPath($site->getConfiguration(), $key, '.'); } catch (MissingArrayPathException $exception) { - $this->logger->warning(sprintf('getData() with "%s" failed', $key), ['exception' => $exception]); + $this->logger->warning('getData() with "{key}" failed', ['key' => $key, 'exception' => $exception]); } } break; @@ -4424,7 +4427,7 @@ class ContentObjectRenderer implements LoggerAwareInterface $fileObject = null; } } catch (Exception $exception) { - $this->logger->warning('The file "' . $fileUidOrCurrentKeyword . '" could not be found and won\'t be included in frontend output', ['exception' => $exception]); + $this->logger->warning('The file "{uid}" could not be found and won\'t be included in frontend output', ['uid' => $fileUidOrCurrentKeyword, 'exception' => $exception]); $fileObject = null; } @@ -4656,7 +4659,10 @@ class ContentObjectRenderer implements LoggerAwareInterface $this->lastTypoLinkLD['target'] = htmlspecialchars($target); $this->lastTypoLinkLD['totalUrl'] = $this->lastTypoLinkUrl; } catch (UnableToLinkException $e) { - $this->logger->debug(sprintf('Unable to link "%s": %s', $e->getLinkText(), $e->getMessage()), ['exception' => $e]); + $this->logger->debug('Unable to link "{text}"', [ + 'text' => $e->getLinkText(), + 'exception' => $e, + ]); // Only return the link text directly return $e->getLinkText(); @@ -5018,7 +5024,7 @@ class ContentObjectRenderer implements LoggerAwareInterface $lastDotLabel = $lastDotLabel ?: '(dot)'; $spamProtectedMailAddress = preg_replace('/\\.([^\\.]+)$/', $lastDotLabel . '$1', $spamProtectedMailAddress); if ($spamProtectedMailAddress === null) { - $this->logger->debug('Error replacing the last dot in email address "' . $spamProtectedMailAddress . '"'); + $this->logger->debug('Error replacing the last dot in email address "{email}"', ['email' => $spamProtectedMailAddress]); $spamProtectedMailAddress = ''; } } diff --git a/typo3/sysext/frontend/Classes/ContentObject/Exception/ProductionExceptionHandler.php b/typo3/sysext/frontend/Classes/ContentObject/Exception/ProductionExceptionHandler.php index 4c75c62a4af9..7a7790300422 100644 --- a/typo3/sysext/frontend/Classes/ContentObject/Exception/ProductionExceptionHandler.php +++ b/typo3/sysext/frontend/Classes/ContentObject/Exception/ProductionExceptionHandler.php @@ -30,10 +30,7 @@ class ProductionExceptionHandler implements ExceptionHandlerInterface, LoggerAwa { use LoggerAwareTrait; - /** - * @var array - */ - protected $configuration = []; + protected array $configuration = []; /** * @param array $configuration @@ -67,7 +64,7 @@ class ProductionExceptionHandler implements ExceptionHandlerInterface, LoggerAwa throw $exception; } } - $errorMessage = $this->configuration['errorMessage'] ?? 'Oops, an error occurred! Code: %s'; + $errorMessage = $this->configuration['errorMessage'] ?? 'Oops, an error occurred! Code: {code}'; $code = date('YmdHis', $_SERVER['REQUEST_TIME']) . GeneralUtility::makeInstance(Random::class)->generateRandomHexString(8); $this->logException($exception, $errorMessage, $code); @@ -82,6 +79,6 @@ class ProductionExceptionHandler implements ExceptionHandlerInterface, LoggerAwa */ protected function logException(\Exception $exception, $errorMessage, $code) { - $this->logger->alert(sprintf($errorMessage, $code), ['exception' => $exception]); + $this->logger->alert($errorMessage, ['exception' => $exception, 'code' => $code]); } } diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index 757e931f4043..492a7995a077 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -1168,7 +1168,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface ); throw new PropagateResponseException($response, 1533931350); } catch (AbstractServerErrorException $e) { - $this->logger->error($message); + $this->logger->error($message, ['exception' => $e]); $exceptionClass = get_class($e); throw new $exceptionClass($message, 1301648167); } @@ -1778,9 +1778,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface $this->sPre = $typoScriptPageTypeName; $this->pSetup = $this->tmpl->setup[$typoScriptPageTypeName . '.']; if (!is_array($this->pSetup)) { - $message = 'The page is not configured! [type=' . $this->type . '][' . $typoScriptPageTypeName . '].'; - $this->logger->alert($message); + $this->logger->alert('The page is not configured! [type={type}][{type_name}].', ['type' => $this->type, 'type_name' => $typoScriptPageTypeName]); try { + $message = 'The page is not configured! [type=' . $this->type . '][' . $typoScriptPageTypeName . '].'; $response = GeneralUtility::makeInstance(ErrorController::class)->internalErrorAction( $request, $message, @@ -3082,8 +3082,10 @@ class TypoScriptFrontendController implements LoggerAwareInterface */ public function set_no_cache($reason = '', $internal = false) { + $context = []; if ($reason !== '') { - $warning = '$TSFE->set_no_cache() was triggered. Reason: ' . $reason . '.'; + $warning = '$TSFE->set_no_cache() was triggered. Reason: {reason}.'; + $context['reason'] = $reason; } else { $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1); // This is a hack to work around ___FILE___ resolving symbolic links @@ -3094,9 +3096,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface } else { $file = str_replace(Environment::getPublicPath() . '/', '', $file); } - $line = $trace[0]['line']; - $trigger = $file . ' on line ' . $line; - $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by ' . $trigger . '.'; + $warning = '$GLOBALS[\'TSFE\']->set_no_cache() was triggered by {file} on line {line}.'; + $context['file'] = $file; + $context['line'] = $trace[0]['line']; } if (!$internal && $GLOBALS['TYPO3_CONF_VARS']['FE']['disableNoCacheParameter']) { $warning .= ' However, $TYPO3_CONF_VARS[\'FE\'][\'disableNoCacheParameter\'] is set, so it will be ignored!'; @@ -3106,9 +3108,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface $this->disableCache(); } if ($internal && $this->isBackendUserLoggedIn()) { - $this->logger->notice($warning); + $this->logger->notice($warning, $context); } else { - $this->logger->warning($warning); + $this->logger->warning($warning, $context); } } diff --git a/typo3/sysext/frontend/Classes/Middleware/PageArgumentValidator.php b/typo3/sysext/frontend/Classes/Middleware/PageArgumentValidator.php index 0f776ebc4729..be93df9436a3 100644 --- a/typo3/sysext/frontend/Classes/Middleware/PageArgumentValidator.php +++ b/typo3/sysext/frontend/Classes/Middleware/PageArgumentValidator.php @@ -104,7 +104,7 @@ class PageArgumentValidator implements MiddlewareInterface, LoggerAwareInterface if (empty($relevantParametersForCacheHashArgument)) { // cHash was given, but nothing to be calculated, so let's do a redirect to the current page // but without the cHash - $this->logger->notice('The incoming cHash "' . $cHash . '" is given but not needed. cHash is unset'); + $this->logger->notice('The incoming cHash "{hash}" is given but not needed. cHash is unset', ['hash' => $cHash]); $uri = $request->getUri(); unset($queryParams['cHash']); $uri = $uri->withQuery(HttpUtility::buildQueryString($queryParams)); diff --git a/typo3/sysext/frontend/Classes/Resource/FileCollector.php b/typo3/sysext/frontend/Classes/Resource/FileCollector.php index 3fa8b54140ac..4259fc554296 100644 --- a/typo3/sysext/frontend/Classes/Resource/FileCollector.php +++ b/typo3/sysext/frontend/Classes/Resource/FileCollector.php @@ -197,11 +197,10 @@ class FileCollector implements \Countable, LoggerAwareInterface $this->addFileObjects(array_values($files)); } } catch (Exception $e) { - $this->logger->warning( - 'The folder with identifier "' . $folderIdentifier - . '" could not be found and won\'t be included in frontend output', - ['exception' => $e] - ); + $this->logger->warning('The folder with identifier "{folder}" could not be found and won\'t be included in frontend output', [ + 'folder' => $folderIdentifier, + 'exception' => $e, + ]); } } } @@ -308,8 +307,12 @@ class FileCollector implements \Countable, LoggerAwareInterface * The storage does not exist anymore * Log the exception message for admins as they maybe can restore the storage */ - $logMessage = $e->getMessage() . ' (table: "' . $tableName . '", fieldName: "' . $fieldName . '", currentId: ' . $currentId . ')'; - $this->logger->error($logMessage, ['exception' => $e]); + $this->logger->error('{exception_message}: table: {table}, field: {field}, currentId: {current_id}', [ + 'table' => $tableName, + 'field' => $fieldName, + 'currentId' => $currentId, + 'exception' => $e, + ]); return []; } diff --git a/typo3/sysext/install/Classes/Service/LanguagePackService.php b/typo3/sysext/install/Classes/Service/LanguagePackService.php index 2c96f6a48cc6..79f313f4da8b 100644 --- a/typo3/sysext/install/Classes/Service/LanguagePackService.php +++ b/typo3/sysext/install/Classes/Service/LanguagePackService.php @@ -276,12 +276,11 @@ class LanguagePackService } } } else { - $this->logger->warning(sprintf( - 'Requesting %s was not successful, got status code %d (%s)', - $languagePackBaseUrl . $packageUrl, - $response->getStatusCode(), - $response->getReasonPhrase() - )); + $this->logger->warning('Requesting {request} was not successful, got status code {status} ({reason})', [ + 'request' => $languagePackBaseUrl . $packageUrl, + 'status' => $response->getStatusCode(), + 'reason' => $response->getReasonPhrase(), + ]); } } catch (\Exception $e) { $operationResult = false; diff --git a/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php b/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php index bf35638374b2..5b054d1dd7ff 100644 --- a/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php +++ b/typo3/sysext/linkvalidator/Classes/Task/ValidatorTask.php @@ -556,9 +556,7 @@ class ValidatorTask extends AbstractTask // Add default template name to task if empty or given template name does not exist $this->emailTemplateName = 'ValidatorTask'; $this->taskNeedsUpdate = true; - $this->logger->notice( - $this->getLanguageService()->sL($this->languageFile . ':tasks.notice.useDefaultTemplate') - ); + $this->logger->notice($this->getLanguageService()->sL($this->languageFile . ':tasks.notice.useDefaultTemplate')); } $fluidEmail = GeneralUtility::makeInstance(FluidEmail::class, $templatePaths); diff --git a/typo3/sysext/redirects/Classes/Service/RedirectService.php b/typo3/sysext/redirects/Classes/Service/RedirectService.php index 1206c37a5020..e8fb9a8afede 100644 --- a/typo3/sysext/redirects/Classes/Service/RedirectService.php +++ b/typo3/sysext/redirects/Classes/Service/RedirectService.php @@ -110,7 +110,7 @@ class RedirectService implements LoggerAwareInterface if ($matchResult) { $possibleRedirects += $allRedirects[$domainName]['regexp'][$regexp]; } elseif ($matchResult === false) { - $this->logger->warning('Invalid regex in redirect', ['regex' => $regexp]); + $this->logger->warning('Invalid regex in redirect: {regex}', ['regex' => $regexp]); } } } @@ -190,11 +190,11 @@ class RedirectService implements LoggerAwareInterface $site = $request->getAttribute('site'); $uri = $request->getUri(); $queryParams = $request->getQueryParams(); - $this->logger->debug('Found a redirect to process', $matchedRedirect); + $this->logger->debug('Found a redirect to process', ['redirect' => $matchedRedirect]); $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode((string)$matchedRedirect['target']); $redirectTarget = $linkParameterParts['url']; $linkDetails = $this->resolveLinkDetailsFromLinkTarget($redirectTarget); - $this->logger->debug('Resolved link details for redirect', $linkDetails); + $this->logger->debug('Resolved link details for redirect', ['details' => $linkDetails]); if (!empty($linkParameterParts['additionalParams']) && $matchedRedirect['keep_query_parameters']) { $params = GeneralUtility::explodeUrl2Array($linkParameterParts['additionalParams']); foreach ($params as $key => $value) { diff --git a/typo3/sysext/scheduler/Classes/Example/TestTask.php b/typo3/sysext/scheduler/Classes/Example/TestTask.php index cf46fc7402c4..150153fedac0 100644 --- a/typo3/sysext/scheduler/Classes/Example/TestTask.php +++ b/typo3/sysext/scheduler/Classes/Example/TestTask.php @@ -47,7 +47,7 @@ class TestTask extends AbstractTask { if (!empty($this->email)) { // If an email address is defined, send a message to it - $this->logger->info('[TYPO3\\CMS\\Scheduler\\Example\\TestTask]: Test email sent to "' . $this->email . '"'); + $this->logger->info('[TYPO3\\CMS\\Scheduler\\Example\\TestTask]: Test email sent to "{email}"', ['email' => $this->email]); $templateConfiguration = $GLOBALS['TYPO3_CONF_VARS']['MAIL']; $templateConfiguration['templateRootPaths'][20] = 'EXT:scheduler/Resources/Private/Templates/Email/'; diff --git a/typo3/sysext/scheduler/Classes/Scheduler.php b/typo3/sysext/scheduler/Classes/Scheduler.php index eee9fc333358..1990d1f66e68 100644 --- a/typo3/sysext/scheduler/Classes/Scheduler.php +++ b/typo3/sysext/scheduler/Classes/Scheduler.php @@ -168,12 +168,17 @@ class Scheduler implements SingletonInterface, LoggerAwareInterface // Task is already running and multiple executions are not allowed if (!$task->areMultipleExecutionsAllowed() && $task->isExecutionRunning()) { // Log multiple execution error - $logMessage = 'Task is already running and multiple executions are not allowed, skipping! Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid(); - $this->logger->info($logMessage); + $this->logger->info('Task is already running and multiple executions are not allowed, skipping! Class: {class}, UID: {uid}', [ + 'class' => get_class($task), + 'uid' => $task->getTaskUid(), + ]); $result = false; } else { // Log scheduler invocation - $this->logger->info('Start execution. Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid()); + $this->logger->info('Start execution. Class: {class}, UID: {uid}', [ + 'class' => get_class($task), + 'uid' => $task->getTaskUid(), + ]); // Register execution $executionID = $task->markExecution(); $failure = null; @@ -190,7 +195,10 @@ class Scheduler implements SingletonInterface, LoggerAwareInterface // Un-register execution $task->unmarkExecution($executionID, $failure); // Log completion of execution - $this->logger->info('Task executed. Class: ' . get_class($task) . ', UID: ' . $task->getTaskUid()); + $this->logger->info('Task executed. Class: {class}, UID: {uid}', [ + 'class' => get_class($task), + 'uid' => $task->getTaskUid(), + ]); // Now that the result of the task execution has been handled, // throw the exception again, if any if ($failure instanceof \Throwable) { @@ -475,23 +483,24 @@ class Scheduler implements SingletonInterface, LoggerAwareInterface if (!($this->logger instanceof LoggerInterface)) { $this->setLogger(GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__)); } - $message = trim('[scheduler]: ' . $code) . ' - ' . $message; + $message = '[scheduler]: {code} - {original_message}'; + // @todo Replace these magic numbers with constants or enums. switch ((int)$status) { // error (user problem) case 1: - $this->logger->alert($message); + $this->logger->alert($message, ['code' => $code, 'original_message' => $message]); break; // System Error (which should not happen) case 2: - $this->logger->error($message); + $this->logger->error($message, ['code' => $code, 'original_message' => $message]); break; // security notice (admin) case 3: - $this->logger->emergency($message); + $this->logger->emergency($message, ['code' => $code, 'original_message' => $message]); break; // regular message (= 0) default: - $this->logger->info($message); + $this->logger->info($message, ['code' => $code, 'original_message' => $message]); } } } diff --git a/typo3/sysext/scheduler/Classes/Task/AbstractTask.php b/typo3/sysext/scheduler/Classes/Task/AbstractTask.php index 6eee0d6aec78..f7d5c57c3536 100644 --- a/typo3/sysext/scheduler/Classes/Task/AbstractTask.php +++ b/typo3/sysext/scheduler/Classes/Task/AbstractTask.php @@ -471,9 +471,9 @@ abstract class AbstractTask implements LoggerAwareInterface * Removes given execution from list * * @param int $executionID Id of the execution to remove. - * @param \Throwable $failure An exception to signal a failed execution + * @param \Throwable $e An exception to signal a failed execution */ - public function unmarkExecution($executionID, \Throwable $failure = null) + public function unmarkExecution($executionID, \Throwable $e = null) { // Get the executions for the task $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) @@ -498,18 +498,20 @@ abstract class AbstractTask implements LoggerAwareInterface } else { $runningExecutionsSerialized = ''; } - if ($failure instanceof \Throwable) { + if ($e instanceof \Throwable) { // Log failed execution - $logMessage = 'Task failed to execute successfully. Class: ' . static::class - . ', UID: ' . $this->taskUid . ', Code: ' . $failure->getCode() . ', ' . $failure->getMessage(); - $this->logger->error($logMessage, ['exception' => $failure]); + $this->logger->error('Task failed to execute successfully. Class: {class}, UID: {uid}', [ + 'class' => __CLASS__, + 'uid' => $this->taskUid, + 'exception' => $e, + ]); // Do not serialize the complete exception or the trace, this can lead to huge strings > 50MB $failureString = serialize([ - 'code' => $failure->getCode(), - 'message' => $failure->getMessage(), - 'file' => $failure->getFile(), - 'line' => $failure->getLine(), - 'traceString' => $failure->getTraceAsString(), + 'code' => $e->getCode(), + 'message' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'traceString' => $e->getTraceAsString(), ]); } else { $failureString = ''; @@ -604,7 +606,7 @@ abstract class AbstractTask implements LoggerAwareInterface */ protected function logException(\Exception $e) { - $this->logger->error('A Task Exception was captured: ' . $e->getMessage() . ' (' . $e->getCode() . ')', ['exception' => $e]); + $this->logger->error('A Task Exception was captured.', ['exception' => $e]); } /** diff --git a/typo3/sysext/workspaces/Classes/Notification/StageChangeNotification.php b/typo3/sysext/workspaces/Classes/Notification/StageChangeNotification.php index 5160599f5de2..ecc3814a5899 100644 --- a/typo3/sysext/workspaces/Classes/Notification/StageChangeNotification.php +++ b/typo3/sysext/workspaces/Classes/Notification/StageChangeNotification.php @@ -118,16 +118,17 @@ class StageChangeNotification implements LoggerAwareInterface try { $this->sendEmail($recipientData, $emailConfig, $viewPlaceholders); } catch (TransportException $e) { - $this->logger->warning('Could not send notification email to "' . $recipientData['email'] . '" due to mailer settings error', [ + $this->logger->warning('Could not send notification email to "{recipient}" due to mailer settings error', [ + 'recipient' => $recipientData['email'], 'recipientList' => array_column($recipients, 'email'), - 'exception' => $e + 'exception' => $e, ]); // At this point we break since the next attempts will also fail due to the invalid mailer settings break; } catch (RfcComplianceException $e) { - $this->logger->warning('Could not send notification email to "' . $recipientData['email'] . '" due to invalid email address', [ - 'recipientList' => [$recipientData['email']], - 'exception' => $e + $this->logger->warning('Could not send notification email to "{recipient}" due to invalid email address', [ + 'recipient' => $recipientData['email'], + 'exception' => $e, ]); } } diff --git a/typo3/sysext/workspaces/Classes/Service/GridDataService.php b/typo3/sysext/workspaces/Classes/Service/GridDataService.php index b738772e353f..3d820756811e 100644 --- a/typo3/sysext/workspaces/Classes/Service/GridDataService.php +++ b/typo3/sysext/workspaces/Classes/Service/GridDataService.php @@ -420,7 +420,11 @@ class GridDataService implements LoggerAwareInterface // Do nothing } } else { - $this->logger->critical('Try to sort "' . $this->sort . '" in "\\TYPO3\\CMS\\Workspaces\\Service\\GridDataService::sortDataArray" but $this->dataArray is empty! This might be the bug #26422 which could not be reproduced yet.'); + $this->logger->critical('Trying to sort by {field} in "{class}::{method}" but $this->dataArray is empty! This might be the bug #26422 which could not be reproduced yet.', [ + 'field' => $this->sort, + 'class' => __CLASS__, + 'method' => __FUNCTION__, + ]); } // Trigger an event for extensibility $event = new SortVersionedDataEvent($this, $this->dataArray, $this->sort, $this->sortDir); -- GitLab