Skip to content
memcache_admin.module 7.83 KiB
Newer Older
<?php
// $Id$

/**
 * For the collection of memcache stats. This small .js file makes sure that the
 * HTML displaying the stats is inside of the <body> part of the HTML
 * document.
 */
function memcache_admin_init() {
  global $user;
  if (($user->uid == 0) || strstr($_SERVER['PHP_SELF'], 'update.php') || strstr($_GET['q'], 'autocomplete')) {
    // update.php relies on standard error handler
  }
  else {
    if ($user->uid) {
      drupal_add_js(drupal_get_path('module', 'memcache_admin'). '/memcache.js');
    }
    register_shutdown_function('memcache_admin_shutdown');
  }
}

/**
 * Implementation of hook_perm().
 */
function memcache_admin_perm() {
  return array(
    'access memcache statistics' => array(
      'title' => t('Access memcache statistics'),
    ),
  );
}

/**
 * Implementation of hook_menu().
 */
function memcache_admin_menu() {
  $items['admin/config/system/memcache'] = array(
    'title' => 'Memcache',
    'description' => 'Show or hide memcache statistics at the bottom of each page.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('memcache_admin_admin_settings'),
    'access arguments' => array('administer site configuration'),
  );
  $items['admin/reports/memcache'] = array(
    'title' => 'Memcache status',
    'description' => "View the statistics for this site's memcache.",
    'page callback' => 'memcache_admin_stats',
    'access arguments' => array('access memcache statistics'),
    'weight' => 1,
  );
  $memache_servers = variable_get('memcache_servers', array());
  $clusters = array();
  foreach($memache_servers as $server => $cluster) {
    $clusters[$cluster]['servers'][] = $server;
    $clusters[$cluster]['bin'] = _memcache_admin_get_bin_for_cluster($cluster);
  }

  $count = 0;
  foreach ($clusters as $cluster => $cluster_info) {
    if ($cluster_info['bin']) {
      if (empty($current_cluster)) {
        $current_cluster = arg(3);
        if (empty($current_cluster)) {
          $current_cluster = $cluster;
        }
      }

      $items['admin/reports/memcache/'. $cluster] = array(
        'title' => $cluster,
        'type' =>  $count == 0 ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
        'page callback' => 'memcache_admin_stats',
        'page arguments' => array($cluster),
        'access arguments' => array('access memcache statistics'),
        'weight' => $count,
      );
      $count++;

      $sub_count = 0;
      foreach (array('default', 'reset', 'malloc', 'maps', 'slabs', 'items', 'sizes') as $type) {
        $items['admin/reports/memcache/'. $cluster .'/'. $type] = array(
          'type' => $type == 'default' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
          'page callback' => 'memcache_admin_stats',
          'page arguments' => array($cluster, $type),
          'title' => $type,
          'access arguments' => array('access memcache statistics'),
          'weight' => $sub_count,
        );
        $sub_count++;
      }
    }
  }

  return $items;
}

/**
 * Settings form.
 */
function memcache_admin_admin_settings() {
  $form['show_memcache_statistics'] = array('#type' => 'checkbox',
    '#title' => t('Show memcache statistics at the bottom of each page'),
    '#default_value' => variable_get('show_memcache_statistics', 1),
    '#description' => t("These statistics will be visible to users with the 'access memcache statistics' permission."),
  );
  return system_settings_form($form);
}

/**
 * Memcahe Stats page
 *
 * @param string $cluster - which cluster to view?
 * @param string $type - which type of stat, eg: default, reset, malloc, maps, cachedump, slabs, items or sizes
 * @return string
 */
