diff --git a/core/lib/Drupal/Core/Logger/LogMessageParser.php b/core/lib/Drupal/Core/Logger/LogMessageParser.php
index 8a67df12dc..ddd77439f1 100644
--- a/core/lib/Drupal/Core/Logger/LogMessageParser.php
+++ b/core/lib/Drupal/Core/Logger/LogMessageParser.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\Logger;
 
+use Psr\Log\InvalidArgumentException;
+
 /**
  * Parses log messages and their placeholders.
  */
@@ -29,13 +31,39 @@ 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] === ':')) {
+        if (!$this->placeholderIsStringable($variable)) {
+          throw new InvalidArgumentException('Log context contains non-string value for key "' . $key . '"');
+        }
+
         // The key is now in \Drupal\Component\Utility\SafeMarkup::format() style.
-        $variables[$key] = $variable;
+        $variables[$key] = (string) $variable;
       }
     }
 
     return $variables;
   }
 
+  /**
+   * Check if a placeholder value can be converted into a string.
+   *
+   * @param $placeholder
+   *   The placeholder value to analyze.
+   *
+   * @return bool
+   *   Returns TRUE if the placeholder can be converted into a string.
+   */
+  protected function placeholderIsStringable($placeholder) {
+    if (is_resource($placeholder)) {
+      return FALSE;
+    }
+
+    if (is_object($placeholder) && method_exists($placeholder, '__toString')) {
+      return TRUE;
+    }
+
+    return is_null($placeholder) || is_scalar($placeholder);
+  }
+
 }
