diff --git a/memcache_admin/memcache_admin.services.yml b/memcache_admin/memcache_admin.services.yml
index 41de36ccfaac5c55327a98cc897e26222e28cfaa..c41f4d3dd7c44effa564fba96f5bb0c7c3c5e96a 100644
--- a/memcache_admin/memcache_admin.services.yml
+++ b/memcache_admin/memcache_admin.services.yml
@@ -3,3 +3,11 @@ services:
     class: Drupal\memcache_admin\EventSubscriber\MemcacheAdminSubscriber
     tags:
       - { name: event_subscriber }
+  memcache_stats.memcached:
+    class: Drupal\memcache_admin\EventSubscriber\MemcacheServerStatsSubscriber
+    tags:
+      - { name: event_subscriber }
+  memcache_stats.mcrouter:
+    class: Drupal\memcache_admin\EventSubscriber\McrouterStatsSubscriber
+    tags:
+      - { name: event_subscriber }
\ No newline at end of file
diff --git a/memcache_admin/src/Controller/MemcacheStatisticsController.php b/memcache_admin/src/Controller/MemcacheStatisticsController.php
index 7e119da5f836f1c4c04e98a6a11f1433ac976c89..2be90ba870430911c1d91b097933c7a43bbd6eed 100644
--- a/memcache_admin/src/Controller/MemcacheStatisticsController.php
+++ b/memcache_admin/src/Controller/MemcacheStatisticsController.php
@@ -3,12 +3,11 @@
 namespace Drupal\memcache_admin\Controller;
 
 use Drupal\Core\Controller\ControllerBase;
-use Drupal\Core\Datetime\DateFormatter;
 use Drupal\Core\Link;
 use Drupal\Core\Messenger\MessengerTrait;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Url;
-use Drupal\Component\Render\HtmlEscapedText;
+use Drupal\memcache_admin\Event\MemcacheStatsEvent;
 
 /**
  * Memcache Statistics.
@@ -24,136 +23,35 @@ class MemcacheStatisticsController extends ControllerBase {
    * @param string $bin
    *   The bin name.
    *
-   * @return string
+   * @return array
    *   The page output.
    */
   public function statsTable($bin = 'default') {
-    $output = [];
-    $servers = [];
-
-    // Get the statistics.
-    $bin      = $this->binMapping($bin);
+    $bin = $this->getBinMapping($bin);
     /** @var $memcache \Drupal\memcache\DrupalMemcacheInterface */
     $memcache = \Drupal::service('memcache.factory')->get($bin, TRUE);
-    $stats    = $memcache->stats($bin, 'default', TRUE);
-
-    if (empty($stats[$bin])) {
-      // Failed to load statistics. Provide a useful error about where to get
-      // more information and help.
-      $this->messenger()->addError(
-        $this->t(
-          'There may be a problem with your Memcache configuration. Please review @readme for more information.',
-          [
-            '@readme' => 'README.txt',
-          ]
-        )
-      );
-    }
-    else {
-      if (count($stats[$bin])) {
-        $stats     = $stats[$bin];
-        $aggregate = array_pop($stats);
-
-        if ($memcache->getMemcache() instanceof \Memcached) {
-          $version = $this->t('Memcached v@version', ['@version' => phpversion('Memcached')]);
-        }
-        elseif ($memcache->getMemcache() instanceof \Memcache) {
-          $version = $this->t('Memcache v@version', ['@version' => phpversion('Memcache')]);
-        }
-        else {
-          $version = $this->t('Unknown');
-          $this->messenger()->addError($this->t('Failed to detect the memcache PECL extension.'));
-        }
 
-        foreach ($stats as $server => $statistics) {
-          if (empty($statistics['uptime'])) {
-            $this->messenger()->addError($this->t('Failed to connect to server at :address.', [':address' => $server]));
-          }
-          else {
-            $servers[] = $server;
-
-            $data['server_overview'][$server]    = $this->t('v@version running @uptime', ['@version' => $statistics['version'], '@uptime' => \Drupal::service('date.formatter')->formatInterval($statistics['uptime'])]);
-            $data['server_pecl'][$server]        = $this->t('n/a');
-            $data['server_time'][$server]        = \Drupal::service('date.formatter')->format($statistics['time']);
-            $data['server_connections'][$server] = $this->statsConnections($statistics);
-            $data['cache_sets'][$server]         = $this->statsSets($statistics);
-            $data['cache_gets'][$server]         = $this->statsGets($statistics);
-            $data['cache_counters'][$server]     = $this->statsCounters($statistics);
-            $data['cache_transfer'][$server]     = $this->statsTransfer($statistics);
-            $data['cache_average'][$server]      = $this->statsAverage($statistics);
-            $data['memory_available'][$server]   = $this->statsMemory($statistics);
-            $data['memory_evictions'][$server]   = number_format($statistics['evictions']);
-          }
-        }
-      }
+    // Instantiate our event.
+    $event = new MemcacheStatsEvent($memcache, $bin);
 
-      // Build a custom report array.
-      $report = [
-        'uptime' => [
-          'uptime' => [
-            'label'   => $this->t('Uptime'),
-            'servers' => $data['server_overview'],
-          ],
-          'extension' => [
-            'label'   => $this->t('PECL extension'),
-            'servers' => [$servers[0] => $version],
-          ],
-          'time' => [
-            'label'   => $this->t('Time'),
-            'servers' => $data['server_time'],
-          ],
-          'connections' => [
-            'label'   => $this->t('Connections'),
-            'servers' => $data['server_connections'],
-          ],
-        ],
-        'stats' => [],
-        'memory' => [
-          'memory' => [
-            'label'   => $this->t('Available memory'),
-            'servers' => $data['memory_available'],
-          ],
-          'evictions' => [
-            'label'   => $this->t('Evictions'),
-            'servers' => $data['memory_evictions'],
-          ],
-        ],
-      ];
+    // Get the event_dispatcher service and dispatch the event.
+    $event_dispatcher = \Drupal::service('event_dispatcher');
+    $event_dispatcher->dispatch(MemcacheStatsEvent::BUILD_MEMCACHE_STATS, $event);
 
-      // Don't display aggregate totals if there's only one server.
-      if (count($servers) > 1) {
-        $report['uptime']['uptime']['total']      = $this->t('n/a');
-        $report['uptime']['extension']['servers'] = $data['server_pecl'];
-        $report['uptime']['extension']['total']   = $version;
-        $report['uptime']['time']['total']        = $this->t('n/a');
-        $report['uptime']['connections']['total'] = $this->statsConnections($aggregate);
-        $report['memory']['memory']['total']      = $this->statsMemory($aggregate);
-        $report['memory']['evictions']['total']   = number_format($aggregate['evictions']);
-      }
-
-      // Report on stats.
-      $stats = [
-        'sets'     => $this->t('Sets'),
-        'gets'     => $this->t('Gets'),
-        'counters' => $this->t('Counters'),
-        'transfer' => $this->t('Transferred'),
-        'average'  => $this->t('Per-connection average'),
-      ];
+    // Report the PHP Memcache(d) driver version.
+    if ($memcache->getMemcache() instanceof \Memcached) {
+      $raw_stats['driver_version'] = $this->t('PECL Driver in Use: Memcached v@version', ['@version' => phpversion('Memcached')]);
+    }
+    elseif ($memcache->getMemcache() instanceof \Memcache) {
+      $raw_stats['driver_version'] = $this->t('PECL Driver in Use: Memcache v@version', ['@version' => phpversion('Memcache')]);
+    }
 
-      foreach ($stats as $type => $label) {
-        $report['stats'][$type] = [
-          'label'   => $label,
-          'servers' => $data["cache_{$type}"],
-        ];
+    // Get the event_dispatcher service and dispatch the event.
+    $event_dispatcher = \Drupal::service('event_dispatcher');
+    $event_dispatcher->dispatch(MemcacheStatsEvent::REPORT_MEMCACHE_STATS, $event);
 
-        if (count($servers) > 1) {
-          $func = 'stats' . ucfirst($type);
-          $report['stats'][$type]['total'] = $this->{$func}($aggregate);
-        }
-      }
-
-      $output = $this->statsTablesOutput($bin, $servers, $report);
-    }
+    $output = ['#markup' => '<p>' . $raw_stats['driver_version']];
+    $output[] = $this->statsTablesOutput($bin, $event->getServers(), $event->getReport());
 
     return $output;
   }
