diff --git a/core/lib/Drupal/Core/Entity/EntityType.php b/core/lib/Drupal/Core/Entity/EntityType.php
index 89b83cf..352e442 100644
--- a/core/lib/Drupal/Core/Entity/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/EntityType.php
@@ -161,6 +161,13 @@ class EntityType implements EntityTypeInterface {
   protected $revision_table = NULL;
 
   /**
+   * An array of entity revision metadata keys.
+   *
+   * @var array
+   */
+  protected $revision_metadata_keys = [];
+
+  /**
    * The name of the entity type's data table.
    *
    * @var string|null
@@ -384,6 +391,43 @@ public function hasKey($key) {
   /**
    * {@inheritdoc}
    */
+  public function getRevisionMetadataKeys() {
+    // Provide backwards compatibility in case the revision metadata keys are
+    // not defined in the entity annotation.
+    if (!$this->revision_metadata_keys) {
+      $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->id());
+      if ((isset($base_fields['revision_uid']) && $revision_user = 'revision_uid') || (isset($base_fields['revision_user']) && $revision_user = 'revision_user')) {
+        $this->revision_metadata_keys['revision_user'] = $revision_user;
+      }
+      if ((isset($base_fields['revision_timestamp']) && $revision_timestamp = 'revision_timestamp') || (isset($base_fields['revision_created'])) && $revision_timestamp = 'revision_created') {
+        $this->revision_metadata_keys['revision_created'] = $revision_timestamp;
+      }
+      if ((isset($base_fields['revision_log']) && $revision_log = 'revision_log') || (isset($base_fields['revision_log_message']) && $revision_log = 'revision_log_message')) {
+        $this->revision_metadata_keys['revision_log_message'] = $revision_log;
+      }
+    }
+    return $this->revision_metadata_keys;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRevisionMetadataKey($key) {
+    $keys = $this->getRevisionMetadataKeys();
+    return isset($keys[$key]) ? $keys[$key] : FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasRevisionMetadataKey($key) {
+    $keys = $this->getRevisionMetadataKeys();
+    return isset($keys[$key]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function id() {
     return $this->id;
   }
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
index c6336c9..0f036fd 100644
--- a/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityTypeInterface.php
@@ -133,6 +133,45 @@ public function getKey($key);
   public function hasKey($key);
 
   /**
+   * Gets an array of entity revision metadata keys.
+   *
+   * @return array
+   *   An array describing how the Field API can extract revision metadata
+   *   information of this entity type:
+   *   - revision_log_message: The name of the property that contains description
+   *     of the changes that were made in the current revision.
+   *   - revision_user: The name of the property that contains the user ID of
+   *     the author of the current revision.
+   *   - revision_created: The name of the property that contains the timestamp
+   *     of the current revision.
+   */
+  public function getRevisionMetadataKeys();
+
+  /**
+   * Gets a specific entity revision metadata key.
+   *
+   * @param string $key
+   *   The name of the entity revision metadata key to return.
+   *
+   * @return string|bool
+   *   The entity revision metadata key, or FALSE if it does not exist.
+   *
+   * @see self::getRevisionFields()
+   */
+  public function getRevisionMetadataKey($key);
+
+  /**
+   * Indicates if a given entity revision metadata key exists.
+   *
+   * @param string $key
+   *   The name of the entity revision metadata key to check.
+   *
+   * @return bool
+   *   TRUE if a given entity revision metadata key exists, FALSE otherwise.
+   */
+  public function hasRevisionMetadataKey($key);
+
+  /**
    * Indicates whether entities should be statically cached.
    *
    * @return bool
diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
index 2ea450d..dc51f48 100644
--- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
+++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php
@@ -294,17 +294,11 @@ public function getTableMapping(array $storage_definitions = NULL) {
       // Make sure the key fields come first in the list of fields.
       $all_fields = array_merge($key_fields, array_diff($all_fields, $key_fields));
 
-      // Nodes have all three of these fields, while custom blocks only have
-      // log.
-      // @todo Provide automatic definitions for revision metadata fields in
-      //   https://www.drupal.org/node/2248983.
-      $revision_metadata_fields = array_intersect(array(
-        'revision_timestamp',
-        'revision_uid',
-        'revision_log',
-      ), $all_fields);
-
+      // If the entity is revisionable, gather the fields that need to be put
+      // in the revision table.
       $revisionable = $this->entityType->isRevisionable();
+      $revision_metadata_fields = $revisionable ? array_values($this->entityType->getRevisionMetadataKeys()) : [];
+
       $translatable = $this->entityType->isTranslatable();
       if (!$revisionable && !$translatable) {
         // The base layout stores all the base field values in the base table.
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
index 13af342..277f82f 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -50,6 +50,11 @@
  *     "langcode" = "langcode",
  *     "uuid" = "uuid"
  *   },
+ *   revision_metadata_keys = {
+ *     "revision_user" = "revision_user",
+ *     "revision_created" = "revision_created",
+ *     "revision_log_message" = "revision_log"
+ *   },
  *   bundle_entity_type = "block_content_type",
  *   field_ui_base_route = "entity.block_content_type.edit_form",
  *   render_cache = FALSE,
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index bdb8050..f341254 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -57,6 +57,11 @@
  *     "status" = "status",
  *     "uid" = "uid",
  *   },
+ *   revision_metadata_keys = {
+ *     "revision_user" = "revision_uid",
+ *     "revision_created" = "revision_timestamp",
+ *     "revision_log_message" = "revision_log"
+ *   },
  *   bundle_entity_type = "node_type",
  *   field_ui_base_route = "entity.node_type.edit_form",
  *   common_reference_target = TRUE,
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 3ef24a5..5413191 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1730,3 +1730,92 @@ function system_update_8201() {
 /**
  * @} End of "addtogroup updates-8.2.0".
  */
+
+/**
+ * @addtogroup updates-8.3.0
+ * @{
+ */
+
+/**
+ * Move revision metadata fields from the data tables to the revision table.
+ *
+ * Due to the fields from RevisionLogEntityTrait not being explicitly mentioned
+ * in the storage they might have been installed wrongly in the data tables for
+ * revisionable and translatable entities.
+ */
+function system_update_8300(&$sandbox) {
+  static $revisions = NULL;
+  $entity_definition_update_manager = \Drupal::entityDefinitionUpdateManager();
+  $database = \Drupal::database();
+  $database_schema = $database->schema();
+
+  foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) {
+    if ($entity_type->isRevisionable() && $entity_type->isTranslatable()) {
+      $base_fields = $base_fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($entity_type_id);
+      $fields_to_update = array_intersect_key($base_fields, $entity_type->getRevisionMetadataKeys());
+
+      if (!empty($fields_to_update)) {
+        $data_table = $entity_type->getDataTable() ?: $entity_type_id . '_field_data';
+        $revision_data_table = $entity_type->getRevisionDataTable() ?: $entity_type_id . '_field_revision';
+        $revision_table = $entity_type->getRevisionTable() ?: $entity_type_id . '_revision';
+        $revision_field = $entity_type->getKey('revision');
+
+        foreach ($fields_to_update as $revision_metadata_field_name => $definition) {
+          // If the revision metadata field is present in the data and the
+          // revision data table install its definition again with the updated
+          // storage code in order for the field to be installed in the
+          // revision table, afterwards copy over the field values and remove
+          // the field from the data and the revision data tables.
+          if ($database_schema->fieldExists($data_table, $revision_metadata_field_name) && $database_schema->fieldExists($revision_data_table, $revision_metadata_field_name)) {
+            // Install the field in the revision table.
+            $entity_definition_update_manager->installFieldStorageDefinition($revision_metadata_field_name, $entity_type_id, $entity_type->getProvider(), $definition);
+
+            // Collect the revision ids only once for an entity type.
+            $revisions = isset($revisions) ? $revisions : array_keys(\Drupal::entityQuery($entity_type_id)
+              ->allRevisions()
+              ->execute());
+
+            if (!isset($sandbox['progress'])) {
+              // This must be the first run. Initialize the sandbox.
+              $sandbox['progress'] = 0;
+              $sandbox['current'] = 0;
+              $sandbox['max'] = count($revisions);
+            }
+
+            // Apply the field value from the revision data table to the
+            // revision table.
+            $current = $sandbox['current'];
+            for ($i = $current; $i < $current + 100 && $i < $sandbox['max']; $i++) {
+              $rev_id = $revisions[$i];
+              $field_value = $database->select($revision_data_table, 'rd')
+                ->fields('rd', [$revision_metadata_field_name])
+                ->condition($revision_field, $rev_id)
+                ->execute()
+                ->fetchField();
+              $database->update($revision_table)
+                ->condition($revision_field, $rev_id)
+                ->fields([$revision_metadata_field_name => $field_value])
+                ->execute();
+
+              $sandbox['current']++;
+            }
+
+            $sandbox['#finished'] = $sandbox['current'] == $sandbox['max'] - 1;
+
+            if ($sandbox['#finished']) {
+              // Drop the field from the data tables.
+              $database_schema->dropField($data_table, $revision_metadata_field_name);
+              $database_schema->dropField($revision_data_table, $revision_metadata_field_name);
+              unset($sandbox['progress']);
+            }
+          }
+        }
+        $revisions = NULL;
+      }
+    }
+  }
+}
+
+/**
+ * @} End of "addtogroup updates-8.3.0".
+ */
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
index 4f4f4f1..927dcde 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
@@ -35,6 +35,11 @@
  *     "label" = "name",
  *     "langcode" = "langcode",
  *   },
+ *   revision_metadata_keys = {
+ *     "revision_user" = "revision_uid",
+ *     "revision_created" = "revision_timestamp",
+ *     "revision_log_message" = "revision_log"
+ *   },
  *   links = {
  *     "canonical" = "/entity_test_revlog/manage/{entity_test_revlog}",
  *     "delete-form" = "/entity_test/delete/entity_test_revlog/{entity_test_revlog}",
diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
index 56b4b00..153691f 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
@@ -533,6 +533,9 @@ public function testGetTableMappingRevisionable(array $entity_keys) {
         array('bundle', $entity_keys['bundle']),
         array('revision', $entity_keys['revision']),
       )));
+    $this->entityType->expects($this->any())
+      ->method('getRevisionMetadataKeys')
+      ->will($this->returnValue([]));
 
     $this->setUpEntityStorage();
 
@@ -573,13 +576,13 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
     // PHPUnit does not allow for multiple data providers.
     $test_cases = array(
       array(),
-      array('revision_timestamp'),
-      array('revision_uid'),
-      array('revision_log'),
-      array('revision_timestamp', 'revision_uid'),
-      array('revision_timestamp', 'revision_log'),
-      array('revision_uid', 'revision_log'),
-      array('revision_timestamp', 'revision_uid', 'revision_log'),
+      array('revision_created' => 'revision_timestamp'),
+      array('revision_user' => 'revision_uid'),
+      array('revision_log_message' => 'revision_log'),
+      array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'),
+      array('revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'),
+      array('revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'),
+      array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'),
     );
     foreach ($test_cases as $revision_metadata_field_names) {
       $this->setUp();
@@ -590,7 +593,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
 
       $revisionable_field_names = array('description', 'owner');
       $field_names = array_merge($field_names, $revisionable_field_names);
-      $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, $revision_metadata_field_names), array('isRevisionable' => TRUE));
+      $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), array('isRevisionable' => TRUE));
 
       $this->entityType->expects($this->exactly(2))
         ->method('isRevisionable')
