diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index b52ed5015a2dddf44a0241ba566ddc3690b03da3..5a6ec7051b935b6b5eecba67ed64139c4c4f4ffe 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -291,6 +291,12 @@ public function eventDetails($event_id) {
         ['data' => ['#markup' => $dblog->link]],
       ],
     ];
+    if (isset($dblog->backtrace)) {
+      $rows[] = [
+        ['data' => $this->t('Backtrace'), 'header' => TRUE],
+        $dblog->backtrace,
+      ];
+    }
     $build['dblog_table'] = [
       '#type' => 'table',
       '#rows' => $rows,
@@ -350,6 +356,10 @@ protected function buildFilterQuery(Request $request) {
    *   The record from the watchdog table. The object properties are: wid, uid,
    *   severity, type, timestamp, message, variables, link, name.
    *
+   *   If the variables contain a @backtrace_string placeholder which is not
+   *   used in the message, the formatted backtrace will be assigned to a new
+   *   backtrace property on the row object which can be displayed separately.
+   *
    * @return string|\Drupal\Core\StringTranslation\TranslatableMarkup|false
    *   The formatted log message or FALSE if the message or variables properties
    *   are not set.
@@ -372,6 +382,10 @@ public function formatMessage($row) {
           $variables['@backtrace_string'] = new FormattableMarkup(
             '<pre class="backtrace">@backtrace_string</pre>', $variables
           );
+          // Save a reference so the backtrace can be displayed separately.
+          if (!str_contains($row->message, '@backtrace_string')) {
+            $row->backtrace = $variables['@backtrace_string'];
+          }
         }
         $message = $this->t(Xss::filterAdmin($row->message), $variables);
       }
diff --git a/core/modules/dblog/tests/src/Functional/DbLogTest.php b/core/modules/dblog/tests/src/Functional/DbLogTest.php
index b4ef303d04e504a62db5571572e4038e44438efa..f5d7e0467fe69a219766a6d5cba29bd36bccd6a4 100644
--- a/core/modules/dblog/tests/src/Functional/DbLogTest.php
+++ b/core/modules/dblog/tests/src/Functional/DbLogTest.php
@@ -142,6 +142,36 @@ public function testLogEventPage() {
     $this->assertSession()->pageTextContains('Notice');
   }
 
+  /**
+   * Tests that the details page displays the backtrace for a logged \Throwable.
+   */
+  public function testOnError(): void {
+    // Log in as the admin user.
+    $this->drupalLogin($this->adminUser);
+
+    // Load a page that throws an exception in the controller, and includes its
+    // function arguments in the exception backtrace.
+    $this->drupalGet('error-test/trigger-exception');
+
+    // Load the details page for the most recent event logged by the "php"
+    // logger.
+    $query = Database::getConnection()->select('watchdog')
+      ->condition('type', 'php');
+    $query->addExpression('MAX([wid])');
+    $wid = $query->execute()->fetchField();
+    $this->drupalGet('admin/reports/dblog/event/' . $wid);
+
+    // Verify the page displays a dblog-event table with a "Type" header.
+    $table = $this->assertSession()->elementExists('xpath', "//table[@class='dblog-event']");
+    $type = "//tr/th[contains(text(), 'Type')]/../td";
+    $this->assertSession()->elementsCount('xpath', $type, 1, $table);
+
+    // Verify that the backtrace row exists and is HTML-encoded.
+    $backtrace = "//tr//pre[contains(@class, 'backtrace')]";
+    $this->assertCount(1, $table->findAll('xpath', $backtrace));
+    $this->assertSession()->responseContains('&lt;script&gt;alert(&#039;xss&#039;)&lt;/script&gt;');
+  }
+
   /**
    * Tests that a 403 event is logged with the exception triggering it.
    */
diff --git a/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php b/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
index b15bb9b00c664d34e7f0166a162db67ddc5b7cca..d2845e6d5ac0a12444c87a08779837115b8c5313 100644
--- a/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
+++ b/core/modules/system/tests/modules/error_test/src/Controller/ErrorTestController.php
@@ -68,9 +68,17 @@ public function generateFatals() {
 
   /**
    * Trigger an exception to test the exception handler.
+   *
+   * @param string $argument
+   *   A function argument which will be included in the exception backtrace.
+   *
+   * @throws \Exception
    */
-  public function triggerException() {
+  public function triggerException(string $argument = "<script>alert('xss')</script>"): void {
     define('SIMPLETEST_COLLECT_ERRORS', FALSE);
+    // Add function arguments to the exception backtrace.
+    ini_set('zend.exception_ignore_args', FALSE);
+    ini_set('zend.exception_string_param_max_len', 1024);
     throw new \Exception("Drupal & awesome");
   }