@@ -229,17 +127,6 @@ class MemcacheStatisticsController extends ControllerBase {
     }
   }
 
-  /**
-   * Helper function. Returns the bin name.
-   */
-  private function defaultBin($bin) {
-    if ($bin == 'default') {
-      return 'cache';
-    }
-
-    return $bin;
-  }
-
   /**
    * Statistics report: format total and open connections.
    */
@@ -253,63 +140,6 @@ class MemcacheStatisticsController extends ControllerBase {
     );
   }
 
-  /**
-   * Statistics report: calculate # of set cmds and total cmds.
-   */
-  private function statsSets($stats) {
-    if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
-      $sets = 0;
-    }
-    else {
-      $sets = $stats['cmd_set'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
-    }
-    if (empty($stats['uptime'])) {
-      $average = 0;
-    }
-    else {
-      $average = $sets / $stats['uptime'];
-    }
-    return $this->t(
-      '@average/s; @set sets (@sets%) of @total commands',
-      [
-        '@average' => number_format($average, 2),
-        '@sets'    => number_format($sets, 2),
-        '@set'     => number_format($stats['cmd_set']),
-        '@total'   => number_format($stats['cmd_set'] + $stats['cmd_get']),
-      ]
-    );
-  }
-
-  /**
-   * Statistics report: calculate # of get cmds, broken down by hits and misses.
-   */
-  private function statsGets($stats) {
-    if (($stats['cmd_set'] + $stats['cmd_get']) == 0) {
-      $gets = 0;
-    }
-    else {
-      $gets = $stats['cmd_get'] / ($stats['cmd_set'] + $stats['cmd_get']) * 100;
-    }
-    if (empty($stats['uptime'])) {
-      $average = 0;
-    }
-    else {
-      $average = $stats['cmd_get'] / $stats['uptime'];
-    }
-    return $this->t(
-      '@average/s; @total gets (@gets%); @hit hits (@percent_hit%) @miss misses (@percent_miss%)',
-      [
-        '@average'      => number_format($average, 2),
-        '@gets'         => number_format($gets, 2),
-        '@hit'          => number_format($stats['get_hits']),
-        '@percent_hit'  => ($stats['cmd_get'] > 0 ? number_format($stats['get_hits'] / $stats['cmd_get'] * 100, 2) : '0.00'),
-        '@miss'         => number_format($stats['get_misses']),
-        '@percent_miss' => ($stats['cmd_get'] > 0 ? number_format($stats['get_misses'] / $stats['cmd_get'] * 100, 2) : '0.00'),
-        '@total'        => number_format($stats['cmd_get']),
-      ]
-    );
-  }
-
   /**
    * Statistics report: calculate # of increments and decrements.
    */
