我不知道怎么说这个,所以我会输入它然后编辑并回答任何问题......
目前在我的本地网络设备上(基于PHP4)我正在使用它来定制实时系统日志文件:http://commavee.com/2007/04/13/ajax-logfile-tailer-viewer/
这很好用,并且每1秒加载一个执行tail -n 100 logfile.log
的外部页面(logfile.php)脚本不进行任何缓冲,因此它在屏幕上显示的结果是日志中的最后100行文件。
logfile.php包含:
<? // logtail.php $cmd = "tail -10 /path/to/your/logs/some.log"; exec("$cmd 2>&1", $output);
foreach($output as $outputline) {
echo ("$outputline\n");
}
?>
这部分运作良好。
我已经调整了logfile.php页面,只需使用fwrite($fp,$outputline."\n");
虽然这有效但我在创建的新文件中存在重复问题。
显然,每次运行tail -n 100都会产生结果,下次运行它会产生一些相同的行,因为重复这种情况我最终会在新文本文件中出现多行重复。
我无法直接比较我要写入前一行的行,因为可能会有相同的匹配。
有没有什么方法可以将这100个行的当前块与前一个块进行比较,然后只写出不匹配的行。再次可能的问题是块A&amp; B将包含所需的相同行......
是否可以更新logfile.php以记录它在我的日志文件中最后占用的位置,然后只从那里读取接下来的100行并将其写入新文件?
日志文件最多可达500MB,所以我不想每次都读取它。
欢迎任何建议或建议。
由于
更新@ 16:30
我有点使用:
$file = "/logs/syst.log";
$handle = fopen($file, "r");
if(isset($_SESSION['ftell'])) {
clearstatcache();
fseek($handle, $_SESSION['ftell']);
while ($buffer = fgets($handle)) {
echo $buffer."<br/>";
@ob_flush(); @flush();
}
fclose($handle);
@$_SESSION['ftell'] = ftell($handle);
} else {
fseek($handle, -1024, SEEK_END);
fclose($handle);
@$_SESSION['ftell'] = ftell($handle);
}
这似乎有效,但它首先加载整个文件,然后只加载更新。
如何从最后50行开始,然后只是更新?
谢谢:)
更新04/06/2013 虽然这适用于大文件但速度很慢。
我已经尝试过这段代码了,它似乎更快,但它不仅仅是从它停止的位置读取。
function last_lines($path, $line_count, $block_size = 512){
$lines = array();
// we will always have a fragment of a non-complete line
// keep this in here till we have our next entire line.
$leftover = "";
$fh = fopen($path, 'r');
// go to the end of the file
fseek($fh, 0, SEEK_END);
do{
// need to know whether we can actually go back
// $block_size bytes
$can_read = $block_size;
if(ftell($fh) < $block_size){
$can_read = ftell($fh);
}
// go back as many bytes as we can
// read them to $data and then move the file pointer
// back to where we were.
fseek($fh, -$can_read, SEEK_CUR);
$data = fread($fh, $can_read);
$data .= $leftover;
fseek($fh, -$can_read, SEEK_CUR);
// split lines by \n. Then reverse them,
// now the last line is most likely not a complete
// line which is why we do not directly add it, but
// append it to the data read the next time.
$split_data = array_reverse(explode("\n", $data));
$new_lines = array_slice($split_data, 0, -1);
$lines = array_merge($lines, $new_lines);
$leftover = $split_data[count($split_data) - 1];
}
while(count($lines) < $line_count && ftell($fh) != 0);
if(ftell($fh) == 0){
$lines[] = $leftover;
}
fclose($fh);
// Usually, we will read too many lines, correct that here.
return array_slice($lines, 0, $line_count);
}
任何方式都可以修改,以便从最后的已知位置读取..?
由于
答案 0 :(得分:2)
<强>简介强>
您可以通过跟踪最后一个位置来拖尾文件;
示例
$file = __DIR__ . "/a.log";
$tail = new TailLog($file);
$data = $tail->tail(100) ;
// Save $data to new file
TailLog
是我为此任务编写的一个简单类,这里有一个简单的例子来显示它实际拖尾文件
简单测试
$file = __DIR__ . "/a.log";
$tail = new TailLog($file);
// Some Random Data
$data = array_chunk(range("a", "z"), 3);
// Write Log
file_put_contents($file, implode("\n", array_shift($data)));
// First Tail (2) Run
print_r($tail->tail(2));
// Run Tail (2) Again
print_r($tail->tail(2));
// Write Another data to Log
file_put_contents($file, "\n" . implode("\n", array_shift($data)), FILE_APPEND);
// Call Tail Again after writing Data
print_r($tail->tail(2));
// See the full content
print_r(file_get_contents($file));
输出
// First Tail (2) Run
Array
(
[0] => c
[1] => b
)
// Run Tail (2) Again
Array
(
)
// Call Tail Again after writing Data
Array
(
[0] => f
[1] => e
)
// See the full content
a
b
c
d
e
f
实时拖尾
while(true) {
$data = $tail->tail(100);
// write data to another file
sleep(5);
}
注意:拖尾100行并不意味着总是返回100行。它将返回新行添加100只是要返回的最大行数。如果有任何
,那么在每秒超过100行的大量日志记录的情况下,这可能效率不高
尾巴等级
class TailLog {
private $file;
private $data;
private $timeout = 5;
private $lock;
function __construct($file) {
$this->file = $file;
$this->lock = new TailLock($file);
}
public function tail($lines) {
$pos = - 2;
$t = $lines;
$fp = fopen($this->file, "r");
$break = false;
$line = "";
$text = array();
while($t > 0) {
$c = "";
// Seach for End of line
while($c != "\n" && $c != PHP_EOL) {
if (fseek($fp, $pos, SEEK_END) == - 1) {
$break = true;
break;
}
if (ftell($fp) < $this->lock->getPosition()) {
break;
}
$c = fgetc($fp);
$pos --;
}
if (ftell($fp) < $this->lock->getPosition()) {
break;
}
$t --;
$break && rewind($fp);
$text[$lines - $t - 1] = fgets($fp);
if ($break) {
break;
}
}
// Move to end
fseek($fp, 0, SEEK_END);
// Save Position
$this->lock->save(ftell($fp));
// Close File
fclose($fp);
return array_map("trim", $text);
}
}
尾锁
class TailLock {
private $file;
private $lock;
private $data;
function __construct($file) {
$this->file = $file;
$this->lock = $file . ".tail";
touch($this->lock);
if (! is_file($this->lock))
throw new Exception("can't Create Lock File");
$this->data = json_decode(file_get_contents($this->lock));
// Check if file is valida json
// Check if Data in the original files as not be delete
// You expect data to increate not decrease
if (! $this->data || $this->data->size > filesize($this->file)) {
$this->reset($file);
}
}
function getPosition() {
return $this->data->position;
}
function reset() {
$this->data = new stdClass();
$this->data->size = filesize($this->file);
$this->data->modification = filemtime($this->file);
$this->data->position = 0;
$this->update();
}
function save($pos) {
$this->data = new stdClass();
$this->data->size = filesize($this->file);
$this->data->modification = filemtime($this->file);
$this->data->position = $pos;
$this->update();
}
function update() {
return file_put_contents($this->lock, json_encode($this->data, 128));
}
}
答案 1 :(得分:1)
关于你想如何使用输出并不是很清楚但是这样的工作......
$dat = file_get_contents("tracker.dat");
$fp = fopen("/logs/syst.log", "r");
fseek($fp, $dat, SEEK_SET);
ob_start();
// alternatively you can do a while fgets if you want to interpret the file or do something
fpassthru($fp);
$pos = ftell($fp);
fclose($fp);
echo nl2br(ob_get_clean());
file_put_contents("tracker.dat", ftell($fp));
tracker.dat只是一个文本文件,其中包含读取位置位置与上一次运行的位置。我只是寻求那个位置并将其余部分输送到输出缓冲区。
答案 2 :(得分:0)
使用tail -c <number of bytes
而不是行数,然后检查文件大小。粗略的想法是:
$old_file_size = 0;
$max_bytes = 512;
function last_lines($path) {
$new_file_size = filesize($path);
$pending_bytes = $new_file_size - $old_file_size;
if ($pending_bytes > $max_bytes) $pending_bytes = $max_bytes;
exec("tail -c " + $pending_bytes + " /path/to/your_log", $output);
$old_file_size = $new_file_size;
return $output;
}
优点是你可以取消所有特殊处理的东西,并获得良好的性能。缺点是您必须手动将输出拆分为行,并且可能最终会出现未完成的行。但这不是什么大问题,您可以通过省略输出中的最后一行(并适当地减去old_file_size
中的最后一行字节数)来轻松解决。