Author: selfimpr
Blog: http://blog.youkuaiyun.com/lgg201
Mail: lgg860911@yahoo.com.cn
需求:日志文件按照时间顺序记录,每次只需要按照时间分析其中某段的日志内容。当此条件下,使用sed,awk等工具是需要扫描全文件的,因此,有了这个小东西,按照文件内容二分查找。
该程序未经测试,有不合理,不正确,性能不佳需要改进的地方,请直言。。。
二分查找库程序:
#! /usr/bin/php
<?php
function binary_find_analysis($filename, $process_func, $compare_func) {
if( !is_readable($filename) || !function_exists($process_func) || !function_exists($compare_func) ) return false;
if( !($fp = fopen($filename, 'r')) ) return false;
$file_size = filesize($filename);
$args = func_get_args();
$extra_args = array_slice($args, 3);
#将文件指针移动到符合条件的首行
if( !goto_correct_line($fp, $file_size, 0, $file_size, $compare_func, $extra_args) ) return false;
#读取其后所有匹配条件的行,调用处理函数
while( ($line = fgets($fp)) && !call_user_func_array($compare_func, build_arguments($extra_args, $line)) )
$process_func($line);
fclose($fp);
return true;
}
function build_arguments($extra_args) {
$args = func_get_args();
$other_args = array_slice($args, 1);
return array_merge($other_args, $extra_args);
}
function goto_correct_line($fp, $file_size, $begin, $end, $compare_func, $extra_args) {
#未查找到匹配条件记录
if($begin >= $end) return false;
$middle = floor( ($begin + $end) / 2 );
fseek($fp, $middle);
goto_line_head($fp, $file_size);
$original_pos = ftell($fp);
$line = fgets($fp);
$current_pos = ftell($fp);
$cmp_ret = call_user_func_array($compare_func, build_arguments($extra_args, $line));
if( $cmp_ret > 0 )
return goto_correct_line($fp, $file_size, $begin, $original_pos - 1, $compare_func, $extra_args);
else if( $cmp_ret < 0 )
return goto_correct_line($fp, $file_size, $current_pos, $end, $compare_func, $extra_args);
else
return goto_correct_head($fp, $file_size, $compare_func, $extra_args);
}
function goto_correct_head($fp, $file_size, $compare_func, $extra_args) {
while( ($tmp_pos = ftell($fp)) && !call_user_func_array($compare_func, build_arguments($extra_args, $line = fgets($fp))) ) {
fseek($fp, $tmp_pos);
goto_prev_line_head($fp, $file_size);
}
return true;
}
function goto_prev_line_head($fp, $file_size) {
goto_line_head($fp, $file_size);
goto_nchar($fp, $file_size, -1);
goto_line_head($fp, $file_size);
}
function goto_line_head($fp, $file_size) {
goto_nchar($fp, $file_size, -1);
while( ftell($fp) && PHP_EOL != fgetc($fp) ) goto_nchar($fp, $file_size, -2);
}
function goto_nchar($fp, $file_size, $n) {
$offset = ftell($fp) + $n;
$offset = min($file_size, max($offset, 0));
fseek($fp, $offset);
}
我们的日志格式为:
时间(Y-m-d H:i:s) 日志级别 日志编号 日志内容
下面是根据自己的业务写的一个简单的调用处理
function log_process($data) {
echo $data;
}
function timecmp($a, $b) {
preg_match(LOG_PARSE_PATTERN, $a, $matches);
return floor(strtotime($matches['time']) / 300) - floor($b / 300);
}
define('LOG_PARSE_PATTERN', '/^(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})(?>\s+)(?P<level>(?>\w+))(?>\s+)(?P<number>(?>\d+))(?>\s+)(?P<content>(?>.*))$/');
#为strtotime设置时区
date_default_timezone_set('Asia/Shanghai');
$time = strtotime('2011-07-28 00:10:00');
binary_find_analysis('log_2011-07-28-00', 'log_process', 'timecmp', $time);