9

これをどのように表現すればよいかわからないので、入力してから編集し、出てくる質問に答えます..

現在、ローカル ネットワーク デバイス (PHP4 ベース) で、これを使用してライブ システム ログ ファイルを追跡しています: http://commavee.com/2007/04/13/ajax-logfile-tailer-viewer/

これはうまく機能し、1 秒ごとに外部ページ (logfile.php) をロードしますtail -n 100 logfile.log 。スクリプトはバッファリングを行わないため、画面に表示される結果はログ ファイルの最後の 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");
}
?>

この部分はうまく機能しています。

$outputline を新しいテキスト ファイルに書き込むように logfile.php ページを調整しました。fwrite($fp,$outputline."\n");

これは機能しますが、作成された新しいファイルの重複に問題があります。

明らかに、tail -n 100 が実行されるたびに結果が生成されます。次に実行すると、同じ行がいくつか生成される可能性があります。これが繰り返されるため、新しいテキスト ファイルに複数の行が重複してしまう可能性があります。

同一の一致がある可能性があるため、これから書き込もうとしている行を前の行と直接比較することはできません。

この100行の現在のブロックを前のブロックと比較して、一致しない行のみを書き込む方法はありますか..ブロックAとBに必要な同一の行が含まれるという問題が再び発生する可能性があります...

logfile.php を更新して、ログファイルで最後に取得した位置を記録し、そこから次の 100 行のみを読み取り、それらを新しいファイルに書き込むことは可能ですか?

ログ ファイルは最大 500 MB になる可能性があるため、毎回すべてを読みたくありません..

アドバイスや提案は大歓迎です..

ありがとう

更新 @ 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 行から開始して、更新だけを取得するにはどうすればよいですか?

ありがとう :)

UPDATE 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);
}

最後の既知の位置から読み取るように、これを修正する方法はありますか..?

ありがとう

4

3 に答える 3

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 は、返される行の最大数です。これは、1 秒あたり 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));
    }
}
于 2013-06-08T13:07:10.290 に答える
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 は、読み取り位置 position が前回の実行からの場所を含む単なるテキスト ファイルです。私はちょうどその位置を探して、残りを出力バッファにパイプしています。

于 2013-06-06T02:47:05.283 に答える
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)。

于 2013-06-05T17:28:56.850 に答える