@@ -604,6 +607,10 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
           array('revision', $entity_keys['revision']),
         )));
 
+      $this->entityType->expects($this->any())
+        ->method('getRevisionMetadataKeys')
+        ->will($this->returnValue($revision_metadata_field_names));
+
       $this->setUpEntityStorage();
 
       $mapping = $this->entityStorage->getTableMapping();
@@ -615,7 +622,7 @@ public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
       $expected = array_merge(
         array($entity_keys['id'], $entity_keys['revision']),
         $revisionable_field_names,
-        $revision_metadata_field_names
+        array_values($revision_metadata_field_names)
       );
       $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
 
@@ -760,6 +767,11 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys)
       'uuid' => $entity_keys['uuid'],
       'langcode' => 'langcode',
     );
+    $revision_metadata_keys = array(
+      'revision_created' => 'revision_timestamp',
+      'revision_user' => 'revision_uid',
+      'revision_log_message' => 'revision_log'
+    );
 
     $this->entityType->expects($this->atLeastOnce())
       ->method('isRevisionable')
@@ -779,6 +791,9 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys)
         array('revision', $entity_keys['revision']),
         array('langcode', $entity_keys['langcode']),
       )));
+    $this->entityType->expects($this->any())
+      ->method('getRevisionMetadataKeys')
+      ->will($this->returnValue($revision_metadata_keys));
 
     $this->setUpEntityStorage();
 
