Rolling log file in PHP
I'd like to write/read a rolling log file with PHP, where only the latest ~300 lines are stored/read and anything older is discarded. I'm not sure of the most efficient way of going about it - it needs to work fast as it's recording page hits on high traffic web sites.
Another PHP script will be regularly reading the log files and using the data for calculations. There are so many PHP file functions I'm confused as to where to start!
I don't think my hos开发者_StackOverflow中文版ting environment has access to commands such as tail
or awk
or similar, so a pure PHP solution is preferred. Any help appreciated!
You can use fopen: http://us3.php.net/manual/en/function.fopen.php
$mode = 'a+'; // opens the file with read/write access and sets the pointer to the end of the file
$handle = fopen ($filename, $mode);
Next you pump the file into an array and lob off everything except the last 300 lines.
If you are really interested in just keeping the file down to a certain size (you said ~300 lines) then you can use fseek http://us3.php.net/manual/en/function.fseek.php (From the manual):
<?php
$fp = fopen('somefile.txt', 'r');
// read some data
$data = fgets($fp, 4096);
// move back to the beginning of the file
// same as rewind($fp);
fseek($fp, 0);
?>
For performance comparisons, you'll have to do some benchmarking, but here's one possible way to do it:
<?php
function writeToLog($file, $str, $maxLines = 300) {
$linesToWrite = explode("\n", $str);
$numLinesToWrite = count($linesToWrite);
$logLines = explode("\n", file_get_contents($file));
array_splice($logLines,
$maxLines - $numLinesToWrite,
$numLinesToWrite,
$linesToWrite);
file_put_contents($file, implode("\n", $logLines));
}
Not sure about the performance of this either, but here's my take on it:
// read lines in file as array
$lines = file( 'log.log', FILE_IGNORE_NEW_LINES );
// log file equal to or larger than 300 lines?
if( count( $lines ) >= 300 )
{
// remove everything from line 0 to 299 from the end
// in other words keep last 299 lines
array_splice( $lines, 0, -299 );
}
// append a new line of data
$lines[] = 'Test data ' . time() . "\n";
// put the lines back, first imploding the lines array with a newline char
file_put_contents( 'log.log', implode( "\n", $lines ) );
PHP function for doing so. Makes sure log is writeable. Creates log if not exists:
// keeps log_file <= max_log_lines lines
function logTruncated($report, $log_file, $max_log_lines = 1000)
{
if (!$report || !$log_file)
return;
if (!touch($log_file))
die('Cant write log: '.$log_file);
$lines = array_merge(file($log_file), preg_split('/\r?\n/', $report));
$lines = array_slice($lines, -1 * $max_log_lines);
file_put_contents($log_file, implode("\n", $lines));
}
Just checks if the log file exists already and if it's bigger than the max size (4Mb in my code) it will read it, remove some text at the beginning and add the new text in order to keep the size within the max size
$text = "Your text to log";
//if file exists and it's bigger than 4Mb then remove some text at the beginning to limit size
$log_file = __DIR__ . DIRECTORY_SEPARATOR . 'LOGS' . 'log.txt';
if(file_exists($log_file)){
clearstatcache();
$size = filesize($log_file);
if($size >= 4194304){
$chunk = file_get_contents($log_file, FALSE, NULL, -(4194304 - strlen($text)));
$text = $chunk . $text;
$mode = 0; //overwrite
} else $mode = FILE_APPEND;
}
$file_exc = file_put_contents($log_file, $text . PHP_EOL , $mode | LOCK_EX);
Append line and rename maxed out file
Sometimes the best solution involves tweaking the requirements a little bit.
Rather than jump through hoops parsing and modifying a single log file, just append each new event to the log file and roll-over the file whenever it hits the size limit.
function logEvent($line) {
$maxLogFileSize = 24000; //24K (about 300 lines)
$logFilename = "events.log"; //latest events
$archiveFilename = "events-archive.log"; //older events
if (filesize($logFilename) > $maxLogFileSize)
rename($logFilename, $archiveFilename);
file_put_contents($logFilename, $line . PHP_EOL, FILE_APPEND);
}
By rolling over your log file into an archive log file, you can achieve all the goals plus:
- Simplify the code
- Improve performance
- Reduce risk of file corruption
精彩评论