function memcache_admin_stats($cluster = 'default', $type = 'default') {
  $bin = _memcache_admin_get_bin_for_cluster($cluster);

  if ($bin) {
    $stats = dmemcache_stats($bin, $type);

    if (is_array($stats) && count($stats)) {
      $output = '';

      foreach ($stats as $server => $values) {
        if (is_array($values)) {
          // Do some custome value tweaks for specific stat page types.
          switch ($type) {
            case 'default' :
              $values['uptime'] = format_interval($values['uptime']);
              $values['time'] = format_date($values['time']);
              $values['bytes'] = format_size($values['bytes']);
              $values['bytes_read'] = format_size($values['bytes_read']);
              $values['bytes_written'] = format_size($values['bytes_written']);
              $values['limit_maxbytes'] = format_size($values['limit_maxbytes']);

              //Custom Entries
              $values['hit_percentage'] = ($values['cmd_get'] > 0) 
                ? number_format(100.0 * $values['get_hits'] / $values['cmd_get'], 2) . '%' 
                : '0';

              $mem_used = intval($values['bytes']) / (intval($values['limit_maxbytes']) * 1024);
              $values['mem_used'] = number_format(100.0 * $mem_used, 2) . '%';
              break;
          }

          $output .= theme('memcache_admin_stats_table', array('server' => $server, 'stats' => $values));
        }
        else {
          drupal_set_message(t('Unable to get statistic from server %server', array('%server' => $server)));
        }
      }
    }

    else {
      $output = '';
      drupal_set_message(t('No available statistics for this bin.'));
    }
  }

  return $output;
}

/**
 * Implementation of hook_theme().
 */
function memcache_admin_theme() {
  return array(
    'memcache_admin_stats_table' => array(
      'variables' => array('server' => NULL, 'stats' => NULL),
    )
  );
}

/**
 * Theme function for rendering the output from memcache_admin_stats
 *
 * @param string $server - Server name:port for caption for the table
 * @param array $stats - array of key/value string pairs for the table results
 * @return string
 */
function theme_memcache_admin_stats_table($variables) {
  $server = $variables['server'];
  $stats = $variables['stats'];

  $rows = array();

  foreach ($stats as $key => $value) {
    if (is_array($value)) {
      $rs = array();
      foreach ($value as $k => $v) {
        $rs[] = array($k, $v);
      }
      $rows[] = array($key, theme('table', array('header' => array(), 'rows' => $rs)));
  return theme('table', array('header' => array(t('Property'), t('Value')), 'rows' => $rows, 'caption' => $server));
}


/**
 * Retrieve the cluster for any given bin
 *
 * @param string $cluster - Cluster ID
 * @return string
 */
function _memcache_admin_get_bin_for_cluster($cluster) {
  static $cluster_map = array();

  if (!isset($cluster_map[$cluster])) {
    $memache_bins = variable_get('memcache_bins', array());
    if ($mapping = array_search($cluster, $memache_bins)) {
      $cluster_map[$cluster] = $mapping;
    }
    else {
      $cluster_map[$cluster] = 'default';
    }
  }

  return $cluster_map[$cluster];
}

/**
 * See memcache_admin_init() which registers this function as a shutdown function.
 * Displays memcache stats in the footer.
 */
function memcache_admin_shutdown() {
  global $_memcache_statistics;

  // Try not to break non-HTML pages.
  if (function_exists('drupal_get_headers')) {
    $headers = drupal_get_headers();
    if(strstr($headers, 'xml') || strstr($headers, 'javascript') || strstr($headers, 'plain')) {
      return;
    }
  }

  if (variable_get('show_memcache_statistics', TRUE) && function_exists('user_access') && user_access('access memcache statistics')) {
    $stats = array();

    foreach ($_memcache_statistics as $stat => $value) {
      $items = array('items' => $value);
      $stats[] = "<strong>$stat:</strong> ". theme('item_list', array('items' => $value));
      $output = theme('item_list', array('items' => $stats));

      // this makes sure all of the HTML is within the <body> even though this <script> is outside it
      print '<div id="memcache-devel"><h2>'. t('Memcache statistics'). '</h2>'. $output. '</div>';
    }
  }
}