@@ -334,73 +164,6 @@ class MemcacheStatisticsController extends ControllerBase {
     );
   }
 
-  /**
-   * Statistics report: calculate bytes transferred.
-   */
-  private function statsTransfer($stats) {
-    if ($stats['bytes_written'] == 0) {
-      $written = 0;
-    }
-    else {
-      $written = $stats['bytes_read'] / $stats['bytes_written'] * 100;
-    }
-    return $this->t(
-      '@to:@from (@written% to cache)',
-      [
-        '@to'      => format_size((int) $stats['bytes_read']),
-        '@from'    => format_size((int) $stats['bytes_written']),
-        '@written' => number_format($written, 2),
-      ]
-    );
-  }
-
-  /**
-   * Statistics report: calculate per-connection averages.
-   */
-  private function statsAverage($stats) {
-    if ($stats['total_connections'] == 0) {
-      $get   = 0;
-      $set   = 0;
-      $read  = 0;
-      $write = 0;
-    }
-    else {
-      $get   = $stats['cmd_get'] / $stats['total_connections'];
-      $set   = $stats['cmd_set'] / $stats['total_connections'];
-      $read  = $stats['bytes_written'] / $stats['total_connections'];
-      $write = $stats['bytes_read'] / $stats['total_connections'];
-    }
-    return $this->t(
-      '@read in @get gets; @write in @set sets',
-      [
-        '@get'   => number_format($get, 2),
-        '@set'   => number_format($set, 2),
-        '@read'  => format_size(number_format($read, 2)),
-        '@write' => format_size(number_format($write, 2)),
-      ]
-    );
-  }
-
-  /**
-   * Statistics report: calculate available memory.
-   */
-  private function statsMemory($stats) {
-    if ($stats['limit_maxbytes'] == 0) {
-      $percent = 0;
-    }
-    else {
-      $percent = 100 - $stats['bytes'] / $stats['limit_maxbytes'] * 100;
-    }
-    return $this->t(
-      '@available (@percent%) of @total',
-      [
-        '@available' => format_size($stats['limit_maxbytes'] - $stats['bytes']),
-        '@percent'   => number_format($percent, 2),
-        '@total'     => format_size($stats['limit_maxbytes']),
-      ]
-    );
-  }
-
   /**
    * Generates render array for output.
    */
