diff --git a/core/modules/dblog/dblog.admin.inc b/core/modules/dblog/dblog.admin.inc
index cf744b0..f826ccf 100644
--- a/core/modules/dblog/dblog.admin.inc
+++ b/core/modules/dblog/dblog.admin.inc
@@ -6,71 +6,6 @@
  */
 
 /**
- * Page callback: Shows the most frequent log messages of a given event type.
- *
- * Messages are not truncated on this page because events detailed herein do not
- * have links to a detailed view.
- *
- * @param string $type
- *   Type of database log events to display (e.g., 'search').
- *
- * @return array
- *   A build array in the format expected by drupal_render().
- *
- * @see dblog_menu()
- */
-function dblog_top($type) {
-
-  $header = array(
-    array('data' => t('Count'), 'field' => 'count', 'sort' => 'desc'),
-    array('data' => t('Message'), 'field' => 'message')
-  );
-  $count_query = db_select('watchdog');
-  $count_query->addExpression('COUNT(DISTINCT(message))');
-  $count_query->condition('type', $type);
-
-  $query = db_select('watchdog', 'w')
-    ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
-    ->extend('Drupal\Core\Database\Query\TableSortExtender');
-  $query->addExpression('COUNT(wid)', 'count');
-  $query = $query
-    ->fields('w', array('message', 'variables'))
-    ->condition('w.type', $type)
-    ->groupBy('message')
-    ->groupBy('variables')
-    ->limit(30)
-    ->orderByHeader($header);
-  $query->setCountQuery($count_query);
-  $result = $query->execute();
-
-  $rows = array();
-  foreach ($result as $dblog) {
-    // Check for required properties.
-    if (isset($dblog->message) && isset($dblog->variables)) {
-      // Messages without variables or user specified text.
-      if ($dblog->variables === 'N;') {
-        $message = $dblog->message;
-      }
-      // Message to translate with injected variables.
-      else {
-        $message = t($dblog->message, unserialize($dblog->variables));
-      }
-    }
-    $rows[] = array($dblog->count, $message);
-  }
-
-  $build['dblog_top_table']  = array(
-    '#theme' => 'table',
-    '#header' => $header,
-    '#rows' => $rows,
-    '#empty' => t('No log messages available.'),
-  );
-  $build['dblog_top_pager'] = array('#theme' => 'pager');
-
-  return $build;
-}
-
-/**
  * Page callback: Displays details about a specific database log message.
  *
  * @param int $id
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
index 63e2b8f..aceaa27 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -48,18 +48,12 @@ function dblog_menu() {
   $items['admin/reports/page-not-found'] = array(
     'title' => "Top 'page not found' errors",
     'description' => "View 'page not found' errors (404s).",
-    'page callback' => 'dblog_top',
-    'page arguments' => array('page not found'),
-    'access arguments' => array('access site reports'),
-    'file' => 'dblog.admin.inc',
+    'route_name' => 'dblog_top',
   );
   $items['admin/reports/access-denied'] = array(
     'title' => "Top 'access denied' errors",
     'description' => "View 'access denied' errors (403s).",
-    'page callback' => 'dblog_top',
-    'page arguments' => array('access denied'),
-    'access arguments' => array('access site reports'),
-    'file' => 'dblog.admin.inc',
+    'route_name' => 'dblog_top',
   );
   $items['admin/reports/event/%'] = array(
     'title' => 'Details',
@@ -69,16 +63,12 @@ function dblog_menu() {
     'file' => 'dblog.admin.inc',
   );
 
-  if (module_exists('search')) {
-    $items['admin/reports/search'] = array(
-      'title' => 'Top search phrases',
-      'description' => 'View most popular search phrases.',
-      'page callback' => 'dblog_top',
-      'page arguments' => array('search'),
-      'access arguments' => array('access site reports'),
-      'file' => 'dblog.admin.inc',
-    );
-  }
+  $items['admin/reports/search'] = array(
+    'title' => 'Top search phrases',
+    'description' => 'View most popular search phrases.',
+    'route_name' => 'dblog_top',
+  );
+
 
   return $items;
 }
diff --git a/core/modules/dblog/dblog.routing.yml b/core/modules/dblog/dblog.routing.yml
index 310d798..cdb0888 100644
--- a/core/modules/dblog/dblog.routing.yml
+++ b/core/modules/dblog/dblog.routing.yml
@@ -4,3 +4,11 @@ dblog_overview:
     _content: '\Drupal\dblog\Controller\DbLogController::overview'
   requirements:
     _permission: 'access site reports'
+dblog_top:
+  pattern: '/admin/reports/{type}'
+  defaults:
+    _content: '\Drupal\dblog\Controller\DbLogController::top'
+  requirements:
+    _permission: 'access site reports'
+    # Do not match the dblog route above.
+    type: (access-denied|search|page-not-found).*
diff --git a/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php b/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php
index dcc0d6a..e3363f9 100644
--- a/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php
+++ b/core/modules/dblog/lib/Drupal/dblog/Controller/DbLogController.php
@@ -9,9 +9,10 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Database\Connection;
-use Drupal\Core\ControllerInterface;
+use Drupal\Core\Controller\ControllerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Returns responses for dblog routes.
@@ -225,4 +226,101 @@ protected function buildFilterQuery() {
     );
   }
 
+  /**
+   * Shows the most frequent log messages of a given event type.
+   *
+   * Messages are not truncated on this page because events
+   * detailed herein do not have links to a detailed view.
+   *
+   * Use one of the above *Report() methods.
+   *
+   * @param string $type
+   *   Type of database log events to display (e.g., 'search').
+   *
+   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
+   *
+   * @return array
+   *   A build array in the format expected by drupal_render().
+   *
+   * @see dblog_menu()
+   *
+   * @todo
+   *   Remove drupal_set_title when http://drupal.org/node/1871596 is in.
+   */
+  public function top($type) {
+    // Make sure the search module is enabled.
+    if (strtolower($type) == 'search') {
+      if (!$this->moduleHandler->moduleExists('search')) {
+        // Its not enabled - throw 404.
+        throw new NotFoundHttpException();
+      }
+    }
+
+    // See http://drupal.org/node/1871596.
+    switch ($type) {
+      case 'search':
+        drupal_set_title('Top search phrases');
+        break;
+
+      case 'access-denied':
+        $type = 'access denied';
+        drupal_set_title("Top 'access denied' errors");
+        break;
+
+      case 'page-not-found':
+        $type = 'page not found';
+        drupal_set_title("Top 'page not found' errors");
+        break;
+    }
+
+    $header = array(
+      array('data' => t('Count'), 'field' => 'count', 'sort' => 'desc'),
+      array('data' => t('Message'), 'field' => 'message'),
+    );
+
+    $count_query = $this->database->select('watchdog');
+    $count_query->addExpression('COUNT(DISTINCT(message))');
+    $count_query->condition('type', $type);
+
+    $query = $this->database->select('watchdog', 'w')
+      ->extend('Drupal\Core\Database\Query\PagerSelectExtender')
+      ->extend('Drupal\Core\Database\Query\TableSortExtender');
+    $query->addExpression('COUNT(wid)', 'count');
+    $query = $query
+      ->fields('w', array('message', 'variables'))
+      ->condition('w.type', $type)
+      ->groupBy('message')
+      ->groupBy('variables')
+      ->limit(30)
+      ->orderByHeader($header);
+    $query->setCountQuery($count_query);
+    $result = $query->execute();
+
+    $rows = array();
+    foreach ($result as $dblog) {
+      // Check for required properties.
+      if (isset($dblog->message) && isset($dblog->variables)) {
+        // Messages without variables or user specified text.
+        if ($dblog->variables === 'N;') {
+          $message = $dblog->message;
+        }
+        // Message to translate with injected variables.
+        else {
+          $message = t($dblog->message, unserialize($dblog->variables));
+        }
+      }
+      $rows[] = array($dblog->count, $message);
+    }
+
+    $build['dblog_top_table']  = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => t('No log messages available.'),
+    );
+    $build['dblog_top_pager'] = array('#theme' => 'pager');
+
+    return $build;
+  }
+
 }
