diff --git a/typo3/sysext/backend/Classes/Authentication/PasswordReset.php b/typo3/sysext/backend/Classes/Authentication/PasswordReset.php
index bd4f1492d93a2399f54efa70c6f5d95b9e298749..c431b8a96e417e5cfda52554b2597a74b50dd4d2 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 73bc5bbb9fd46e2c34c9976b5f409e6d30a84f39..17adbcabbff9adc808b643ba37141bdc00361340 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 b72314fec9141c5816778ca39e0f55ac4d48157e..507f55229887ec3679eaf24f7e66d76b427c71d5 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 03058e9a4ad5e09193d9a9a3d6d6617712b1e17b..ca44e70b5e03ccf02d9bc7839eeeffb15eae00a7 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 1a80e8ecd57bab0b0a6c2a30b2fa72ab9991b3dd..b105c8d8944771f931cac265ae900e0fca8362e0 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 6102b69fd150b4909fc9946f5e4a4efa68e39680..148ef517cec5a5e88909f3d34ef0bdd4fdcb6001 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 ff4b1468d64bb18c30249e3b3a89affb2930896d..a03a74dde4556e89e48bca2577c1b36b11b6fa78 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 3637c25b0bb2f4d39c08dfd53b313cd147bea5dd..d82841cec1167a9518c401acb98aecf4620ce668 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 9209db4e5722888f096cff35c564cb637a80d41e..e10d47f0225fb1d5c6302f074b38de56b5e2d426 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 d008373f6b7dfcd42e36c73b3f916226614ca7c9..e4ff49abc98db15168d59f3dbf839639472b0c0d 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 f87ea3a62984e1467539b5b5b2ffae7be04448b5..cca2176c773a61752ccedd8134cbf80c51390751 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 280d3b2043fdeaeb5f2725a5cfbfd397c7668154..a23c9bad0cba06c5f5852baceb50c63e1b8aaab4 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 db49131157cb29af47ed4b2c582098eebc3ce22a..5caa60870526492fadb5fbcfc2ac3f5ee7603ea8 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 74def23e1af4b2330879f47dcab6d1be156456ea..99a042880f08e4606420ec2211edaab954dc931a 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 3af18f9afeb0b3e53cb5776ed613d7de62d75a8f..da0fe92936c9e753fec4e7067c805c16b6e32bcc 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 03c21312707e3e54c22f422b3649addc0d1ec2e1..ccccf4e29aaff9d14969a4473423ce09cbd9d4ea 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 c8c38c13728694ad7c987a088956eece4be03a79..39c864ab0f3843ddb30ab50eeef69b388d68c73b 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 b3e42f20eeea7a2553b7b2cbcafe67a0aa6508fc..70665341aafde155e8f838a16d87104b3791e7e1 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 660022da4e36945125b88827c2440275aeb2e72f..00595937328c89ea3d53fc4e586d47cb2db8651b 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 0af10ed61bac2d4a6c80431c3adc115d1753522b..92404466dfb812cc87592b9276dda3a12376bed5 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 5d71ea6d5e70d3f256f4ee42d4ce94cc54394fa4..16fab5a4e04f2e8b336912539d56948c2d5a9a1b 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 2c8dda800c6076720b7e02e1b9854378d14b4032..cc0016736ef7fac7ade30313761e666205b12174 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 b63554cc8169cb54094adb6e9543395634bfc290..ce601f781c05e90dc6087294d6aaf57ab5464872 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 16249b75550e9729d6f00a39d2dec7608da5960f..e75bf035dd213f773cfc28fc9fd0dfe262e60bb3 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 c72d91d7a3ac590757678979a004e02e1649188f..a49c2604eb39ce01c5b77d40dbeb587671f3ae9f 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 6f24c60657c3a81f37dd89f64710df3e959d23a8..3f9acd4669d6ad0b248838a418965993678942ad 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 2737004cba5b5196d9084e6972950e2d421703f1..1bf9ebe076c01cca77e55214ac2961ba60b1aae0 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 482aa7b8f74884b941bbfdb11d75721661519b7d..506e00918ed78ec448470a866e08ded19b5bc603 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 62c796c381bcac17e19ab5781357a90a48bf9221..ef35044a8baefa7d7a0aa0bd0f67bd5c889d588c 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 5ffb61db1e97c54d023685beb738be8aa3a64764..c025ed7a1718d80f230a7c789e75be0b725799d4 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 089e0c866038f7571c2a7418571ef70099a1c2d1..4e396e0178980d9f2792ff9acec030786cda40b3 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 0000000000000000000000000000000000000000..43c11e6ae5e5f7c5af26fac1fd321eb1ab1c4076
--- /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 eb3c2aeeab7891f74cff232066d0196d4be2f4af..f07c901f26c3f2e391e4a17ff757e3e92cc830ac 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 e5242562c015f9d5bed203eb7dd8c68b6cd02484..db383f023d4aabc4541803d4bf9c713ef80ed291 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 b835f9c0049744fa0cf618add94c55af8639376f..876aeacaf5e6a6c025acdaa3791baafdd801fa99 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 724d2620aab1469351910f1c5aa76e075c7dcb0d..a6130ee610136fc305c8c1ed477eff2015d85ed4 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 68280909a1ef1c264b9a633d6215c5ed7644d7c5..c6e4f4eb0411476b8df128f9a388c2c91cb06f9b 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 28bf87aa3543b9c6b5f401ea73f2bcae33bc8ca3..25fee3e4072e6da4e215caf7264f1a230f66f5d5 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 c7b910879cd4bfcff9d07c9183fb28f30437d23a..f35a45b412a0bac4f581d3b936253697c48b6f5f 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 6be72cba476fb9bd9b4bae021136c010bb9ca499..1285d55a17802c8f219e0da18c84ca2653d50b9b 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 0d5134334e795344c5c792de374f5376feb16e61..359adb211c627261b86e960143e466b214c9cf7e 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 15e0a94c6c8d7b5983c7763cbfcf8bcdb76553cf..393cbaaa2be6f0ef25b2fce52e221eca5d162638 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 4c75c62a4af9961beda573b6dabdec7291ba4dc4..7a779030042289a91415dc524504c2516f680021 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 757e931f404327c68475ea2f199228334d09c3dd..492a7995a07761f415f957e2faf7086e1886be08 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 0f776ebc4729c489b976ce17caa408f12053888a..be93df9436a3945cb10419b1809fc75f1fdea056 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 3fa8b54140acb02d4671035cdc1abd3ed94b1c99..4259fc554296b66688beb8d8f7739b6141286bf5 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 2c96f6a48cc614de365ff178dba5a06de343f1fc..79f313f4da8b6d48e6cb46626de89e75114bb7d0 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 bf35638374b29fbf41a4f707798429106e3eae31..5b054d1dd7ff7e3aa8b7170d45261c4bfcd2cfdc 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 1206c37a5020df07318861adbdeb6ffb5ddb55aa..e8fb9a8afedefbb24cd2550a4a1efbe6378a3a7c 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 cf46fc7402c43231d0671a88cd97f891de01989d..150153fedac02cc403cd3fd605caca8b9e3281bd 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 eee9fc333358f3e77062ec51d581a280250ba918..1990d1f66e68fa5860817448d558ae5569b3f533 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 6eee0d6aec7879636f3ff5ecd134e6a56c9e4122..f7d5c57c3536ec79aef75e3105a076e21e26cb5e 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 5160599f5de2c4402b0d31113e6337fde332bfb0..ecc3814a58995f2b427a037bcd1f335531c6ffb5 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 b738772e353fe48b5afef11e0014e13fdc192ca1..3d820756811ea8518924a6140ae59a667bbde49f 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);