@@ -409,6 +172,9 @@ class MemcacheStatisticsController extends ControllerBase {
     $memcache_bins = $memcache->getBins();
 
     $links = [];
+    if (!is_array($servers)) {
+      return;
+    }
     foreach ($servers as $server) {
 
       // Convert socket file path so it works with an argument, this should
@@ -546,4 +312,31 @@ class MemcacheStatisticsController extends ControllerBase {
     return $build;
   }
 
+  /**
+   * Helper function, reverse map the memcache_bins variable.
+   */
+  protected function getBinMapping($bin = 'cache') {
+    $memcache      = \Drupal::service('memcache.factory')->get(NULL, TRUE);
+    $memcache_bins = $memcache->getBins();
+
+    $bins = array_flip($memcache_bins);
+    if (isset($bins[$bin])) {
+      return $bins[$bin];
+    }
+    else {
+      return $this->defaultBin($bin);
+    }
+  }
+
+  /**
+   * Helper function. Returns the bin name.
+   */
+  protected function defaultBin($bin) {
+    if ($bin == 'default') {
+      return 'cache';
+    }
+
+    return $bin;
+  }
+
 }
diff --git a/memcache_admin/src/Event/MemcacheStatsEvent.php b/memcache_admin/src/Event/MemcacheStatsEvent.php
new file mode 100644
index 0000000000000000000000000000000000000000..4b8fc07c59b9c34d995a30dda98e03ab2cea501a
--- /dev/null
+++ b/memcache_admin/src/Event/MemcacheStatsEvent.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\memcache_admin\Event;
+
+use Drupal\memcache\DrupalMemcacheInterface;
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Memcache Stats Event
+ *
+ * The memcache stats event stores all the attributes generated by the different
+ * types of memcache servers. Currently memcache_admin supports memcache and
+ * mcrouter.
+ */
+class MemcacheStatsEvent extends Event {
+
+  /**
+   * Event used to build the memcache stats array.
+   *
+   * When the stats array is created, this event allows modules to inject extra
+   * data to be contained within the array.
+   */
+  const BUILD_MEMCACHE_STATS = 'memcache_build_memcache_stats';
+
+  /**
+   * Event used to report out the memcache stats array.
+   *
+   * When the stats array is created, this event allows modules to inject extra
+   * data to be contained within the array.
+   */
+  const REPORT_MEMCACHE_STATS = 'memcache_report_memcache_stats';
+
+  /**
+   * The Stats Array for which to create attributes.
+   *
+   * @var array
+   */
+  protected $rawStats;
+
+  /**
+   * The Stats Array for which to create attributes.
+   *
+   * @var array
+   */
+  protected $formattedStats = [];
+
+  /**
+   * The Stats Array for which to create attributes.
+   *
+   * @var array
+   */
+  protected $totals;
+
+  /**
+   * The Stats Array for which to create attributes.
+   *
+   * @var array
+   */
+  protected $report;
+
+  /**
+   * The Stats Array for which to create attributes.
+   *
+   * @var array
+   */
+  protected $servers;
+
+  /**
+   * Cache Bin To Retrieve
+   * @var string $bin
+   */
+  protected $bin;
+
+  protected $memcache;
+
+  /**
+   * MemcacheStatsEvent constructor.
+   *
+   * @param array $raw_stats
+   *   The Stats Data.
+   * @param string $bin
+   *   The cache bin.
+   *
+   */
+  public function __construct(DrupalMemcacheInterface $memcache, string $bin = 'default') {
+    $this->memcache = $memcache;
+    $this->rawStats = $memcache->stats($bin, 'default', TRUE);
+    $this->formattedStats = [];
+    $this->bin = $bin;
+  }
+
+  /**
+   * Get the Stats Array being created.
+   *
+   * @return array
+   *   The Stats Object.
+   */
+  public function getRawStats() {
+    return $this->rawStats;
+  }
+
+  /**
+   * Gets the stats formatted from a MemcacheStatsInterface.
+   * @param $server_type
+   *
+   * @return array|mixed
+   */
+  public function getFormattedStats($server_type) {
+    if (isset($this->formattedStats[$server_type])) {
+      return $this->formattedStats[$server_type];
+    }
+    return [];
+  }
+
+  /**
+   * Returns the memcache connection.
+   */
+  public function getMemcache() {
+    return $this->memcache;
+  }
+
+  /**
+   * Returns the cache bin from this event.
+   */
+  public function getCacheBin() {
+    return $this->bin;
+  }
+
+  /**
+   * Sets the formatted stats array with relevant data.
+   *
+   * @param string $format
+   * @param string $bin
+   * @param string $server
+   * @param \Drupal\memcache_admin\Stats\MemcacheStatsInterface $data
+   */
+  public function updateFormattedStats($format, $bin, $server, $data) {
+    $this->formattedStats[$format][$bin][$server] = $data;
+  }
+
+  /**
+   * Update the total column when multiple memcache servers exist.
+   * @param $total
+   */
+  public function updateTotals($total) {
+    $this->totals = $total;
+  }
+
+  /**
+   * Return the total values from all memcache servers.
+   * @return array
+   */
+  public function getTotals() {
+    return $this->totals;
+  }
+
+  /**
+   * Add a new server to the servers array.
+   * @param string $server
+   */
+  public function updateServers($server) {
+    $this->servers[] = $server;
+  }
+
+  /**
+   * Retrieve all servers from the servers array.
+   *
+   * @return array
+   */
+  public function getServers() {
+    return $this->servers;
+  }
+
+  /**
+   * Update the full report from the event.
+   * @param $report
+   */
+  public function updateReport($report) {
+    $this->report = $report;
+  }
+
+  /**
+   * Returns the stats report.
+   * @return array
+   */
+  public function getReport() {
+    return $this->report;
+  }
+
+}
diff --git a/memcache_admin/src/EventSubscriber/McrouterStatsSubscriber.php b/memcache_admin/src/EventSubscriber/McrouterStatsSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..6432ba94b65ee32b8bc53becffa71bf866d65bb1
--- /dev/null
+++ b/memcache_admin/src/EventSubscriber/McrouterStatsSubscriber.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Drupal\memcache_admin\EventSubscriber;
+
+use Drupal\Core\Messenger\MessengerTrait;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\memcache_admin\Event\MemcacheStatsEvent;
+use Drupal\memcache_admin\Stats\McrouterStatsObject;
+use Drupal\memcache_admin\Stats\MemcacheStatsObject;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Adds memcache server specific details to the stats array.
+ */
+class McrouterStatsSubscriber implements EventSubscriberInterface {
+
+  use StringTranslationTrait;
+  use MessengerTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[MemcacheStatsEvent::BUILD_MEMCACHE_STATS][] = [
+      'onPopulateStats',
+      100
+    ];
+    return $events;
+  }
+
+  /**
+   * Adds stats to the memcache event.
+   *
+   * @param \Drupal\memcache_admin\Event\MemcacheStatsEvent $event
+   *   The event being dispatched.
+   *
+   * @throws \Exception
+   */
+  public function onPopulateStats(MemcacheStatsEvent $event) {
+    $memcache = $event->getMemcache();
+    if (!$memcache->getMemcache()->get('__mcrouter__.version')) {
+      return;
+    }
+
+    $raw_stats = $event->getRawStats();
+    $bin = $event->getCacheBin();
+
+    // No cache bin data, return.
+    if (!isset($raw_stats[$bin])) {
+      return;
+    }
+    // No servers found, return.
+    if (!is_array($raw_stats[$bin])) {
+      return;
+    }
+    $servers = array_keys($raw_stats[$bin]);
+    foreach ($servers as $server) {
+      if ($server =='total') {
+        continue;
+      }
+      // McRouter reports num_servers use that for detecting stats.
+      if (isset($raw_stats[$bin][$server]['num_servers'])) {
+        $event->updateFormattedStats('memcache', $bin, $server, new McrouterStatsObject($raw_stats[$bin][$server]));
+        $event->updateServers($server);
+      }
+    }
+  }
+}
diff --git a/memcache_admin/src/EventSubscriber/MemcacheAdminSubscriber.php b/memcache_admin/src/EventSubscriber/MemcacheAdminSubscriber.php
index e0a967627203a3c3357228d17964b1259b21342e..03ae73981360e2acb9715dc073fd5cd4f1e95697 100644
--- a/memcache_admin/src/EventSubscriber/MemcacheAdminSubscriber.php
+++ b/memcache_admin/src/EventSubscriber/MemcacheAdminSubscriber.php
@@ -32,16 +32,6 @@ class MemcacheAdminSubscriber implements EventSubscriberInterface {
   public function displayStatistics(FilterResponseEvent $event) {
     $user = \Drupal::currentUser();
 
-    // Removed exclusion criteria, untested. Will likely need to add some of
-    // these back in.
-    // @codingStandardsIgnoreStart
-    //   strstr($_SERVER['PHP_SELF'], '/update.php')
-    //   substr($_GET['q'], 0, strlen('batch')) == 'batch'
-    //   strstr($_GET['q'], 'autocomplete')
-    //   substr($_GET['q'], 0, strlen('system/files')) == 'system/files'
-    //   in_array($_GET['q'], ['upload/js', 'admin/content/node-settings/rebuild'])
-    // @codingStandardsIgnoreEnd
-    // @todo validate these checks
     if ($user->id() == 0) {
       // Suppress for the above criteria.
     }
diff --git a/memcache_admin/src/EventSubscriber/MemcacheServerStatsSubscriber.php b/memcache_admin/src/EventSubscriber/MemcacheServerStatsSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..758221bd49c43c0e4be225dd2b08250f1c6fe9f8
--- /dev/null
+++ b/memcache_admin/src/EventSubscriber/MemcacheServerStatsSubscriber.php
@@ -0,0 +1,190 @@
+<?php
+
+namespace Drupal\memcache_admin\EventSubscriber;
+
+use Drupal\Core\Messenger\MessengerTrait;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\memcache_admin\Event\MemcacheStatsEvent;
+use Drupal\memcache_admin\Stats\MemcacheStatsObject;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Adds memcache server specific details to the stats array.
+ */
+class MemcacheServerStatsSubscriber implements EventSubscriberInterface {
+
+  use StringTranslationTrait;
+  use MessengerTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[MemcacheStatsEvent::BUILD_MEMCACHE_STATS][] = [
+      'onPopulateStats',
+      100
+    ];
+    $events[MemcacheStatsEvent::REPORT_MEMCACHE_STATS][] = [
+      'onReportStats',
+      100
+    ];
+    return $events;
+  }
+
+  /**
+   * Populates the Memcache Server Stats
+   *
+   * @param \Drupal\memcache_admin\Event\MemcacheStatsEvent $event
+   *   The event being dispatched.
+   *
+   * @throws \Exception
+   */
+  public function onPopulateStats(MemcacheStatsEvent $event) {
+    $raw_stats = $event->getRawStats();
+    $bin = $event->getCacheBin();
+
+    // No cache bin data, return.
+    if (!isset($raw_stats[$bin])) {
+      return;
+    }
+    // No servers found, return.
+    if (!is_array($raw_stats[$bin])) {
+      return;
+    }
+    $servers = array_keys($raw_stats[$bin]);
+    $memcache_servers = [];
+    foreach ($servers as $server) {
+      // Memcache servers report libevent version, use that for detecting stats.
+      if (isset($raw_stats[$bin][$server]['libevent'])) {
+        $event->updateFormattedStats('memcache', $bin, $server, new MemcacheStatsObject($raw_stats[$bin][$server]));
+        $event->updateServers($server);
+      }
+    }
+    if (isset($raw_stats[$bin]['total'])) {
+      $event->updateTotals([$bin => new MemcacheStatsObject($raw_stats[$bin]['total'])]);
+    }
+  }
+
+  /**
+   * Populates the reporting of a stored set of stats.
+   *
+   * @param \Drupal\memcache_admin\Event\MemcacheStatsEvent $event
+   */
+  public function onReportStats(MemcacheStatsEvent $event) {
+    $stats = $event->getFormattedStats('memcache');
+    $bin = $event->getCacheBin();
+
+    // No cache bin data, return.
+    if (empty($stats[$bin])) {
+      // Failed to load statistics. Provide a useful error about where to get
+      // more information and help.
+      $this->messenger()->addError(
+        $this->t(
+          'There may be a problem with your Memcache configuration. Please review @readme for more information.',
+          [
+            '@readme' => 'README.txt',
+          ]
+        )
+      );
+      return;
+    }
+    // No servers found, return.
+    if (!is_array($stats[$bin])) {
+      return;
+    }
+
+    /**
+     * @var string $server
+     * @var MemcacheStatsObject $statistics
+     */
+    foreach ($stats[$bin] as $server => $statistics) {
+      if (empty($statistics->getUptime())) {
+        $this->messenger()
+          ->addError($this->t('Failed to connect to server at :address.', [':address' => $server]));
+      }
+      else {
+        $data['server_overview'][$server] = $this->t('v@version running @uptime', [
+          '@version' => $statistics->getVersion(),
+          '@uptime' => $statistics->getUptime()
+        ]);
+        $data['server_time'][$server] = $statistics->getServerTime();
+        $data['server_connections'][$server] = $statistics->getConnections();
+        $data['cache_sets'][$server] = $statistics->getSets();
+        $data['cache_gets'][$server] = $statistics->getGets();
+        $data['cache_counters'][$server] = $statistics->getCounters();
+        $data['cache_transfer'][$server] = $statistics->getTransferred();
+        $data['cache_average'][$server] = $statistics->getConnectionAvg();
+        $data['memory_available'][$server] = $statistics->getMemory();
+        $data['memory_evictions'][$server] = $statistics->getEvictions();
+      }
+    }
+
+    // Build a custom report array.
+    $report = [
+      'uptime' => [
+        'uptime' => [
+          'label' => $this->t('Uptime'),
+          'servers' => $data['server_overview'],
+        ],
+        'time' => [
+          'label' => $this->t('Time'),
+          'servers' => $data['server_time'],
+        ],
+        'connections' => [
+          'label' => $this->t('Connections'),
+          'servers' => $data['server_connections'],
+        ],
+      ],
+      'stats' => [
+        'sets' => [
+          'label' => $this->t('Sets'),
+          'servers' => $data["cache_sets"],
+        ],
+        'gets' => [
+          'label' => $this->t('Gets'),
+          'servers' => $data["cache_gets"],
+        ],
+        'counters' => [
+          'label' => $this->t('Counters'),
+          'servers' => $data["cache_counters"],
+        ],
+        'transfer' => [
+          'label' => $this->t('Transferred'),
+          'servers' => $data["cache_transfer"],
+        ],
+        'average' => [
+          'label' => $this->t('Per-connection average'),
+          'servers' => $data["cache_average"],
+        ],
+      ],
+      'memory' => [
+        'memory' => [
+          'label' => $this->t('Available memory'),
+          'servers' => $data['memory_available'],
+        ],
+        'evictions' => [
+          'label' => $this->t('Evictions'),
+          'servers' => $data['memory_evictions'],
+        ],
+      ],
+    ];
+
+    // Don't display aggregate totals if there's only one server.
+    if (count($stats[$bin]) > 1) {
+      /** @var MemcacheStatsObject $totals */
+      $totals = $event->getTotals();
+      $report['uptime']['uptime']['total'] = $this->t('n/a');
+      $report['uptime']['time']['total'] = $this->t('n/a');
+      $report['uptime']['connections']['total'] = $totals[$bin]->getConnections();
+      $report['stats']['sets']['total'] = $totals[$bin]->getSets();
+      $report['stats']['gets']['total'] = $totals[$bin]->getGets();
+      $report['stats']['counters']['total'] = $totals[$bin]->getCounters();
+      $report['stats']['transfer']['total'] = $totals[$bin]->getTransferred();
+      $report['stats']['average']['total'] = $totals[$bin]->getConnectionAvg();
+      $report['memory']['memory']['total'] = $totals[$bin]->getMemory();
+      $report['memory']['evictions']['total'] = $totals[$bin]->getEvictions();
+    }
+
+    $event->updateReport($report);
+  }
+}
diff --git a/memcache_admin/src/Stats/McrouterStatsObject.php b/memcache_admin/src/Stats/McrouterStatsObject.php
new file mode 100644
index 0000000000000000000000000000000000000000..4651bdf23578786294c791f667edfeb08be29f40
--- /dev/null
+++ b/memcache_admin/src/Stats/McrouterStatsObject.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Drupal\memcache_admin\Stats;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Class MemcacheStats.
+ *
+ * @package Drupal\memcache_admin\Stats
+ */
+class McrouterStatsObject extends MemcacheStatsObject implements MemcacheStatsInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * @var array $stats
+   */
+  protected $stats;
+
+  public function __construct(array $raw_stats) {
+    $this->stats = $raw_stats;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getExtension(): string {
+    return isset($this->stats['version']) ?? self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getServerTime(): string {
+    return isset($this->stats['time']) ? \Drupal::service('date.formatter')->format($this->stats['time']) : self::NA;
+  }
+
+  /**
+   * Statistics report: format total and open connections.
+   */
+  public function getConnections() {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getCurrentConnections(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getTotalConnections(): string {
+    return self::NA;
+  }
+
+  /**
+   * Statistics report: calculate # of get cmds, broken down by hits and misses.
+   */
+  public function getGets(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getCounters(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getTransferred(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getConnectionAvg(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getMemory(): string {
+    return self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getEvictions(): string {
+    return isset($this->stats['evictions']) ? number_format($this->stats['evictions']) : self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function setRaw(array $raw_data) {
+    $this->stats = $raw_data;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getRaw(): array {
+    return $this->stats;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getVersion(): string {
+    return isset($this->stats['version']) ? (string)$this->stats['version'] : self::NA;
+  }
+
+}
\ No newline at end of file
diff --git a/memcache_admin/src/Stats/MemcacheStatsInterface.php b/memcache_admin/src/Stats/MemcacheStatsInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..7659558974552d2563e4ed0928deb2620fda9717
--- /dev/null
+++ b/memcache_admin/src/Stats/MemcacheStatsInterface.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Drupal\memcache_admin\Stats;
+
+/**
+ * Defines the Memcache connection interface.
+ */
+interface MemcacheStatsInterface {
+
+  /**
+   * Sets an array of raw data for the memcache server.
+   *
+   * @param array $raw_data
+   *
+   * @return void
+   */
+  public function setRaw(array $raw_data);
+
+  /**
+   * Returns raw data from the memcache server.
+   *
+   * @return array
+   */
+  public function getRaw(): array;
+
+  /**
+   * Returns the memcache server version.
+   *
+   * @return string
+   */
+  public function getVersion(): string;
+
+  /**
+   * Returns the uptime for the memcache server.
+   *
+   * @return string
+   */
+  public function getUptime(): string;
+
+  /**
+   * Returns the PECL extension for the memcache server.
+   *
+   * @return string
+   */
+  public function getExtension(): string;
+
+  /**
+   * Returns the total connections for the memcache server.
+   *
+   * @return string
+   */
+  public function getTotalConnections(): string;
+  
+  /**
+   * Returns the cache sets for the memcache server.
+   *
+   * @return string
+   */
+  public function getSets(): string;
+
+  /**
+   * Returns the cache gets for the memcache server.
+   *
+   * @return string
+   */
+  public function getGets(): string;
+
+  /**
+   * Returns the counters for the memcache server.
+   *
+   * @return string
+   */
+  public function getCounters(): string;
+
+  /**
+   * Returns the data transferred for the memcache server.
+   *
+   * @return string
+   */
+  public function getTransferred(): string;
+
+  /**
+   * Returns the connection averages for the memcache server.
+   *
+   * @return string
+   */
+  public function getConnectionAvg(): string;
+
+  /**
+   * Returns the memory available for the memcache server.
+   *
+   * @return string
+   */
+  public function getMemory(): string;
+
+  /**
+   * Returns the evictions for the memcache server.
+   *
+   * @return string
+   */
+  public function getEvictions(): string;
+  
+}
\ No newline at end of file
diff --git a/memcache_admin/src/Stats/MemcacheStatsObject.php b/memcache_admin/src/Stats/MemcacheStatsObject.php
new file mode 100644
index 0000000000000000000000000000000000000000..df3a4119c8ad492488267b64cbdd36aa2e61c531
--- /dev/null
+++ b/memcache_admin/src/Stats/MemcacheStatsObject.php
@@ -0,0 +1,285 @@
+<?php
+
+namespace Drupal\memcache_admin\Stats;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Class MemcacheStats.
+ *
+ * @package Drupal\memcache_admin\Stats
+ */
+class MemcacheStatsObject implements MemcacheStatsInterface {
+
+  use StringTranslationTrait;
+
+  /**
+   * Stat Not Available
+   */
+  CONST NA = 'n/a';
+
+  /**
+   * @var array $stats
+   */
+  protected $stats;
+
+  public function __construct(array $raw_stats) {
+    $this->stats = $raw_stats;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getUptime(): string {
+    return isset($this->stats['uptime']) ? \Drupal::service('date.formatter')->formatInterval($this->stats['uptime']) : self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getExtension(): string {
+    return isset($this->stats['extension']) ?? self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getServerTime(): string {
+    return isset($this->stats['time']) ? \Drupal::service('date.formatter')->format($this->stats['time']) : self::NA;
+  }
+
+  /**
+   * Statistics report: format total and open connections.
+   */
+  public function getConnections() {
+    if (!isset($this->stats['curr_connections']) ||
+      !isset($this->stats['total_connections'])
+    ) {
+      return self::NA;
+    }
+
+    return $this->t(
+      '@current open of @total total',
+      [
+        '@current' => number_format($this->stats['curr_connections']),
+        '@total'   => number_format($this->stats['total_connections']),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getCurrentConnections(): string {
+    return isset($this->stats['curr_connections']) ?? '0';
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getTotalConnections(): string {
+    return isset($this->stats['total_connections']) ?? '0';
+  }
+
+  /**
+   * Statistics report: calculate # of set cmds and total cmds.
+   */
+  public function getSets(): string {
+    if (!isset($this->stats['cmd_set'])) {
+      return self::NA;
+    }
+
+    if (($this->stats['cmd_set'] + $this->stats['cmd_get']) == 0) {
+      $sets = 0;
+    }
+    else {
+      $sets = $this->stats['cmd_set'] / ($this->stats['cmd_set'] + $this->stats['cmd_get']) * 100;
+    }
+    if (!isset($this->stats['uptime'])) {
+      $average = 0;
+    }
+    else {
+      $average = $sets / $this->stats['uptime'];
+    }
+    return $this->t(
+      '@average/s; @set sets (@sets%) of @total commands',
+      [
+        '@average' => number_format($average, 2),
+        '@sets'    => number_format($sets, 2),
+        '@set'     => number_format($this->stats['cmd_set']),
+        '@total'   => number_format($this->stats['cmd_set'] + $this->stats['cmd_get']),
+      ]
+    );
+  }
+
+  /**
+   * Statistics report: calculate # of get cmds, broken down by hits and misses.
+   */
+  public function getGets(): string {
+    if (!isset($this->stats['cmd_set']) || !isset($this->stats['cmd_get'])) {
+      return self::NA;
+    }
+    else {
+      $get = $this->stats['cmd_get'];
+      $set = $this->stats['cmd_set'];
+      $hits = isset($this->stats['get_hits']) ?? 0;
+      $misses = isset($this->stats['get_misses']) ?? 0;
+    }
+
+    if (($set + $get) == 0) {
+      $gets = 0;
+    }
+    else {
+      $gets = $get / ($set + $get) * 100;
+    }
+    if (empty($stats['uptime'])) {
+      $average = 0;
+    }
+    else {
+      $average = $get / $stats['uptime'];
+    }
+    return $this->t(
+      '@average/s; @total gets (@gets%); @hit hits (@percent_hit%) @miss misses (@percent_miss%)',
+      [
+        '@average'      => number_format($average, 2),
+        '@gets'         => number_format($gets, 2),
+        '@hit'          => number_format($hits),
+        '@percent_hit'  => ($get > 0 ? number_format($hits / $get * 100, 2) : '0.00'),
+        '@miss'         => number_format($misses),
+        '@percent_miss' => ($get > 0 ? number_format($misses / $get * 100, 2) : '0.00'),
+        '@total'        => number_format($get),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getCounters(): string {
+    $incr_hits = isset($this->stats['incr_hits']) ?? 0;
+    $incr_misses = isset($this->stats['incr_misses']) ?? 0;
+    $decr_hits  = isset($this->stats['decr_hits']) ?? 0;
+    $decr_misses = isset($this->stats['decr_misses']) ?? 0;
+
+    return $this->t(
+      '@incr increments, @decr decrements',
+      [
+        '@incr' => number_format($incr_hits + $incr_misses),
+        '@decr' => number_format($decr_hits + $decr_misses),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getTransferred(): string {
+    $read = isset($this->stats['bytes_read']) ?? 0;
+    $write = isset($this->stats['bytes_written']) ?? 0;
+
+    if ($write == 0) {
+      $written = 0;
+    }
+    else {
+      $written = $read / $write * 100;
+    }
+    return $this->t(
+      '@to:@from (@written% to cache)',
+      [
+        '@to'      => format_size((int) $read),
+        '@from'    => format_size((int) $write),
+        '@written' => number_format($written, 2),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getConnectionAvg(): string {
+    if (!isset($this->stats['total_connections']) ||
+        !isset($this->stats['cmd_get']) ||
+        !isset($this->stats['cmd_set']) ||
+        !isset($this->stats['bytes_written']) ||
+        !isset($this->stats['bytes_read'])
+    ) {
+      return self::NA;
+    }
+    if ($this->stats['total_connections'] == 0) {
+      $get   = 0;
+      $set   = 0;
+      $read  = 0;
+      $write = 0;
+    }
+    else {
+      $get   = $this->stats['cmd_get'] / $this->stats['total_connections'];
+      $set   = $this->stats['cmd_set'] / $this->stats['total_connections'];
+      $read  = $this->stats['bytes_written'] / $this->stats['total_connections'];
+      $write = $this->stats['bytes_read'] / $this->stats['total_connections'];
+    }
+    return $this->t(
+      '@read in @get gets; @write in @set sets',
+      [
+        '@get'   => number_format($get, 2),
+        '@set'   => number_format($set, 2),
+        '@read'  => format_size(number_format($read, 2)),
+        '@write' => format_size(number_format($write, 2)),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getMemory(): string {
+    if (!isset($this->stats['limit_maxbytes']) ||
+        !isset($this->stats['bytes'])
+    ) {
+      return self::NA;
+    }
+
+    if ($this->stats['limit_maxbytes'] == 0) {
+      $percent = 0;
+    }
+    else {
+      $percent = 100 - $this->stats['bytes'] / $this->stats['limit_maxbytes'] * 100;
+    }
+    return $this->t(
+      '@available (@percent%) of @total',
+      [
+        '@available' => format_size($this->stats['limit_maxbytes'] - $this->stats['bytes']),
+        '@percent'   => number_format($percent, 2),
+        '@total'     => format_size($this->stats['limit_maxbytes']),
+      ]
+    );
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getEvictions(): string {
+    return isset($this->stats['evictions']) ? number_format($this->stats['evictions']) : self::NA;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function setRaw(array $raw_data) {
+    $this->stats = $raw_data;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getRaw(): array {
+    return $this->stats;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function getVersion(): string {
+    return isset($this->stats['version']) ? (string)$this->stats['version'] : self::NA;
+  }
+
+}
\ No newline at end of file