Author: selfimpr
Blog: http://blog.csdn.net/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);