Любое приложение генерирует информацию о ходе своей работы. Как правило эта информация скапливается в лог-файлах. Для контроля работы приложения приходится регулярно эти логи читать. Для широко распространенных продуктов пишут анализаторы, которые позволяют более удобно читать логи и агрегировать скопившуюся информацию. Нужно, что бы программа прочитала файл, вычленила из него самую полезную информацию и показала ее разработчику. Записей в логе может быть много и самих файлов может быть тоже много. Поэтому необходимо создать страницу, которая сможет вывести по несколько последних записей из каждого файла.
Представим, что есть лог-файл, в который постоянно добавляются записи. Нам нужно получить, скажем, последние три. Засовывать файл в память с помощью например file_get_contents
не рационально. В некоторых случаях памяти может просто не хватить. Обходить весь файл построчно тоже не интересно, потому что программе придется просмотреть каждую строчку, пока она доберется до конца. И как в этом случае узнать, что это три последние записи. Такая проблема встала передо мной когда я писал свой парсер логов для Yii2. Гугл мне ничего не ответил и я смог найти ответ в документации.
Чудесная функция fseek
принимает вторым аргументом число - отступ от начала, равный количеству байт, на которое надо сдвинуть курсор. В текстовом редакторе, курсор работает по следующему принципу. Когда вы ставите его в самое начало документа, то его положение равно нулю. Стоит нажать один раз вправо на клавиатуре - курсор смещается на один символ и его положение уже равно единице. Здесь мы действуем с обратной стороны. Мы указываем число, на которое курсору надо сместиться. Курсор находит указанное место, и мы можем прочитать нужную строку.
Так как быстро прочитать три записи с конца? Функция принимает третий необязательный аргумент whence
(константу), который влияет на результат указания офсета.
- SEEK_SET - устанавливает курсор на указанное количество байт от начала файла.
- SEEK_CUR - устанавливает курсор на указанное количество байт от текущего положения.
- SEEK_END - устанавливает курсор на указанное количество байт от конца файла.
Так вот, последняя константа нам и нужна. Её то я и использовал. Но оффсет здесь должен быть отрицательным. Если вы хотите сместить курсор на 10 байт с конца, то вызывать ее нужно следующим образом:
fseek($handle, -10, SEEK_END);
Обратите внимание, что при неудачном смещении функция вернет не false
а -1
. Для своего модуля который читает логи я написал функцию, которая смещает курсор на одну строчку:
public function seek()
{
while(true)
{
$this->length--;
if(fseek($this->handle, $this->length, SEEK_END) == -1) return false;
if(fgetc($this->handle) == "\n") return true;
}
}
Она сдвигает курсор к началу файла на один символ и смотрит, что это за символ. Если это перенос строки, то значит смещение завершено. Таким образом я могу прочитать три записи с конца файла.