diff --git a/core/modules/statistics/config/install/statistics.settings.yml b/core/modules/statistics/config/install/statistics.settings.yml
index 6686062..8c73e19 100644
--- a/core/modules/statistics/config/install/statistics.settings.yml
+++ b/core/modules/statistics/config/install/statistics.settings.yml
@@ -1,2 +1,2 @@
-count_content_views: 0
+entity_type_ids: {}
 display_max_age: 3600
diff --git a/core/modules/statistics/config/schema/statistics.schema.yml b/core/modules/statistics/config/schema/statistics.schema.yml
index c72a227..0c2cd05 100644
--- a/core/modules/statistics/config/schema/statistics.schema.yml
+++ b/core/modules/statistics/config/schema/statistics.schema.yml
@@ -4,12 +4,15 @@ statistics.settings:
   type: config_object
   label: 'Statistics settings'
   mapping:
-    count_content_views:
-      type: integer
-      label: 'Count content views'
     display_max_age:
       type: integer
       label: 'How long any statistics may be cached, i.e. the refresh interval'
+    entity_type_ids:
+      type: sequence
+      label: 'Entity Type IDs'
+      sequence:
+        type: string
+        label: 'Entity Type ID'
 
 block.settings.statistics_popular_block:
   type: block_settings
diff --git a/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php b/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php
deleted file mode 100644
index 32d2872..0000000
--- a/core/modules/statistics/src/NodeStatisticsDatabaseStorage.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-namespace Drupal\statistics;
-
-use Drupal\Core\Database\Connection;
-use Drupal\Core\State\StateInterface;
-use Symfony\Component\HttpFoundation\RequestStack;
-
-/**
- * Provides the default database storage backend for statistics.
- */
-class NodeStatisticsDatabaseStorage implements StatisticsStorageInterface {
-
-  /**
-  * The database connection used.
-  *
-  * @var \Drupal\Core\Database\Connection
-  */
-  protected $connection;
-
-  /**
-   * The state service.
-   *
-   * @var \Drupal\Core\State\StateInterface
-   */
-  protected $state;
-
-  /**
-   * The request stack.
-   *
-   * @var \Symfony\Component\HttpFoundation\RequestStack
-   */
-  protected $requestStack;
-
-  /**
-   * Constructs the statistics storage.
-   *
-   * @param \Drupal\Core\Database\Connection $connection
-   *   The database connection for the node view storage.
-   * @param \Drupal\Core\State\StateInterface $state
-   *   The state service.
-   */
-  public function __construct(Connection $connection, StateInterface $state, RequestStack $request_stack) {
-    $this->connection = $connection;
-    $this->state = $state;
-    $this->requestStack = $request_stack;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function recordView($id) {
-    return (bool) $this->connection
-      ->merge('node_counter')
-      ->key('nid', $id)
-      ->fields([
-        'daycount' => 1,
-        'totalcount' => 1,
-        'timestamp' => $this->getRequestTime(),
-      ])
-      ->expression('daycount', 'daycount + 1')
-      ->expression('totalcount', 'totalcount + 1')
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fetchViews($ids) {
-    $views = $this->connection
-      ->select('node_counter', 'nc')
-      ->fields('nc', ['totalcount', 'daycount', 'timestamp'])
-      ->condition('nid', $ids, 'IN')
-      ->execute()
-      ->fetchAll();
-    foreach ($views as $id => $view) {
-      $views[$id] = new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp);
-    }
-    return $views;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fetchView($id) {
-    $views = $this->fetchViews(array($id));
-    return reset($views);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function fetchAll($order = 'totalcount', $limit = 5) {
-    assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument.");
-
-    return $this->connection
-      ->select('node_counter', 'nc')
-      ->fields('nc', ['nid'])
-      ->orderBy($order, 'DESC')
-      ->range(0, $limit)
-      ->execute()
-      ->fetchCol();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function deleteViews($id) {
-    return (bool) $this->connection
-      ->delete('node_counter')
-      ->condition('nid', $id)
-      ->execute();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function resetDayCount() {
-    $statistics_timestamp = $this->state->get('statistics.day_timestamp') ?: 0;
-    if (($this->getRequestTime() - $statistics_timestamp) >= 86400) {
-      $this->state->set('statistics.day_timestamp', $this->getRequestTime());
-      $this->connection->update('node_counter')
-        ->fields(['daycount' => 0])
-        ->execute();
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function maxTotalCount() {
-    $query = $this->connection->select('node_counter', 'nc');
-    $query->addExpression('MAX(totalcount)');
-    $max_total_count = (int)$query->execute()->fetchField();
-    return $max_total_count;
-  }
-
-  /**
-   * Get current request time.
-   *
-   * @return int
-   *   Unix timestamp for current server request time.
-   */
-  protected function getRequestTime() {
-    return $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME');
-  }
-
-}
diff --git a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
index 8bd84b2..3e195d7 100644
--- a/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
+++ b/core/modules/statistics/src/Plugin/Block/StatisticsPopularBlock.php
@@ -83,7 +83,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('entity_type.manager'),
       $container->get('entity.repository'),
-      $container->get('statistics.storage.node'),
+      $container->get('statistics.storage'),
       $container->get('renderer')
     );
   }
@@ -95,7 +95,7 @@ public function defaultConfiguration() {
     return array(
       'top_day_num' => 0,
       'top_all_num' => 0,
-      'top_last_num' => 0
+      'top_last_num' => 0,
     );
   }
 
@@ -151,9 +151,9 @@ public function blockSubmit($form, FormStateInterface $form_state) {
    */
   public function build() {
     $content = array();
-
+    $entity_type = $this->entityTypeManager->getDefinition('node');
     if ($this->configuration['top_day_num'] > 0) {
-      $nids = $this->statisticsStorage->fetchAll('daycount', $this->configuration['top_day_num']);
+      $nids = $this->statisticsStorage->fetchAll($entity_type, 'daycount', $this->configuration['top_day_num']);
       if ($nids) {
         $content['top_day'] = $this->nodeTitleList($nids, $this->t("Today's:"));
         $content['top_day']['#suffix'] = '<br />';
@@ -161,7 +161,7 @@ public function build() {
     }
 
     if ($this->configuration['top_all_num'] > 0) {
-      $nids = $this->statisticsStorage->fetchAll('totalcount', $this->configuration['top_all_num']);
+      $nids = $this->statisticsStorage->fetchAll($entity_type, 'totalcount', $this->configuration['top_all_num']);
       if ($nids) {
         $content['top_all'] = $this->nodeTitleList($nids, $this->t('All time:'));
         $content['top_all']['#suffix'] = '<br />';
@@ -169,7 +169,7 @@ public function build() {
     }
 
     if ($this->configuration['top_last_num'] > 0) {
-      $nids = $this->statisticsStorage->fetchAll('timestamp', $this->configuration['top_last_num']);
+      $nids = $this->statisticsStorage->fetchAll($entity_type, 'timestamp', $this->configuration['top_last_num']);
       $content['top_last'] = $this->nodeTitleList($nids, $this->t('Last viewed:'));
       $content['top_last']['#suffix'] = '<br />';
     }
diff --git a/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php b/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php
index fb0eb30..0375269 100644
--- a/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php
+++ b/core/modules/statistics/src/Plugin/views/field/NodeCounterTimestamp.php
@@ -2,9 +2,6 @@
 
 namespace Drupal\statistics\Plugin\views\field;
 
-use Drupal\views\Plugin\views\field\Date;
-use Drupal\Core\Session\AccountInterface;
-
 /**
  * Field handler to display the most recent time the node has been viewed.
  *
@@ -12,13 +9,6 @@
  *
  * @ViewsField("node_counter_timestamp")
  */
-class NodeCounterTimestamp extends Date {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(AccountInterface $account) {
-    return $account->hasPermission('view post access counter');
-  }
+class NodeCounterTimestamp extends EntityCounterTimestamp {
 
 }
diff --git a/core/modules/statistics/src/StatisticsDatabaseStorage.php b/core/modules/statistics/src/StatisticsDatabaseStorage.php
new file mode 100644
index 0000000..fbd0f27
--- /dev/null
+++ b/core/modules/statistics/src/StatisticsDatabaseStorage.php
@@ -0,0 +1,173 @@
+<?php
+
+namespace Drupal\statistics;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Provides the default database storage backend for statistics.
+ */
+class StatisticsDatabaseStorage implements StatisticsStorageInterface {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $connection;
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Constructs the statistics storage.
+   *
+   * @param \Drupal\Core\Database\Connection $connection
+   *   The database connection for the node view storage.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
+   */
+  public function __construct(Connection $connection, RequestStack $request_stack) {
+    $this->connection = $connection;
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function recordView($entity_type_id, $key , $id) {
+    $table = $entity_type_id . '_counter';
+    return (bool) $this->connection
+      ->merge($table)
+      ->key($key, $id)
+      ->fields([
+        'daycount' => 1,
+        'totalcount' => 1,
+        'timestamp' => $this->requestStack->getCurrentRequest()->server->get('REQUEST_TIME'),
+      ])
+      ->expression('daycount', 'daycount + 1')
+      ->expression('totalcount', 'totalcount + 1')
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchView(EntityTypeInterface $entity_type, $id) {
+    $entity_type_id = $entity_type->id();
+    $table = $entity_type_id . '_counter';
+    $view = $this->connection
+      ->select($table, 'c')
+      ->fields('c', ['totalcount', 'daycount', 'timestamp'])
+      ->condition($entity_type->getKey('id'), $id)
+      ->execute()
+      ->fetchObject();
+    if ($view) {
+      return new StatisticsViewsResult($view->totalcount, $view->daycount, $view->timestamp);
+    }
+    return NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchAll(EntityTypeInterface $entity_type, $order = 'totalcount', $limit = 5) {
+    assert(in_array($order, ['totalcount', 'daycount', 'timestamp']), "Invalid order argument.");
+    $table = $entity_type->id() . '_counter';
+    return $this->connection
+      ->select($table, 'nc')
+      ->fields('nc', [$entity_type->getKey('id')])
+      ->orderBy($order, 'DESC')
+      ->range(0, $limit)
+      ->execute()
+      ->fetchCol();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deleteViews(EntityTypeInterface $entity_type, $id) {
+    $table = $entity_type->id() . '_counter';
+    return (bool) $this->connection
+      ->delete($table)
+      ->condition($entity_type->getKey('id'), $id)
+      ->execute();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function maxTotalCount($entity_type_id) {
+    $table = $entity_type_id . '_counter';
+    $query = $this->connection->select($table, 'nc');
+    $query->addExpression('MAX(totalcount)');
+    $max_total_count = (int) $query->execute()->fetchField();
+    return $max_total_count;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createTable(EntityTypeInterface $entity_type) {
+    $entity_type_id = $entity_type->id();
+    $idKey = $entity_type->getKey('id');
+    $table = $entity_type_id . '_counter';
+    if (!$this->connection->schema()->tableExists($table)) {
+      $schema = [
+        'description' => 'Access statistics for {' . $entity_type_id . '}s.',
+        'fields' => [
+          $idKey => [
+            'description' => "The {{$entity_type_id}}.$idKey for these statistics.",
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0,
+          ],
+          'totalcount' => [
+            'description' => "The total number of times the {{$entity_type_id}} has been viewed.",
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0,
+            'size' => 'big',
+          ],
+          'daycount' => [
+            'description' => "The total number of times the {{$entity_type_id}} has been viewed today.",
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0,
+            'size' => 'medium',
+          ],
+          'timestamp' => [
+            'description' => "The most recent time the {{$entity_type_id}} has been viewed.",
+            'type' => 'int',
+            'unsigned' => TRUE,
+            'not null' => TRUE,
+            'default' => 0,
+          ],
+        ],
+        'primary key' => [$idKey],
+      ];
+      $this->connection->schema()->createTable($table, $schema);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function dropTable(EntityTypeInterface $entity_type) {
+    $entity_type_id = $entity_type->id();
+    $table = $entity_type_id . '_counter';
+    if ($this->connection->schema()->tableExists($table)) {
+      $this->connection->schema()->dropTable($table);
+    }
+  }
+
+}
diff --git a/core/modules/statistics/src/StatisticsSettingsForm.php b/core/modules/statistics/src/StatisticsSettingsForm.php
index 2abb564..81a0fc8 100644
--- a/core/modules/statistics/src/StatisticsSettingsForm.php
+++ b/core/modules/statistics/src/StatisticsSettingsForm.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\statistics;
 
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\EntityTypeRepositoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Drupal\Core\Config\ConfigFactoryInterface;
@@ -21,16 +23,45 @@ class StatisticsSettingsForm extends ConfigFormBase {
   protected $moduleHandler;
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity type repository service.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeRepositoryInterface
+   */
+  protected $entityTypeRepository;
+
+  /**
+   * The storage for statistics.
+   *
+   * @var \Drupal\statistics\StatisticsStorageInterface
+   */
+  protected $statisticsStorage;
+
+  /**
    * Constructs a \Drupal\user\StatisticsSettingsForm object.
    *
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The factory for configuration objects.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   * @param \Drupal\Core\Entity\EntityTypeRepositoryInterface $entity_type_repository
+   *   The entity type repository service.
+   * @param \Drupal\statistics\StatisticsStorageInterface $statistics_storage
+   *   The storage for statistics.
    */
-  public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
+  public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, EntityTypeRepositoryInterface $entity_type_repository, StatisticsStorageInterface $statistics_storage) {
     parent::__construct($config_factory);
-
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityTypeRepository = $entity_type_repository;
+    $this->statisticsStorage = $statistics_storage;
     $this->moduleHandler = $module_handler;
   }
 
@@ -40,7 +71,10 @@ public function __construct(ConfigFactoryInterface $config_factory, ModuleHandle
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('config.factory'),
-      $container->get('module_handler')
+      $container->get('module_handler'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_type.repository'),
+      $container->get('statistics.storage')
     );
   }
 
@@ -63,19 +97,23 @@ protected function getEditableConfigNames() {
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
     $config = $this->config('statistics.settings');
+    $labels = $this->entityTypeRepository->getEntityTypeLabels(TRUE);
+    $options = $labels[(string) $this->t('Content', [], ['context' => 'Entity type group'])];
 
-    // Content counter settings.
-    $form['content'] = array(
+    // Content entity counter settings.
+    $form['content'] = [
       '#type' => 'details',
-      '#title' => t('Content viewing counter settings'),
+      '#title' => $this->t('Content entity viewing counter settings'),
+      '#description' => $this->t('Increment a counter each time content entity is viewed.'),
       '#open' => TRUE,
-    );
-    $form['content']['statistics_count_content_views'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Count content views'),
-      '#default_value' => $config->get('count_content_views'),
-      '#description' => t('Increment a counter each time content is viewed.'),
-    );
+    ];
+    $form['content']['entity_type_ids'] = [
+      '#type' => 'checkboxes',
+      '#title' => $this->t('Select entities'),
+      '#options' => $options,
+      '#default_value' => $config->get('entity_type_ids'),
+
+    ];
 
     return parent::buildForm($form, $form_state);
   }
@@ -84,9 +122,19 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
+    $entity_type_ids = $form_state->getValue('entity_type_ids');
     $this->config('statistics.settings')
-      ->set('count_content_views', $form_state->getValue('statistics_count_content_views'))
+      ->set('entity_type_ids', $entity_type_ids)
       ->save();
+    foreach (array_keys($entity_type_ids) as $entity_type_id) {
+      $entity_type = $this->entityTypeManager->getDefinition($entity_type_id);
+      if ($entity_type_ids[$entity_type_id] === $entity_type_id) {
+        $this->statisticsStorage->createTable($entity_type);
+      }
+      else {
+        $this->statisticsStorage->dropTable($entity_type);
+      }
+    }
 
     // The popular statistics block is dependent on these settings, so clear the
     // block plugin definitions cache.
diff --git a/core/modules/statistics/src/StatisticsStorageInterface.php b/core/modules/statistics/src/StatisticsStorageInterface.php
index ccb51e4..e7b0327 100644
--- a/core/modules/statistics/src/StatisticsStorageInterface.php
+++ b/core/modules/statistics/src/StatisticsStorageInterface.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\statistics;
 
+use Drupal\Core\Entity\EntityTypeInterface;
+
 /**
  * Provides an interface defining Statistics Storage.
  *
@@ -13,73 +15,87 @@
   /**
    * Count a entity view.
    *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   * @param int $key
+   *   The ID key of the entity to count.
    * @param int $id
    *   The ID of the entity to count.
    *
    * @return bool
    *   TRUE if the entity view has been counted.
    */
-  public function recordView($id);
-
-  /**
-   * Returns the number of times entities have been viewed.
-   *
-   * @param array $ids
-   *   An array of IDs of entities to fetch the views for.
-   *
-   * @return array \Drupal\statistics\StatisticsViewsResult
-   */
-  public function fetchViews($ids);
+  public function recordView($entity_type_id, $key, $id);
 
   /**
    * Returns the number of times a single entity has been viewed.
    *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
    * @param int $id
    *   The ID of the entity to fetch the views for.
    *
-   * @return \Drupal\statistics\StatisticsViewsResult
+   * @return \Drupal\statistics\StatisticsViewsResult|null
+   *   The StatisticsViewsResult object if the result is present NULL otherwise.
    */
-  public function fetchView($id);
+  public function fetchView(EntityTypeInterface $entity_type, $id);
 
   /**
-   * Returns the number of times a entity has been viewed.
+   * Returns the number of times an entity has been viewed.
    *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
    * @param string $order
    *   The counter name to order by:
    *   - 'totalcount' The total number of views.
    *   - 'daycount' The number of views today.
    *   - 'timestamp' The unix timestamp of the last view.
-   *
    * @param int $limit
    *   The number of entity IDs to return.
    *
    * @return array
    *   An ordered array of entity IDs.
    */
-  public function fetchAll($order = 'totalcount', $limit = 5);
+  public function fetchAll(EntityTypeInterface $entity_type, $order = 'totalcount', $limit = 5);
 
   /**
    * Delete counts for a specific entity.
    *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
    * @param int $id
    *   The ID of the entity which views to delete.
    *
    * @return bool
    *   TRUE if the entity views have been deleted.
    */
-  public function deleteViews($id);
-
-  /**
-   * Reset the day counter for all entities once every day.
-   */
-  public function resetDayCount();
+  public function deleteViews(EntityTypeInterface $entity_type, $id);
 
   /**
    * Returns the highest 'totalcount' value.
    *
+   * @param string $entity_type_id
+   *   The entity type ID.
+   *
    * @return int
    *   The highest 'totalcount' value.
    */
-  public function maxTotalCount();
+  public function maxTotalCount($entity_type_id);
+
+  /**
+   * Creates entity counter table.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   */
+  public function createTable(EntityTypeInterface $entity_type);
+
+  /**
+   * Drops entity counter table.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   */
+  public function dropTable(EntityTypeInterface $entity_type);
 
 }
diff --git a/core/modules/statistics/statistics.install b/core/modules/statistics/statistics.install
index f232e1d..45c0df4 100644
--- a/core/modules/statistics/statistics.install
+++ b/core/modules/statistics/statistics.install
@@ -9,53 +9,13 @@
  * Implements hook_uninstall().
  */
 function statistics_uninstall() {
+  $state = \Drupal::state();
   // Remove states.
-  \Drupal::state()->delete('statistics.node_counter_scale');
-  \Drupal::state()->delete('statistics.day_timestamp');
-}
-
-/**
- * Implements hook_schema().
- */
-function statistics_schema() {
-  $schema['node_counter'] = array(
-    'description' => 'Access statistics for {node}s.',
-    'fields' => array(
-      'nid' => array(
-        'description' => 'The {node}.nid for these statistics.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-      'totalcount' => array(
-        'description' => 'The total number of times the {node} has been viewed.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'big',
-      ),
-      'daycount' => array(
-        'description' => 'The total number of times the {node} has been viewed today.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'medium',
-      ),
-      'timestamp' => array(
-        'description' => 'The most recent time the {node} has been viewed.',
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 0,
-      ),
-    ),
-    'primary key' => array('nid'),
-  );
-
-  return $schema;
+  $state->delete('statistics.day_timestamp');
+  $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids');
+  foreach (array_keys($entity_types) as $entity_type_id) {
+    $state->delete("statistics.{$entity_type_id}_counter_scale");
+  }
 }
 
 /**
@@ -86,3 +46,14 @@ function statistics_update_8002() {
 function statistics_update_8300() {
   \Drupal::configFactory()->getEditable('statistics.settings')->clear('access_log')->save();
 }
+
+/**
+ * Replace count_content_views settings with entity_type_ids setting.
+ */
+function statistics_update_8301() {
+  $config = \Drupal::configFactory()->getEditable('statistics.settings');
+  $value = $config->get('count_content_views') ? ['node' => 'node'] : [];
+  $config->clear('count_content_views')->save();
+  // Set the new configuration setting for entity_type_ids to the initial value.
+  $config->set('entity_type_ids', $value)->save();
+}
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index 922ed57..f4399c3 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -19,7 +19,7 @@ function statistics_help($route_name, RouteMatchInterface $route_match) {
     case 'help.page.statistics':
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Statistics module shows you how often content is viewed. This is useful in determining which pages of your site are most popular. For more information, see the <a href=":statistics_do">online documentation for the Statistics module</a>.', array(':statistics_do' => 'https://www.drupal.org/documentation/modules/statistics/')) . '</p>';
+      $output .= '<p>' . t('The Statistics module shows you how often content entity is viewed. This is useful in determining which pages of your site are most popular. For more information, see the <a href=":statistics_do">online documentation for the Statistics module</a>.', array(':statistics_do' => 'https://www.drupal.org/documentation/modules/statistics/')) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<dl>';
       $output .= '<dt>' . t('Displaying popular content') . '</dt>';
@@ -40,7 +40,43 @@ function statistics_help($route_name, RouteMatchInterface $route_match) {
 function statistics_node_view(array &$build, EntityInterface $node, EntityViewDisplayInterface $display, $view_mode) {
   if (!$node->isNew() && $view_mode == 'full' && node_is_page($node) && empty($node->in_preview)) {
     $build['#attached']['library'][] = 'statistics/drupal.statistics';
-    $settings = array('data' => array('nid' => $node->id()), 'url' => Url::fromUri('base:' . drupal_get_path('module', 'statistics') . '/statistics.php')->toString());
+    $settings = [
+      'data' => [
+        'key' => 'nid',
+        'id' => $node->id(),
+        'type' => 'node',
+      ],
+      'url' => Url::fromUri('base:' . drupal_get_path('module', 'statistics') . '/statistics.php')->toString(),
+    ];
+    $build['#attached']['drupalSettings']['statistics'] = $settings;
+  }
+}
+
+/**
+ * Implements hook_ENTITY_view() for node entities.
+ */
+function statistics_entity_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode) {
+  $entity_type_id = $entity->getEntityTypeId();
+  // @todo Remove this condition once statistics_node_view() is removed.
+  if ($entity_type_id == 'node') {
+    return;
+  }
+  $route_match = \Drupal::routeMatch();
+  $route_name = "entity.{$entity_type_id}.canonical";
+  if ($route_match->getRouteName() == $route_name) {
+    $page_entity = $route_match->getParameter($entity_type_id);
+  }
+  $entity_is_page = (!empty($page_entity) ? $page_entity->id() == $entity->id() : FALSE);
+  if (!$entity->isNew() && $view_mode == 'full' && $entity_is_page && empty($entity->in_preview)) {
+    $build['#attached']['library'][] = 'statistics/drupal.statistics';
+    $settings = [
+      'data' => [
+        'key' => $entity->getEntityType()->getKey('id'),
+        'id' => $entity->id(),
+        'type' => $entity_type_id,
+      ],
+      'url' => Url::fromUri('base:' . drupal_get_path('module', 'statistics') . '/statistics.php')->toString(),
+    ];
     $build['#attached']['drupalSettings']['statistics'] = $settings;
   }
 }
@@ -52,7 +88,7 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
   if ($context['view_mode'] != 'rss') {
     $links['#cache']['contexts'][] = 'user.permissions';
     if (\Drupal::currentUser()->hasPermission('view post access counter')) {
-      $statistics = \Drupal::service('statistics.storage.node')->fetchView($entity->id());
+      $statistics = \Drupal::service('statistics.storage')->fetchView($entity->getEntityType(), $entity->id());
       if ($statistics) {
         $statistics_links['statistics_counter']['title'] = \Drupal::translation()->formatPlural($statistics->getTotalCount(), '1 view', '@count views');
         $links['statistics'] = array(
@@ -70,10 +106,14 @@ function statistics_node_links_alter(array &$links, NodeInterface $entity, array
  * Implements hook_cron().
  */
 function statistics_cron() {
-  $storage = \Drupal::service('statistics.storage.node');
-  $storage->resetDayCount();
-  $max_total_count = $storage->maxTotalCount();
-  \Drupal::state()->set('statistics.node_counter_scale', 1.0 / max(1.0, $max_total_count));
+  \Drupal::service('statistics.reset_count')->resetDayCount();
+  $storage = \Drupal::service('statistics.storage');
+  $state = \Drupal::state();
+  $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids');
+  foreach (array_filter($entity_types) as $entity_type_id) {
+    $max_total_count = $storage->maxTotalCount($entity_type_id);
+    $state->set("statistics.{$entity_type_id}_counter_scale", 1.0 / max(1.0, $max_total_count));
+  }
 }
 
 /**
@@ -119,17 +159,20 @@ function statistics_title_list($dbfield, $dbrows) {
  * Retrieves a node's "view statistics".
  *
  * @deprecated in Drupal 8.2.x, will be removed before Drupal 9.0.0.
- *   Use \Drupal::service('statistics.storage.node')->fetchView($id).
+ *   Use \Drupal::service('statistics.storage')->fetchView($entity_type, $id).
  */
 function statistics_get($id) {
   if ($id > 0) {
+    $entity_type = \Drupal::entityTypeManager()->getDefinition('node');
     /** @var \Drupal\statistics\StatisticsViewsResult $statistics */
-    $statistics = \Drupal::service('statistics.storage.node')->fetchView($id);
-    return [
-      'totalcount' => $statistics->getTotalCount(),
-      'daycount' => $statistics->getDayCount(),
-      'timestamp' => $statistics->getTimestamp(),
-    ];
+    if ($statistics = \Drupal::service('statistics.storage')->fetchView($entity_type, $id)) {
+      return [
+        'totalcount' => $statistics->getTotalCount(),
+        'daycount' => $statistics->getDayCount(),
+        'timestamp' => $statistics->getTimestamp(),
+      ];
+    }
+    return [];
   }
 }
 
@@ -139,14 +182,28 @@ function statistics_get($id) {
 function statistics_node_predelete(EntityInterface $node) {
   // Clean up statistics table when node is deleted.
   $id = $node->id();
-  return \Drupal::service('statistics.storage.node')->deleteViews($id);
+  return \Drupal::service('statistics.storage')->deleteViews($node->getEntityType(), $id);
+}
+
+/**
+ * Implements hook_ENTITY_predelete() for node entities.
+ */
+function statistics_entity_predelete(EntityInterface $entity) {
+  // @todo Remove this condition once statistics_node_view() is removed.
+  if ($entity->getEntityTypeId() == 'node') {
+    return;
+  }
+  // Clean up statistics table when entity is deleted.
+  $id = $entity->id();
+  return \Drupal::service('statistics.storage')->deleteViews($entity->getEntityType(), $id);
 }
 
 /**
  * Implements hook_ranking().
  */
 function statistics_ranking() {
-  if (\Drupal::config('statistics.settings')->get('count_content_views')) {
+  $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids');
+  if (in_array('node', $entity_types, TRUE)) {
     return array(
       'views' => array(
         'title' => t('Number of views'),
@@ -185,8 +242,8 @@ function statistics_preprocess_block(&$variables) {
  * to count content views.
  */
 function statistics_block_alter(&$definitions) {
-  $statistics_count_content_views = \Drupal::config('statistics.settings')->get('count_content_views');
-  if (empty($statistics_count_content_views)) {
+  $entity_types = \Drupal::config('statistics.settings')->get('entity_type_ids');
+  if (!in_array('node', $entity_types, TRUE)) {
     unset($definitions['statistics_popular_block']);
   }
 }
diff --git a/core/modules/statistics/statistics.php b/core/modules/statistics/statistics.php
index a43509e..ffacd20 100644
--- a/core/modules/statistics/statistics.php
+++ b/core/modules/statistics/statistics.php
@@ -11,20 +11,32 @@
 chdir('../../..');
 
 $autoloader = require_once 'autoload.php';
-
-$kernel = DrupalKernel::createFromRequest(Request::createFromGlobals(), $autoloader, 'prod');
+$request = Request::createFromGlobals();
+$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
 $kernel->boot();
 $container = $kernel->getContainer();
 
-$views = $container
+$entity_types = $container
   ->get('config.factory')
   ->get('statistics.settings')
-  ->get('count_content_views');
+  ->get('entity_type_ids');
+$key = filter_input(INPUT_POST, 'key', FILTER_CALLBACK, ['options' => 'statistics_validate_machine_name']);
+$id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT);
+$entity_type = filter_input(INPUT_POST, 'type', FILTER_CALLBACK, ['options' => 'statistics_validate_machine_name']);
+if ($key && $id && $entity_type && in_array($entity_type, $entity_types, TRUE)) {
+  $container->get('request_stack')->push($request);
+  $container->get('statistics.storage')->recordView($entity_type, $key ,$id);
+}
 
-if ($views) {
-  $nid = filter_input(INPUT_POST, 'nid', FILTER_VALIDATE_INT);
-  if ($nid) {
-    $container->get('request_stack')->push(Request::createFromGlobals());
-    $container->get('statistics.storage.node')->recordView($nid);
-  }
+/**
+ * Validate entity type machine name.
+ *
+ * @param string $machine_name
+ *   The machine name.
+ *
+ * @return string|null
+ *   The valid machine name or NULL.
+ */
+function statistics_validate_machine_name($machine_name) {
+  return preg_match('@[^a-z0-9_]+@', $machine_name) === 0 ? $machine_name : NULL;
 }
diff --git a/core/modules/statistics/statistics.services.yml b/core/modules/statistics/statistics.services.yml
index cf15573..cda1a87 100644
--- a/core/modules/statistics/statistics.services.yml
+++ b/core/modules/statistics/statistics.services.yml
@@ -1,6 +1,12 @@
 services:
-  statistics.storage.node:
-    class: Drupal\statistics\NodeStatisticsDatabaseStorage
-    arguments: ['@database', '@state', '@request_stack']
+  statistics.storage:
+    class: Drupal\statistics\StatisticsDatabaseStorage
+    arguments: ['@database', '@request_stack']
+    tags:
+      - { name: backend_overridable }
+
+  statistics.reset_count:
+    class: Drupal\statistics\StatisticsResetCount
+    arguments: ['@database', '@config.factory', '@state', '@datetime.time']
     tags:
       - { name: backend_overridable }
diff --git a/core/modules/statistics/statistics.views.inc b/core/modules/statistics/statistics.views.inc
index e851251..f9eac28 100644
--- a/core/modules/statistics/statistics.views.inc
+++ b/core/modules/statistics/statistics.views.inc
@@ -9,68 +9,80 @@
  * Implements hook_views_data().
  */
 function statistics_views_data() {
-  $data['node_counter']['table']['group']  = t('Content statistics');
+  $data = [];
+  $entity_type_manager = \Drupal::entityTypeManager();
+  $entity_types = \Drupal::configFactory()->get('statistics.settings')->get('entity_type_ids');
+  foreach (array_filter($entity_types) as $entity_type_id) {
+    if ($entity_type_manager->hasHandler($entity_type_id, 'views_data')) {
+      $table = $entity_type_id . '_counter';
+      $entity_type = $entity_type_manager->getDefinition($entity_type_id);
+      $base_table = $entity_type_manager->getHandler($entity_type_id, 'views_data')
+        ->getViewsTableForEntityType($entity_type);
+      $data[$table]['table']['group'] = t('@label statistics', ['@label' => $entity_type->getLabel()]);
 
-  $data['node_counter']['table']['join'] = array(
-    'node_field_data' => array(
-      'left_field' => 'nid',
-      'field' => 'nid',
-    ),
-  );
+      $data[$table]['table']['join'] = array(
+        $base_table => array(
+          'left_field' => $entity_type->getKey('id'),
+          'field' => $entity_type->getKey('id'),
+        ),
+      );
 
-  $data['node_counter']['totalcount'] = array(
-    'title' => t('Total views'),
-    'help' => t('The total number of times the node has been viewed.'),
-    'field' => array(
-      'id' => 'statistics_numeric',
-      'click sortable' => TRUE,
-     ),
-    'filter' => array(
-      'id' => 'numeric',
-    ),
-    'argument' => array(
-      'id' => 'numeric',
-    ),
-    'sort' => array(
-      'id' => 'standard',
-    ),
-  );
+      $data[$table]['totalcount'] = array(
+        'title' => t('Total views'),
+        'help' => t('The total number of times the @entity has been viewed.', ['@entity' => $entity_type->id()]),
+        'field' => array(
+          'id' => 'statistics_numeric',
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'id' => 'numeric',
+        ),
+        'argument' => array(
+          'id' => 'numeric',
+        ),
+        'sort' => array(
+          'id' => 'standard',
+        ),
+      );
 
-  $data['node_counter']['daycount'] = array(
-    'title' => t('Views today'),
-    'help' => t('The total number of times the node has been viewed today.'),
-    'field' => array(
-      'id' => 'statistics_numeric',
-      'click sortable' => TRUE,
-     ),
-    'filter' => array(
-      'id' => 'numeric',
-    ),
-    'argument' => array(
-      'id' => 'numeric',
-    ),
-    'sort' => array(
-      'id' => 'standard',
-    ),
-  );
+      $data[$table]['daycount'] = array(
+        'title' => t('Views today'),
+        'help' => t('The total number of times the @entity has been viewed today.', ['@entity' => $entity_type->id()]),
+        'field' => array(
+          'id' => 'statistics_numeric',
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'id' => 'numeric',
+        ),
+        'argument' => array(
+          'id' => 'numeric',
+        ),
+        'sort' => array(
+          'id' => 'standard',
+        ),
+      );
 
-  $data['node_counter']['timestamp'] = array(
-    'title' => t('Most recent view'),
-    'help' => t('The most recent time the node has been viewed.'),
-    'field' => array(
-      'id' => 'node_counter_timestamp',
-      'click sortable' => TRUE,
-    ),
-    'filter' => array(
-      'id' => 'date',
-    ),
-    'argument' => array(
-      'id' => 'date',
-    ),
-    'sort' => array(
-      'id' => 'standard',
-    ),
-  );
+      $data[$table]['timestamp'] = array(
+        'title' => t('Most recent view'),
+        'help' => t('The most recent time the @entity has been viewed.', ['@entity' => $entity_type->id()]),
+        'field' => array(
+          // @todo replace node_counter_timestamp with entity_counter_timestamp.
+          'id' => 'node_counter_timestamp',
+          'click sortable' => TRUE,
+        ),
+        'filter' => array(
+          'id' => 'date',
+        ),
+        'argument' => array(
+          'id' => 'date',
+        ),
+        'sort' => array(
+          'id' => 'standard',
+        ),
+      );
+    }
+  }
 
   return $data;
 }
