diff --git a/core/modules/migrate/src/Plugin/migrate/process/Log.php b/core/modules/migrate/src/Plugin/migrate/process/Log.php
index 62088f607e8899a92b2877889b481ac602f6416c..8807d51c1c770ea66e016479b97677e90c1020c0 100644
--- a/core/modules/migrate/src/Plugin/migrate/process/Log.php
+++ b/core/modules/migrate/src/Plugin/migrate/process/Log.php
@@ -31,9 +31,38 @@ class Log extends ProcessPluginBase {
    * {@inheritdoc}
    */
   public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
-    // Log the value.
-    $migrate_executable->saveMessage($value);
+    if (is_null($value) || is_bool($value)) {
+      $export = var_export($value, TRUE);
+    }
+    elseif (is_float($value)) {
+      $export = sprintf('%f', $value);
+    }
+    elseif (method_exists($value, 'toString')) {
+      $export = print_r($value->toString(), TRUE);
+    }
+    elseif (method_exists($value, 'toArray')) {
+      $export = print_r($value->toArray(), TRUE);
+    }
+    elseif (is_string($value) || is_numeric($value) || is_array($value)) {
+      $export = print_r($value, TRUE);
+    }
+    elseif (method_exists($value, '__toString')) {
+      $export = print_r((string) $value, TRUE);
+    }
+    else {
+      $export = NULL;
+    }
+
+    $class_name = $export !== NULL && is_object($value)
+      ? $class_name = get_class($value) . ":\n"
+      : '';
 
+    $message = $export === NULL
+      ? "Unable to log the value for '$destination_property'"
+      : "'$destination_property' value is $class_name'$export'";
+
+    // Log the value.
+    $migrate_executable->saveMessage($message);
     // Pass through the same value we received.
     return $value;
   }
diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
index 85b125604ca09076e11a7114c5461c522347a7ce..707144c668b1c4eae0ff5fee8ba965a1c1aec08d 100644
--- a/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
+++ b/core/modules/migrate/tests/src/Kernel/Plugin/LogTest.php
@@ -2,9 +2,11 @@
 
 namespace Drupal\Tests\migrate\Kernel\Plugin;
 
+use Drupal\Core\Url;
 use Drupal\KernelTests\KernelTestBase;
-use Drupal\migrate\MigrateExecutableInterface;
+use Drupal\migrate\MigrateExecutable;
 use Drupal\migrate\Row;
+use Drupal\node\Entity\Node;
 
 /**
  * Tests the Log process plugin.
@@ -16,21 +18,192 @@ class LogTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  protected static $modules = ['migrate'];
+  protected static $modules = ['node', 'migrate'];
+
+  /**
+   * The Log process plugin.
+   *
+   * @var \Drupal\migrate\Plugin\migrate\process\Log
+   */
+  protected $logPlugin;
+
+  /**
+   * Migrate executable.
+   *
+   * @var \Drupal\Tests\migrate\Kernel\Plugin\TestMigrateExecutable
+   */
+  protected $executable;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp(): void {
+    parent::setUp();
+    $definition = [
+      'source' => [
+        'plugin' => 'embedded_data',
+        'data_rows' => [
+          ['id' => '1'],
+        ],
+        'ids' => [
+          'id' => ['type' => 'integer'],
+        ],
+      ],
+      'destination' => [
+        'plugin' => 'null',
+      ],
+    ];
+
+    /** @var \Drupal\migrate\Plugin\migration $migration */
+    $migration = \Drupal::service('plugin.manager.migration')
+      ->createStubMigration($definition);
+    $this->executable = new TestMigrateExecutable($migration);
+
+    // Plugin being tested.
+    $this->logPlugin = \Drupal::service('plugin.manager.migrate.process')
+      ->createInstance('log');
+  }
 
   /**
    * Test the Log plugin.
    */
   public function testLog() {
-    $plugin = \Drupal::service('plugin.manager.migrate.process')
-      ->createInstance('log');
-    $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal();
-    $row = new Row();
-    $log_message = "Testing the log message";
+    $values = [
+      'nid' => 2,
+      'type' => 'page',
+      'title' => 'page',
+    ];
+
+    $node = new Node($values, 'node', 'test');
+    $node_array = <<< NODE
+Array
+(
+    [nid] => Array
+        (
+        )
+
+    [uuid] => Array
+        (
+        )
+
+    [vid] => Array
+        (
+        )
+
+    [langcode] => Array
+        (
+        )
+
+    [type] => Array
+        (
+        )
+
+    [revision_timestamp] => Array
+        (
+        )
+
+    [revision_uid] => Array
+        (
+        )
+
+    [revision_log] => Array
+        (
+        )
+
+    [status] => Array
+        (
+        )
+
+    [uid] => Array
+        (
+        )
 
-    // Ensure the log is getting saved.
-    $saved_message = $plugin->transform($log_message, $executable, $row, 'buffalo');
-    $this->assertSame($log_message, $saved_message);
+    [title] => Array
+        (
+        )
+
+    [created] => Array
+        (
+        )
+
+    [changed] => Array
+        (
+        )
+
+    [promote] => Array
+        (
+        )
+
+    [sticky] => Array
+        (
+        )
+
+    [default_langcode] => Array
+        (
+        )
+
+    [revision_default] => Array
+        (
+        )
+
+    [revision_translation_affected] => Array
+        (
+        )
+
+)
+
+NODE;
+
+    $data = [
+      'node' => [
+        'value' => $node,
+        'expected_message' => "'foo' value is Drupal\\node\Entity\Node:\n'$node_array'",
+      ],
+      'url' => [
+        'value' => Url::fromUri('https://en.wikipedia.org/wiki/Drupal#Community'),
+        'expected_message' => "'foo' value is Drupal\Core\Url:\n'https://en.wikipedia.org/wiki/Drupal#Community'",
+      ],
+    ];
+
+    $i = 1;
+    foreach ($data as $datum) {
+      $this->executable->sourceIdValues = ['id' => $i++];
+
+      // Test the input value is not altered.
+      $new_value = $this->logPlugin->transform($datum['value'], $this->executable, new Row(), 'foo');
+      $this->assertSame($datum['value'], $new_value);
+
+      // Test the stored message.
+      $message = $this->executable->getIdMap()
+        ->getMessages($this->executable->sourceIdValues)
+        ->fetchAllAssoc('message');
+      $actual_message = key($message);
+      $this->assertSame($datum['expected_message'], $actual_message);
+    }
+  }
+
+}
+
+/**
+ * MigrateExecutable test class.
+ */
+class TestMigrateExecutable extends MigrateExecutable {
+
+  /**
+   * The configuration values of the source.
+   *
+   * @var array
+   */
+  public $sourceIdValues;
+
+  /**
+   * Get the ID map from the current migration.
+   *
+   * @return \Drupal\migrate\Plugin\MigrateIdMapInterface
+   *   The ID map.
+   */
+  public function getIdMap() {
+    return parent::getIdMap();
   }
 
 }