diff --git a/core/modules/comment/tests/src/Kernel/CommentIntegrationTest.php b/core/modules/comment/tests/src/Kernel/CommentIntegrationTest.php
index af730346cb..9029b7e899 100644
--- a/core/modules/comment/tests/src/Kernel/CommentIntegrationTest.php
+++ b/core/modules/comment/tests/src/Kernel/CommentIntegrationTest.php
@@ -51,6 +51,7 @@ public function testViewMode() {
     // Create a new comment view mode and a view display entity.
     EntityViewMode::create([
       'id' => "comment.$mode",
+      'label' => "comment.$mode",
       'targetEntityType' => 'comment',
       'settings' => ['comment_type' => 'comment'],
     ])->save();
diff --git a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php
index 395517f51f..e853dd049e 100644
--- a/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php
+++ b/core/tests/Drupal/Tests/Core/Logger/LogMessageParserTest.php
@@ -4,6 +4,23 @@
 
 use Drupal\Core\Logger\LogMessageParser;
 use Drupal\Tests\UnitTestCase;
+use Psr\Log\InvalidArgumentException;
+
+/**
+ * A class that isn't string convertible.
+ */
+class NotStringConvertible { }
+
+/**
+ * A class that is string convertible.
+ */
+class StringConvertible {
+
+  public function __toString() {
+    return 'convertible';
+  }
+
+}
 
 /**
  * @coversDefaultClass \Drupal\Core\Logger\LogMessageParser
@@ -22,15 +39,25 @@ class LogMessageParserTest extends UnitTestCase {
    *   An array with the expected values after the test has run.
    *    - message: The expected parsed message.
    *    - context: The expected values of the placeholders.
+   * @param bool $stringable_placeholders
+   *   A boolean flag indicating if the placeholders should be convertible
+   *   into strings.
    *
    * @dataProvider providerTestParseMessagePlaceholders
    * @covers ::parseMessagePlaceholders
    */
-  public function testParseMessagePlaceholders(array $value, array $expected) {
+  public function testParseMessagePlaceholders(array $value, array $expected, $stringable_placeholders) {
     $parser = new LogMessageParser();
-    $message_placeholders = $parser->parseMessagePlaceholders($value['message'], $value['context']);
-    $this->assertEquals($expected['message'], $value['message']);
-    $this->assertEquals($expected['context'], $message_placeholders);
+    try {
+      $message_placeholders = $parser->parseMessagePlaceholders($value['message'], $value['context']);
+      $this->assertEquals($expected['message'], $value['message']);
+      $this->assertEquals($expected['context'], $message_placeholders);
+      $this->assertTrue($stringable_placeholders);
+    }
+    catch (InvalidArgumentException $e) {
+      $this->assertFalse($stringable_placeholders);
+      $this->assertStringStartsWith('Log context contains non-string value for key', $e->getMessage());
+    }
   }
 
   /**
@@ -42,26 +69,79 @@ public function providerTestParseMessagePlaceholders() {
       [
         ['message' => 'User {username} created', 'context' => ['username' => 'Dries']],
         ['message' => 'User @username created', 'context' => ['@username' => 'Dries']],
+        TRUE,
       ],
       // PSR3 style mixed in a format_string style message.
       [
         ['message' => 'User {username} created @time', 'context' => ['username' => 'Dries', '@time' => 'now']],
         ['message' => 'User @username created @time', 'context' => ['@username' => 'Dries', '@time' => 'now']],
+        TRUE,
       ],
       // format_string style message only.
       [
         ['message' => 'User @username created', 'context' => ['@username' => 'Dries']],
         ['message' => 'User @username created', 'context' => ['@username' => 'Dries']],
+        TRUE,
+      ],
+      // format_string style message only.
+      [
+        ['message' => 'Example :url', 'context' => [':url' => 'http://example.com']],
+        ['message' => 'Example :url', 'context' => [':url' => 'http://example.com']],
+        TRUE,
       ],
       // Message without placeholders but wildcard characters.
       [
         ['message' => 'User W-\\};~{&! created @', 'context' => ['' => '']],
         ['message' => 'User W-\\};~{&! created @', 'context' => []],
+        TRUE,
       ],
       // Message with double PSR3 style messages.
       [
         ['message' => 'Test {with} two {encapsuled} strings', 'context' => ['with' => 'together', 'encapsuled' => 'awesome']],
         ['message' => 'Test @with two @encapsuled strings', 'context' => ['@with' => 'together', '@encapsuled' => 'awesome']],
+        TRUE,
+      ],
+      // Placeholders not convertible into a string.
+      [
+        ['message' => 'array @a', 'context' => ['@a' => []]],
+        ['message' => 'array @a', 'context' => ['@a' => []]],
+        FALSE,
+      ],
+      // Placeholders not convertible into a string.
+      [
+        ['message' => 'object @b', 'context' => ['@b' => new StringConvertible()]],
+        ['message' => 'object @b', 'context' => ['@b' => 'convertible']],
+        TRUE,
+      ],
+      // Placeholders not convertible into a string.
+      [
+        ['message' => 'object @b', 'context' => ['@b' => new NotStringConvertible()]],
+        ['message' => 'object @b', 'context' => ['@b' => new NotStringConvertible()]],
+        FALSE,
+      ],
+      // Closures are not convertible into a string.
+      [
+        ['message' => 'closure @c', 'context' => ['@c' => function() {} ]],
+        ['message' => 'closure @c', 'context' => ['@c' => function() {} ]],
+        FALSE,
+      ],
+      // Resources cannot be converted into a string.
+      [
+        ['message' => 'resource @r', 'context' => ['@r' => fopen('php://memory', 'r+') ]],
+        ['message' => 'resource @r', 'context' => ['@r' => fopen('php://memory', 'r+') ]],
+        FALSE,
+      ],
+      // Placeholders not converible into strings that are not the first placeholder.
+      [
+        ['message' => 'mixed @a @b @c', 'context' => ['@a' => 123, '@b' => [1], '@c' => TRUE]],
+        ['message' => 'mixed @a @b @c', 'context' => ['@a' => 123, '@b' => [1], '@c' => TRUE]],
+        FALSE,
+      ],
+      // Message without placeholders, but with other context values.
+      [
+        ['message' => 'message', 'context' => ['not_a_placeholder' => new NotStringConvertible()]],
+        ['message' => 'message', 'context' => []],  // All non placeholders are filtered out.
+        TRUE,
       ],
     ];
   }
