Tailing Log File e Write resultados para o novo arquivo

Não tenho a certeza de como dizer isso, então vou escrevê-lo e depois editar e responder a quaisquer perguntas que surgirem.

Atualmente no meu dispositivo de rede local (baseado em PHP4), estou usando isso para colar um arquivo de log do sistema ao vivo: http://commavee.com/2007/04/13/ajax-logfile-tailer-viewer/

Isso funciona bem e a cada 1 segundo carrega uma página externa (logfile.php) que faz uma tail -n 100 logfile.log O script não faz qualquer buffer para que os resultados exibidos na canvas sejam as últimas 100 linhas do arquivo de log .

O logfile.php contém:

 &1", $output); foreach($output as $outputline) { echo ("$outputline\n"); } ?> 

Esta parte está funcionando bem.

Eu adaptei a página logfile.php para escrever o $ outputline para um novo arquivo de texto, simplesmente usando fwrite($fp,$outputline."\n");

Enquanto isso funciona, estou tendo problemas com a duplicação no novo arquivo que foi criado.

Obviamente, cada vez que a cauda -n 100 é executada produz resultados, a próxima vez que ela é executada, poderia produzir algumas das mesmas linhas, pois isso repõe, eu posso acabar com várias linhas de duplicação no novo arquivo de texto.

Não consigo comparar diretamente a linha que estou prestes a escrever em linhas anteriores, pois pode haver correspondências idênticas.

Existe alguma maneira de comparar este bloco atual de 100 linhas com o bloco anterior e, em seguida, apenas escrever as linhas que não estão em correspondência. Mais uma vez o problema que o bloco A & B conterá linhas idênticas que são necessárias …

É possível atualizar logfile.php para observar a posição que ocupou no meu arquivo de registro e, em seguida, leio as próximas 100 linhas a partir daí e escreva essas no novo arquivo?

O arquivo de log pode ser até 500 MB, então eu não quero ler tudo em cada momento ..

Qualquer conselho ou sugestão de boas vindas ..

obrigado

UPDATE @ 16:30

Eu tenho conseguido trabalhar usando:

 $file = "/logs/syst.log"; $handle = fopen($file, "r"); if(isset($_SESSION['ftell'])) { clearstatcache(); fseek($handle, $_SESSION['ftell']); while ($buffer = fgets($handle)) { echo $buffer."
"; @ob_flush(); @flush(); } fclose($handle); @$_SESSION['ftell'] = ftell($handle); } else { fseek($handle, -1024, SEEK_END); fclose($handle); @$_SESSION['ftell'] = ftell($handle); }

Isso parece funcionar, mas ele carrega todo o arquivo primeiro e depois apenas as atualizações.

Como eu conseguiria começar com as últimas 50 linhas e depois apenas as atualizações?

Obrigado 🙂

UPDATE 04/06/2013 Enquanto isso funciona, é muito lento com arquivos grandes.

Eu tentei este código e parece mais rápido, mas não apenas lê de onde ele deixou.

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

De qualquer forma, isso pode ser modificado, então ele vai ler da última posição conhecida …?

obrigado

Introdução

Você pode colar um arquivo rastreando a última posição;

Exemplo

 $file = __DIR__ . "/a.log"; $tail = new TailLog($file); $data = $tail->tail(100) ; // Save $data to new file 

TailLog é uma class simples que eu escrevi para esta tarefa, aqui é um exemplo simples para mostrar o fato de estar realmente rejeitando o arquivo

Teste Simples

 $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)); 

Saída

 // 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 

Real Time Tailing

 while(true) { $data = $tail->tail(100); // write data to another file sleep(5); } 

Nota: Tailing 100 linhas não significa que sempre retornará 100 linhas. Ele retornaria novas linhas adicionadas 100 é apenas o número máximo de linhas para retornar. Isso pode não ser eficiente onde você possui um registro intenso de mais de 100 linhas por segundo. Existe algum

Classe de cauda

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

Bloqueio da cauda

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

Na verdade, não é claro sobre como você quer usar o resultado, mas algo assim funcionará …

 $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 é apenas um arquivo de texto que contém onde a posição de posição de leitura foi da execução anterior. Estou apenas procurando essa posição e encaminhando o resto para o buffer de saída.

Use tail -c , em vez de número de linhas e, em seguida, verifique o tamanho do arquivo. A idéia aproximada é:

 $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; } 

A vantagem é que você pode acabar com todas as coisas de processamento especial e obter um bom desempenho. A desvantagem é que você tem que dividir manualmente a saída em linhas, e provavelmente você pode acabar com linhas inacabadas. Mas isso não é um grande problema, você pode facilmente trabalhar ao mesmo tempo, omitido a última linha sozinha da saída (e subtraindo apropriadamente o último número de bytes da old_file_size do old_file_size ).