diff --git a/core/modules/migrate/tests/src/Unit/process/LogTest.php b/core/modules/migrate/tests/src/Unit/process/LogTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..56c6817d524fddeeb3f34c606bbbc5e616d9c28d
--- /dev/null
+++ b/core/modules/migrate/tests/src/Unit/process/LogTest.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Drupal\Tests\migrate\Unit\process;
+
+use Drupal\migrate\Plugin\migrate\process\Log;
+use Drupal\migrate\Row;
+
+/**
+ * Tests the Log process plugin.
+ *
+ * @group migrate
+ */
+class LogTest extends MigrateProcessTestCase {
+
+  /**
+   * Tests the Log plugin.
+   *
+   * @dataProvider providerTestLog()
+   */
+  public function testLog($value, $expected_message) {
+    // Test the expected log message.
+    $this->migrateExecutable->expects($this->once())
+      ->method('saveMessage')
+      ->with($expected_message);
+    $plugin = new Log([], 'log', []);
+
+    // Test the input value is not altered.
+    $new_value = $plugin->transform($value, $this->migrateExecutable, new Row(), 'foo');
+    $this->assertSame($value, $new_value);
+  }
+
+  /**
+   * Provides data for testLog.
+   *
+   * @return \string[][]
+   *   An array of test data arrays.
+   */
+  public function providerTestLog() {
+    $object = (object) [
+      'a' => 'test',
+      'b' => 'test2',
+      'c' => 'test3',
+    ];
+    $xml_str = <<<XML
+<?xml version='1.0'?>
+<mathematician>
+ <name>Ada Lovelace</name>
+</mathematician>
+XML;
+    return [
+      'int zero' => [
+        'value' => 0,
+        'expected_message' => "'foo' value is '0'",
+      ],
+      'string empty' => [
+        'value' => '',
+        'expected_message' => "'foo' value is ''",
+      ],
+      'string' => [
+        'value' => 'Testing the log message',
+        'expected_message' => "'foo' value is 'Testing the log message'",
+      ],
+      'array' => [
+        'value' => ['key' => 'value'],
+        'expected_message' => "'foo' value is 'Array\n(\n    [key] => value\n)\n'",
+      ],
+      'float' => [
+        'value' => 1.123,
+        'expected_message' => "'foo' value is '1.123000'",
+      ],
+      'NULL' => [
+        'value' => NULL,
+        'expected_message' => "'foo' value is 'NULL'",
+      ],
+      'boolean' => [
+        'value' => TRUE,
+        'expected_message' => "'foo' value is 'true'",
+      ],
+      'object_with_to_String' => [
+        'value' => new ObjWithString(),
+        'expected_message' => "'foo' value is Drupal\Tests\migrate\Unit\process\ObjWithString:\n'a test string'",
+      ],
+      'object_no_to_string' => [
+        'value' => $object,
+        'expected_message' => "Unable to log the value for 'foo'",
+      ],
+      'simple_xml' => [
+        'value' => new \SimpleXMLElement($xml_str),
+        'expected_message' => "'foo' value is SimpleXMLElement:\n'\n \n'",
+      ],
+    ];
+  }
+
+}
+
+/**
+ * Test class with a __toString() method.
+ */
+class ObjWithString {
+
+  /**
+   * Returns a string.
+   *
+   * @return string
+   *   A string.
+   */
+  public function __toString() {
+    return 'a test string';
+  }
+
+}