@@ -809,6 +824,7 @@ public function testGetTableMappingRevisionableTranslatable(array $entity_keys)
       $entity_keys['revision'],
       $entity_keys['langcode'],
     )));
+    $expected = array_merge($expected, array_values($revision_metadata_keys));
     $actual = $mapping->getFieldNames('entity_test_revision');
     $this->assertEquals($expected, $actual);
     // The UUID is not stored on the data table.
@@ -864,13 +880,13 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent
     // PHPUnit does not allow for multiple data providers.
     $test_cases = array(
       array(),
-      array('revision_timestamp'),
-      array('revision_uid'),
-      array('revision_log'),
-      array('revision_timestamp', 'revision_uid'),
-      array('revision_timestamp', 'revision_log'),
-      array('revision_uid', 'revision_log'),
-      array('revision_timestamp', 'revision_uid', 'revision_log'),
+      array('revision_created' => 'revision_timestamp'),
+      array('revision_user' => 'revision_uid'),
+      array('revision_log_message' => 'revision_log'),
+      array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'),
+      array('revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'),
+      array('revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'),
+      array('revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'),
     );
     foreach ($test_cases as $revision_metadata_field_names) {
       $this->setUp();
@@ -880,7 +896,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent
       $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
 
       $revisionable_field_names = array('description', 'owner');
-      $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, $revision_metadata_field_names), array('isRevisionable' => TRUE));
+      $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), array('isRevisionable' => TRUE));
 
       $this->entityType->expects($this->atLeastOnce())
         ->method('isRevisionable')
@@ -900,6 +916,9 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent
           array('revision', $entity_keys['revision']),
           array('langcode', $entity_keys['langcode']),
         )));
+      $this->entityType->expects($this->any())
+        ->method('getRevisionMetadataKeys')
+        ->will($this->returnValue($revision_metadata_field_names));
 
       $this->setUpEntityStorage();
 
@@ -937,7 +956,7 @@ public function testGetTableMappingRevisionableTranslatableWithFields(array $ent
         $entity_keys['id'],
         $entity_keys['revision'],
         $entity_keys['langcode'],
-      )), $revision_metadata_field_names);
+      )), array_values($revision_metadata_field_names));
       $actual = $mapping->getFieldNames('entity_test_revision');
       $this->assertEquals($expected, $actual);
       // The UUID is not stored on the data table.
