diff --git a/core/lib/Drupal/Core/Logger/LogMessageParser.php b/core/lib/Drupal/Core/Logger/LogMessageParser.php
index 8a67df12dc..4eb5407567 100644
--- a/core/lib/Drupal/Core/Logger/LogMessageParser.php
+++ b/core/lib/Drupal/Core/Logger/LogMessageParser.php
@@ -17,7 +17,7 @@ public function parseMessagePlaceholders(&$message, array &$context) {
       $has_psr3 = TRUE;
       // Transform PSR3 style messages containing placeholders to
       // \Drupal\Component\Utility\SafeMarkup::format() style.
-      $message = preg_replace('/\{(.*)\}/U', '@$1', $message);
+      $message = preg_replace('/\{([^\{}]*)\}/U', '@$1', $message);
     }
     foreach ($context as $key => $variable) {
       // PSR3 style placeholders.
@@ -29,7 +29,7 @@ public function parseMessagePlaceholders(&$message, array &$context) {
           $key = '@' . $key;
         }
       }
-      if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === '!')) {
+      if (!empty($key) && ($key[0] === '@' || $key[0] === '%' || $key[0] === ':')) {
         // The key is now in \Drupal\Component\Utility\SafeMarkup::format() style.
         $variables[$key] = $variable;
       }
diff --git a/core/modules/dblog/src/Controller/DbLogController.php b/core/modules/dblog/src/Controller/DbLogController.php
index 4cfbece02e..44ffc46695 100644
--- a/core/modules/dblog/src/Controller/DbLogController.php
+++ b/core/modules/dblog/src/Controller/DbLogController.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Logger\RfcLogLevel;
+use Drupal\Core\Render\Markup;
 use Drupal\Core\Url;
 use Drupal\user\Entity\User;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -342,30 +343,29 @@ protected function buildFilterQuery() {
    *   The record from the watchdog table. The object properties are: wid, uid,
    *   severity, type, timestamp, message, variables, link, name.
    *
-   * @return string|\Drupal\Core\StringTranslation\TranslatableMarkup|false
+   * @return \Drupal\Component\Render\MarkupInterface|string|false
    *   The formatted log message or FALSE if the message or variables properties
    *   are not set.
    */
   public function formatMessage($row) {
     // Check for required properties.
-    if (isset($row->message, $row->variables)) {
-      $variables = @unserialize($row->variables);
-      // Messages without variables or user specified text.
-      if ($variables === NULL) {
-        $message = Xss::filterAdmin($row->message);
-      }
-      elseif (!is_array($variables)) {
-        $message = $this->t('Log data is corrupted and cannot be unserialized: @message', ['@message' => Xss::filterAdmin($row->message)]);
-      }
-      // Message to translate with injected variables.
-      else {
-        $message = $this->t(Xss::filterAdmin($row->message), $variables);
-      }
+    if (!isset($row->message, $row->variables)) {
+      return FALSE;
+    }
+
+    $variables = @unserialize($row->variables);
+    // Messages without variables or user specified text.
+    if ($variables === NULL) {
+      $message = $row->message;
+    }
+    elseif (!is_array($variables)) {
+      $message = $this->t('Log data is corrupted and cannot be unserialized: @message', ['@message' => Xss::filterAdmin($row->message)]);
     }
+    // Message to translate with injected variables.
     else {
-      $message = FALSE;
+      $message = $this->t($row->message, $variables);
     }
-    return $message;
+    return Markup::create(Xss::filterAdmin($message));
   }
 
   /**
diff --git a/core/modules/dblog/tests/src/Functional/DbLogTest.php b/core/modules/dblog/tests/src/Functional/DbLogTest.php
index 5b82282d6a..d81583214c 100644
--- a/core/modules/dblog/tests/src/Functional/DbLogTest.php
+++ b/core/modules/dblog/tests/src/Functional/DbLogTest.php
@@ -2,13 +2,16 @@
 
 namespace Drupal\Tests\dblog\Functional;
 
+use Drupal\Component\Render\FormattableMarkup;
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Utility\Unicode;
+use Drupal\Component\Utility\Xss;
 use Drupal\Core\Logger\RfcLogLevel;
 use Drupal\Core\Url;
-use Drupal\dblog\Controller\DbLogController;
 use Drupal\Tests\BrowserTestBase;
 use Drupal\Tests\Traits\Core\CronRunTrait;
+use Drupal\dblog\Controller\DbLogController;
+use Drupal\language\Entity\ConfigurableLanguage;
 
 /**
  * Generate events and verify dblog entries; verify user access to log reports
@@ -24,7 +27,7 @@ class DbLogTest extends BrowserTestBase {
    *
    * @var array
    */
-  public static $modules = ['dblog', 'node', 'forum', 'help', 'block'];
+  public static $modules = ['dblog', 'node', 'forum', 'help', 'block', 'locale'];
 
   /**
    * A user with some relevant administrative permissions.
@@ -834,4 +837,154 @@ public function testSameTimestampEntries() {
     $this->assertEquals($entries[2]['message'], 'First Entry #0');
   }
 
+  /**
+   * Make sure log messages in log pages are properly escaped.
+   */
+  public function testLogEventPageMessageEscaped() {
+    $this->drupalLogin($this->adminUser);
+
+    $context = [
+      'request_uri' => 'http://example.com?dblog=1',
+      'referer' => 'http://example.org?dblog=2',
+      'uid' => 0,
+      'channel' => 'testing',
+      'link' => 'foo/bar',
+      'ip' => '0.0.1.0',
+      'timestamp' => REQUEST_TIME,
+    ];
+
+    // Make sure HTML tags are filtered out in admin/reports/dblog/event/.
+    \Drupal::service('logger.dblog')->notice("<script>alert('foo');</script> <strong>Lorem ipsum</strong>", $context);
+    $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
+
+    $this->drupalGet('admin/reports/dblog/event/' . $wid);
+    $this->assertResponse(200);
+    $this->assertNoRaw("<script>alert('foo');</script>");
+    $this->assertRaw("alert('foo'); <strong>Lorem ipsum</strong>");
+  }
+
+  /**
+   * Make sure log messages in log pages are properly translated and filtered.
+   */
+  public function testLogEventPageMessageTranslated() {
+    $this->drupalLogin($this->adminUser);
+
+    $langcode = 'ja';
+    ConfigurableLanguage::createFromLangcode($langcode)->save();
+    $this->config('system.site')->set('default_langcode', $langcode)->save();
+
+    $text_source = '< > & source @one %two :three';
+    $text_translation = '< > & translation @one %two :three';
+
+    $locale_storage = $this->container->get('locale.storage');
+
+    $source = $locale_storage->createString([
+      'source' => $text_source,
+    ]);
+    $source->save();
+    $translation = $locale_storage->createTranslation([
+      'lid' => $source->lid,
+      'language' => $langcode,
+      'translation' => $text_translation,
+    ]);
+    $translation->save();
+
+    $translation_manager = $this->container->get('string_translation');
+    $translation_manager->reset();
+
+    $translation_variables = [
+      '@one' => 'foo',
+      '%two' => 'bar',
+      ':three' => 'http://example.com',
+      'four' => 'twig',
+      'five' => 'plain'
+    ];
+
+    $context = $translation_variables + [
+      'request_uri' => 'http://example.com?dblog=1',
+      'referer' => 'http://example.org?dblog=2',
+      'uid' => 0,
+      'channel' => 'testing',
+      'link' => 'foo/bar',
+      'ip' => '0.0.1.0',
+      'timestamp' => REQUEST_TIME,
+    ];
+
+    \Drupal::service('logger.dblog')->notice($text_source, $context);
+    $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
+
+    // Make sure HTML tags are filtered out after translation.
+    $this->drupalGet('admin/reports/dblog/event/' . $wid);
+    $this->assertResponse(200);
+
+    $source_unfiltered = new FormattableMarkup($text_source, $translation_variables);
+    $source_filtered = Xss::filterAdmin(new FormattableMarkup($text_source, $translation_variables));
+    $translation_unfiltered = new FormattableMarkup($text_translation, $translation_variables);
+    $translation_filtered = Xss::filterAdmin(new FormattableMarkup($text_translation, $translation_variables));
+
+    $this->assertNoRaw($source_unfiltered);
+    $this->assertNoRaw($source_filtered);
+    $this->assertNoRaw($translation_unfiltered);
+    $this->assertRaw($translation_filtered);
+  }
+
+  /**
+   * Test that twig errors are displayed correctly.
+   */
+  public function testMessageParsing() {
+    $this->drupalLogin($this->adminUser);
+
+    $langcode = 'ja';
+    ConfigurableLanguage::createFromLangcode($langcode)->save();
+    $this->config('system.site')->set('default_langcode', $langcode)->save();
+
+    $text_source = 'Incorrect parameter {{foo}} in path {path}: {value}';
+    $text_translation = 'Incorrect parameter {bar} in path /baz: horse';
+
+    $locale_storage = $this->container->get('locale.storage');
+
+    $source = $locale_storage->createString([
+      'source' => $text_source,
+    ]);
+    $source->save();
+    $translation = $locale_storage->createTranslation([
+      'lid' => $source->lid,
+      'language' => $langcode,
+      'translation' => $text_translation,
+    ]);
+    $translation->save();
+
+    $translation_manager = $this->container->get('string_translation');
+    $translation_manager->reset();
+
+    $translation_variables = ['foo' => 'bar', 'path' => '/baz', 'value' => 'horse'];
+
+    $context = $translation_variables + [
+      'request_uri' => 'http://example.com?dblog=1',
+      'referer' => 'http://example.org?dblog=2',
+      'uid' => 0,
+      'channel' => 'testing',
+      'link' => 'foo/bar',
+      'ip' => '0.0.1.0',
+      'timestamp' => REQUEST_TIME,
+    ];
+
+    \Drupal::service('logger.dblog')->notice($text_source, $context);
+    $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField();
+
+    // Make sure HTML tags are filtered out after translation.
+    $this->drupalGet('admin/reports/dblog/event/' . $wid);
+    $this->assertResponse(200);
+
+    $source_unfiltered = new FormattableMarkup($text_source, $translation_variables);
+    $source_filtered = Xss::filterAdmin(new FormattableMarkup($text_source, $translation_variables));
+    $translation_unfiltered = new FormattableMarkup($text_translation, $translation_variables);
+    $translation_filtered = Xss::filterAdmin(new FormattableMarkup($text_translation, $translation_variables));
+
+    $this->assertNoRaw($source_unfiltered);
+    $this->assertNoRaw($source_filtered);
+    $this->assertNoRaw($translation_unfiltered);
+    $this->assertRaw($translation_filtered);
+  }
+
 }
