LINUX.ORG.RU

Чтение файла с конца

 , ,


0

1

Ищу быстрый способ чтения построчно большого файла с конца.
Нашел такой вариант с fseek https://werxltd.com/wp/2010/10/29/reading-a-file-line-by-line-in-reverse-with... но он тормозной. Все примеры в гугле основаны на перемещении указателя, но почему то все тормозное. Неужели fseek операция такая ресурсоемкая?
Собственно нужно вот что. Анализ лог-файла за последний час(в кроне скрипт запускается каждый час) и отыскивание ошибок с оповещением админу. Читаю файл с конца и когда дохожу до строки X, то прерывается

Замерил

Считывание 10 000 строк с начала файла
time loganalizer.php
real    0m0.020s

Считывание 50 000 строк с начала файла
time loganalizer.php
real    0m0.036s

Считывание 10 000 строк с конца файла
time loganalizer.php
real    0m3.105s

Считывание 50 000 строк с конца файла
time loganalizer.php
real    0m15.547s


stat /var/log/nginx/access.log.1
Size: 2 497 557 076



разница просто сумасшедшая, с конца файла в 155 и 500 раз дольше

★★★★

Последнее исправление: gobot (всего исправлений: 1)

Ответ на: комментарий от anonymous

Если файл большой, нужно чанкать.

anonymous
()

mmap заюзай, че за рофлы с fseek.

Deleted
()

Чо-то само решение выглядит так себе. Я бы попытался сначала прикрутить отправку уведомления админку к той штуке которая этот лог пишет.

А если невозможно, то сначала фильтровал бы файл в другой временный файл с помощью sed, и им же (а еще лучше tac'ом) переворачивал файл в обратную сторону построчно и только потом спокойно по порядку разбирал бы нужные строки с начала файла.

Suntechnic ★★★★★
()

Собственно нужно вот что. Анализ лог-файла за последний час(в кроне скрипт запускается каждый час) и отыскивание ошибок с оповещением админу. Читаю файл с конца и когда дохожу до строки X, то прерывается

А не лучше ли запомнить смещение на котором ты в прошлый раз остановился и продолжать с него в следующий раз?

urxvt ★★★★★
()
Ответ на: комментарий от urxvt

Хотелось бы без костылей и промежуточных данных. Тем более файл может быть перезаписан(ротация логов)

gobot ★★★★
() автор топика
Ответ на: комментарий от Mike_RM

tac на порядок быстрее и вроде многопоточный. Именно так я и сделал

tac /var/log/nginx/access.log /var/log/nginx/access.log.1 | /var/www/cli/loganalizer.php

в самом скрипте
while($line = fgets(STDIN)){
}

Вроде норм решение пока

gobot ★★★★
() автор топика

Позиционирование по символам в примере - это 3.14ц strace на этот пример натрави и все будет ясно.

Быстро читать файл с конца можно только читая достаточно большими блоками (и желательно с позиции кратной размеру страницы памяти) в обратном порядке, а в блоке пройти по строкам в обратном порядке не так уж и сложно.

vel ★★★★★
()
Ответ на: комментарий от vel

Да...это надо уже какой то спец класс писать с буфером. Не нашел пока реализации готовой, сделал через tac

gobot ★★★★
() автор топика

tail дергай системным вызовом.

r_asian ★☆☆
()
Ответ на: комментарий от romanlinux

Выше я кидал уже, он проигнорировал.

anonymous
()
function FlockOpen($fname, $flock_flag=LOCK_SH, $mode='r', $is_read=true, $flags=0)
	{
	$fh = fopen($fname, $mode);
	while (!flock($fh, $flock_flag)) usleep(1000);
	if ($is_read)
		{
		$str = fread($fh, filesize($fname));
		if ($flags & FILE_SKIP_EMPTY_LINES)
			{
			$str = trim(str_replace("\r", "\n", $str), "\n");
			while (mb_strpos($str, "\n\n") !== false) $str = str_replace("\n\n", "\n", $str);
			}
		return (array($fh, $str));
		}
	else return ($fh);
	}

function FlockClose($fh, $is_write=false, $str='')
	{
	if ($is_write)
		{
		ftruncate($fh, 0);
		rewind($fh);
		fwrite($fh, $str);
		fflush($fh);
		}
	flock($fh, LOCK_UN);
	fclose($fh);
	}

function Tail($fname, $lines, $bs, $is_remove_title)
	{
	$fh = FlockOpen($fname, LOCK_SH, 'r', false);
	$fs = filesize($fname);
	$stack = '';
	$Arr = array();
	$readlines = 0;
	$c = ceil($fs/$bs);
	for ($i=1; $i <= $c; $i++)
		{
		if ($readlines >= $lines) break;
		else
			{
			$pos = $fs-($bs*$i);
			if ($pos >= 0)
				{
				fseek($fh, $pos, SEEK_SET);
				$stack = fread($fh, $bs) . $stack;
				}
			else
				{
				fseek($fh, 0, SEEK_SET);
				$stack = fread($fh, $bs+$pos) . $stack;
				}
			for ($j=1; $j <= $bs; $j++)
				{
				$n_pos = strrpos($stack, "\n");
				if ($n_pos === false) break;
				else
					{
					$str = trim(substr($stack, $n_pos), "\r\n");
					if (!IsStrEmpty($str))
						{
						$Arr[++$readlines] = $str;
						}
					$stack = substr($stack, 0, $n_pos);
					if ($readlines >= $lines) break;
					}
				}
			if ((!$is_remove_title) && ($readlines < $lines) && ($pos < 0) && (!IsStrEmpty($stack)))
				{
				$str = trim($stack, "\r\n");
				if (!IsStrEmpty($str))
					{
					$Arr[++$readlines] = $str;
					}
				}
			}
		}
	//if ($pos < 0) unset($Arr[count($Arr)]);
	FlockClose($fh);
	return ($Arr);
	}

Вроде работает. Под свои нужды адаптируешь сам.

Grzegorz

anonymous
()

Пацанам респект и уважуха пацанчикам

Пацанчики, я тут вклинюсь, да!

Короче, надо раскумекать как на википедии сделаны всплывающие сноски (footnotes).

Ну вот типа как тут:

https://en.wikipedia.org/wiki/Linux

``` Linux (/ˈlɪnəks/ (About this soundlisten) LIN-əks)[9][10] is a family of open source ```

Подносим мышку к `[9]` и там появляется всплывающая фигня. Понятно что это сделано через JS. Но где на страничке сам код выцапать? Не нашОол. Памагите.

anonymous
()

Может быть быстрее и проще каждый раз читать файл с начала, пропуская неинтересное.

Ещё довольно просто сделать наблюдалку из tail -F, ей и ротация нипочём.

legolegs ★★★★★
()

Что если настроить ротацию логов и бить их по часу?

Когда нужно, брать лог файл за последний час.

serioja
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.