#!/usr/sbin/php -q
<?php
##
## mysqld log files analyzing
## databases, tables query statistics
##
## [author]
##  - Chilbong Kim <san2(at)linuxchannel.net>
##  - http://linuxchannel.net/
##
## [changes]
##  - 2003.07.27 : fixed, check_args()
##  - 2003.07.18 : fixed, memory_limit by fopen(), it's maximum file size
##  - 2003.07.11 : bug fixed and add options
##  - 2003.07.09 : new build
##
## [download & online source view]
##  - http://ftp.linuxchannel.net/devel/php_mysql_log/
##
## [requirements]
##  - PHP CGI
##  - some mysqld log files
##
## [usage]
##  shell> php -q this.file.name --help
##  or
##  shell> chmod u+x this.file.name
##  shell> ./this.file.name --help
##

## print usage to stderr
##
function usage($errstr='')
{
  
error_log(
    
'usage  : '.T_MD.'php -q '.basename(__FILE__).
    
' [OPTIONS] [mysqld.log [mysqld.log2 ...]]'.T_ME."\n".
    
'options:'."\n".
    
'  -h, --help        print this message and exit'."\n".
    
'  -r, --print_r     print by print_r() style, this default'."\n".
    
'  -p, --php         print by associative array style'."\n".
    
'  -s, --serialize   print by serialize() style'."\n"
    
);

  if(
$errstrerror_log(T_MR.$errstr.T_ME);
  exit;
}

function 
check_args($argc$argv, &$pr, &$logfiles)
{
  global 
$_SERVER// for PHP/4.0.x

  ## check user-agent, only php execute on command line shell
  ##
  
if($_SERVER[HTTP_USER_AGENT])
  { 
usage('sorry !!!'); }

  
## reset
  ##
  
$pr $logfiles = array();

  for(
$i=1$i<$argc$i++) // skip $i=0
  
{
    
$cw preg_replace('/^[-]+(?)/','\\1',&$argv[$i]);
    
$cw = ($cw[0].$cw[1] == 'pr') ? 'r' $cw[0];

    if(
$cw == 'h')
    { 
usage(); break; }

    else if(
$cw == 'r' || $cw == 'p' || $cw == 's')
    { 
$pr[$cw] = TRUE; }

    else
    {
        if(
file_exists(&$argv[$i]) && is_file(&$argv[$i]))
        { 
$logfiles[] = &$argv[$i]; }
        else
        { 
error_log(T_MR.$argv[$i].' file not exists'.T_ME); }
    }
  }

  if(
sizeof($logfiles)<1usage();
  if(!
$pr[r] && !$pr[p] && !$pr[s]) $pr[r] = TRUE;
}

## Direct IO access get file
## 2002.08.23
##
function get_diofile($file)
{
  if(
$fp = @dio_open($file,O_RDONLY))
  {
    
$contents dio_read($fp,filesize($file));
    
dio_close($fp);
  }

  return 
$contents;
}

## common get file
## 2002.08.23
##
function get_file($file)
{
  if(!
file_exists($file)) return '';
  if(
function_exists(dio_open)) return get_diofile($file);

  if(
$fp = @fopen($file,'r'))
  {
    
$contents fread($fp,filesize($file));
    
fclose($fp);
  }

  return 
$contents;
}

## get percents
##
function add_per($sums$types$s2=array(), $q2=array())
{
  
$tmp array_keys($sums);
  foreach(
$tmp AS $dt)
  {
    foreach(
$types[$dt] AS $q=>$v)
    {
        
$types[$dt][$q] .= ' ('.
            
sprintf('%.2f',@($v*100/$sums[$dt])).'%)';
    }
    if(
$s2[$dt] && $q2[$dt]) add_per(&$s2[$dt], &$q2[$dt]);
  }
}

## get table name in each line
##
function get_table_name($line$splitw)
{
  if(
$splitw)
  {
    list(,
$cwords) = preg_split("/$splitw/i",$line);
    list(
$table) = preg_split('/[\s]+/',$cwords);
    
$table preg_replace('/[(,\'"`].*$/','',$table);

    if(
preg_match('/\./',$table))
    { list(
$refdb,$table) = preg_split('/\./',$table); }
  }

  return array(
trim($table),trim($refdb));
}

## for printing
##
function arr2print($array$name$title=''$int=0)
{
  if(!
$array || !is_array($array)) return;
  if(
$title) echo "\n".'## '.$title."\n".'##'."\n";
  if(!
$int$pt '\'';

  
$keys array_keys($array);
  
$size sizeof($array);

  for(
$i=0$i<$size$i++)
  {
    
$key = &$keys[$i];
    
$var $name.'[\''.$key.'\']';

    if(
is_array($array[$key]))
    { 
arr2print($array[$key],$var,0,$int); }
    else
    { echo 
"\$$var\t= ${pt}$array[$key]${pt};\n"; }
  }
}

######################################################
######################################################

set_time_limit(0);
ini_set('memory_limit','50M'); // it's maximum file size by fopen()

global $_SERVER// force to set
unset($argc,$argv,$dbq,$dbs,$tableq,$tables,$pr,$logfiles); // reset

## for xterm, color-terminal
##
define('T_MR'sprintf('%s',"\033[1;31m")); // bold red
define('T_MD'sprintf('%s',"\033[1;39m")); // bold white
define('T_ME'sprintf('%s',"\033[0;39m")); // normal white

$argc $_SERVER[argc];
$argv $_SERVER[argv]; // array

## check arguments
##
check_args($argc,$argv,&$pr,&$logfiles); // $pr and $logfile is reference

## split words of each query, for get_table_name()
##
$splits = array
(
  
'select'    => 'FROM\s+',
  
'insert'    => 'INSERT\s+(LOW_PRIORITY|DELAYED)*\s*(IGNORE)*\s*(INTO)*\s*',
  
'update'    => 'UPDATE\s+(LOW_PRIORITY)*\s*(IGNORE)*\s*',
  
'delete'    => 'FROM\s+',
  
'show'    => 'CREATE\s+TABLE\s+',
  
'desc'    => 'DESC\s+',
  
'describe'    => 'DESCRIBE\s+',
  
'explain'    => 'EXPLAIN\s+(SELECT.+FROM\s+)*',
  
'lock'    => 'TABLES\s+',
  
'create'    => 'TABLE\s+(IF\s+NOT\s+EXISTS)*\s*',
  
'replace'    => 'REPLACE\s+(LOW_PRIORITY|DELAYED)*\s*(INTO)*\s*',
  
'alter'    => 'TABLE\s+',
  
'analyze'    => 'TABLE\s+',
  
'optimize'    => 'TABLE\s+',
  
'rename'    => 'TABLE\s+',
  
'drop'    => 'TABLE\s+(IF\s+EXISTS)*\s*',
  
'backup'    => 'TABLE\s+',
  
'restore'    => 'TABLE\s+',
  
'check'    => 'TABLE\s+',
  
'repair'    => 'TABLE\s+',
  
'load'    => 'INTO\s+TABLE\s+',
  
'handler'    => 'HANDLER\s+',
  
'turncate'    => 'TABLE\s+',
);

## get contents
##
//$contents = `cat $logfile 2>/dev/null`;

$size sizeof($logfiles);
for(
$i=0$i<$size$i++)
$contents .= get_file(&$logfiles[$i]); }

## parsing
##
$contents preg_replace
(
  array(
'/([0-9]+)\sInit DB/','/[0-9]+\s(Init_DB|Query|Connect)/'),
  array(
'\\1 Init_DB','__DIV__\\0'),
  
$contents
);

## get array(splited by '__DIV__')
##
$arr preg_split('/__DIV__/',$contents);

$size sizeof($arr);
$term = (int)($size/10);

## get query statistics each databases, tables
##
for($i=1$i<$size$i++) // skip 0
{
  if(!(
$i%$term)) error_log(sprintf('%03.2f',$i*100/$size).'% done');
  
flush();

  
$line preg_replace('/[\r\n]+/',' ',&$arr[$i]);
  list(
$nid,$cmd,$args,$on,$db) = preg_split('/[\s]+/',$line);

  if(
$cmd == 'Connect')
  {
    if(
$db && $on == 'on' && !preg_match('/^(for|[\d]+)$/',$db))
    { 
$dbname $db; }    // change dbname
    
else
    { 
$dbname ''; }    // this false
    
continue;
  }
  else if(
$cmd == 'Init_DB')
  {
    if(!
$db && !$on)
    { 
$dbname $args; }    // change dbname
    
else
    { 
$dbname ''; }    // this false
    
continue;
  }
  else if(
$cmd == 'Query'// this Query
  
{
    
$query strtolower($args);    // query command
    
list($table,$refdb) = get_table_name($line,&$splits[$query]);
    
$tmpdb $refdb $refdb $dbname;

    if(
$tmpdb)
    {
        
$dbq[$tmpdb][$query]++;    // db query statistics
        
$dbs[$tmpdb]++;        // db sum statistics

        
if($table)
        {
            
$tableq[$tmpdb][$table][$query]++; // table statistics
            
$tables[$tmpdb][$table]++; // table sum statistics
        
}
    }
  }
}

error_log('100.00% done'."\n".'preparing to print. wait...');
sleep(1);

unset(
$arr);
@
add_per(&$dbs, &$dbq, &$tables, &$tableq); // use array-references

@ksort($dbq);
@
ksort($dbs);
@
ksort($tableq);
@
ksort($tables);

## print print_r() style
##
if($pr[r])
{
  echo 
"\n".'STATISTICS OF DATABASES(QUERY) :'."\n";
  
print_r($dbq);
  
print_r($dbs);

  echo 
"\n".'STATISTICS OF TABLES(QUERY) :'."\n";
  
print_r($tableq);
  
print_r($tables);
}

## print associative-array style
##
if($pr[p])
{
  echo 
'<?php'."\n\n";
  
arr2print($dbq,'databas_query','database query statistics :');
  
arr2print($dbs,'databas_sum','database sum statistics :',1);
  
arr2print($tableq,'table_query','table query statistics :');
  
arr2print($tables,'table_sum','table sum statistics :',1);
  echo 
"\n".'?>';
}

## print serialize style
##
if($pr[s])
{
  echo 
serialize($dbq)."\n";
  echo 
serialize($dbs)."\n";
  echo 
serialize($tableq)."\n";
  echo 
serialize($tables)."\n";
}


## last
##
unset($dbq,$dbs,$tableq,$tables);
flush();

exit;
?>