Как я могу закрепить файл журнала в Python?
Я хотел бы сделать вывод tail -F или чего-то подобного доступного мне в Python без блокировки или блокировки. Я нашел какой-то действительно старый код, чтобы сделать это здесь, но я думаю, что должен быть лучший способ или библиотека, чтобы сделать то же самое к настоящему времени. Кто-нибудь знает об одном?
В идеале, у меня было бы что-то вроде tail.getNewData()
, которое я мог бы вызвать каждый раз, когда мне нужно больше данных.
Ответы
Ответ 1
Итак, это происходит довольно поздно, но я снова столкнулся с той же проблемой, и теперь есть гораздо лучшее решение. Просто используйте pygtail:
Pygtail считывает строки журнала, которые не были прочитаны. Это будет даже обрабатывать файлы журналов, которые были повернуты. На основе logtail logtail2 (http://logcheck.org)
Ответ 2
Неблокирование
Если вы используете linux (поскольку окна не поддерживают выбор вызова в файлах), вы можете использовать модуль подпроцесса вместе с модулем select.
import time
import subprocess
import select
f = subprocess.Popen(['tail','-F',filename],\
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
p = select.poll()
p.register(f.stdout)
while True:
if p.poll(1):
print f.stdout.readline()
time.sleep(1)
Обследует выходной канал для новых данных и печатает его, когда он доступен. Обычно time.sleep(1)
и print f.stdout.readline()
заменяются полезным кодом.
Блокировка
Вы можете использовать модуль подпроцесса без дополнительных вызовов модуля.
import subprocess
f = subprocess.Popen(['tail','-F',filename],\
stdout=subprocess.PIPE,stderr=subprocess.PIPE)
while True:
line = f.stdout.readline()
print line
Это также будет печатать новые строки по мере их добавления, но он будет блокироваться до закрытия хвостовой программы, возможно, с помощью f.kill()
.
Ответ 3
Используя модуль sh (pip install sh):
from sh import tail
# runs forever
for line in tail("-f", "/var/log/some_log_file.log", _iter=True):
print(line)
[обновление]
Так как sh.tail с _iter
= True - это генератор, вы можете:
import sh
tail = sh.tail("-f", "/var/log/some_log_file.log", _iter=True)
Затем вы можете "getNewData" с помощью:
new_data = tail.next()
Обратите внимание, что если буфер хвоста пуст, он будет блокироваться, пока не появится больше данных (из вашего вопроса не ясно, что вы хотите сделать в этом случае).
[обновление]
Это работает, если вы замените -f на -F, но в Python он будет заблокирован. Меня больше интересовала бы функция, которую я мог бы вызвать, чтобы получить новые данные, когда захочу, если это возможно. - Эли
Генератор контейнера, помещающий хвостовой вызов через некоторое время. Истинный цикл и перехватывание возможных исключений I/O будет иметь почти такой же эффект -F.
def tail_F(some_file):
while True:
try:
for line in sh.tail("-f", some_file, _iter=True):
yield line
except sh.ErrorReturnCode_1:
yield None
Если файл становится недоступным, генератор возвращает None. Однако он все еще блокируется до появления новых данных, если файл доступен. Мне остается неясным, что вы хотите сделать в этом случае.
Раймонд Хеттингер выглядит довольно неплохо:
def tail_F(some_file):
first_call = True
while True:
try:
with open(some_file) as input:
if first_call:
input.seek(0, 2)
first_call = False
latest_data = input.read()
while True:
if '\n' not in latest_data:
latest_data += input.read()
if '\n' not in latest_data:
yield ''
if not os.path.isfile(some_file):
break
continue
latest_lines = latest_data.split('\n')
if latest_data[-1] != '\n':
latest_data = latest_lines[-1]
else:
latest_data = input.read()
for line in latest_lines[:-1]:
yield line + '\n'
except IOError:
yield ''
Этот генератор вернет '', если файл становится недоступным или нет новых данных.
[обновление]
От второго до последнего ответа кружки вокруг верхней части файла появляются, когда у него заканчиваются данные. - Эли
Я думаю, что второй будет выводить последние десять строк всякий раз, когда заканчивается хвостовой процесс, который с -f
возникает всякий раз, когда возникает ошибка ввода-вывода. Поведение tail --follow --retry
не так уж и мало для большинства случаев, о которых я могу думать в unix-подобных средах.
Возможно, если вы обновите свой вопрос, чтобы объяснить, какова ваша реальная цель (причина, по которой вы хотите имитировать tail -retry), вы получите лучший ответ.
Последний ответ фактически не следует за хвостом и просто читает то, что доступно во время выполнения. - Эли
Конечно, хвост покажет последние 10 строк по умолчанию... Вы можете поместить указатель файла в конец файла с помощью file.seek, я оставил правильную реализацию как упражнение для читателя.
IMHO метод file.read() гораздо шире, чем решение на основе подпроцесса.
Ответ 4
Единственный переносимый способ tail -f
- это, по-видимому, файл читать и повторять (после sleep
), если read
возвращает 0. Утилиты tail
на разных платформах используют специфичные для платформы трюки (например, kqueue
в BSD), чтобы эффективно навсегда закрепить файл без необходимости sleep
.
Следовательно, реализация хорошего tail -f
чисто в Python, вероятно, не очень хорошая идея, так как вам придется использовать реализацию с наименьшим общим знаменателем (не прибегая к хакам платформы). Используя простой subprocess
для открытия tail -f
и итерации по строкам в отдельном потоке, вы можете легко реализовать неблокирующую операцию tail
в Python.
Пример реализации:
import threading, Queue, subprocess
tailq = Queue.Queue(maxsize=10) # buffer at most 100 lines
def tail_forever(fn):
p = subprocess.Popen(["tail", "-f", fn], stdout=subprocess.PIPE)
while 1:
line = p.stdout.readline()
tailq.put(line)
if not line:
break
threading.Thread(target=tail_forever, args=(fn,)).start()
print tailq.get() # blocks
print tailq.get_nowait() # throws Queue.Empty if there are no lines to read
Ответ 5
В идеале, у меня было бы что-то вроде tail.getNewData(), которое я мог бы вызвать каждый раз, когда мне нужно больше данных
У нас уже есть один и его очень хороший. Просто позвоните f.read(), когда вам нужно больше данных. Он начнет чтение, когда предыдущее чтение будет остановлено, и оно будет считываться через конец потока данных:
f = open('somefile.log')
p = 0
while True:
f.seek(p)
latest_data = f.read()
p = f.tell()
if latest_data:
print latest_data
print str(p).center(10).center(80, '=')
Для чтения строки за строкой используйте f.readline(). Иногда считываемый файл заканчивается частично прочитанной строкой. Обработайте этот случай, когда f.tell() находит текущую позицию файла и использует f.seek() для перемещения указателя файла назад к началу неполной строки. См. этот рецепт ActiveState для рабочего кода.
Ответ 6
Вы можете использовать библиотеку 'tailer': https://pypi.python.org/pypi/tailer/
У него есть возможность получить последние несколько строк:
# Get the last 3 lines of the file
tailer.tail(open('test.txt'), 3)
# ['Line 9', 'Line 10', 'Line 11']
И он также может следовать за файлом:
# Follow the file as it grows
for line in tailer.follow(open('test.txt')):
print line
Если вы хотите хвостовое поведение, это кажется хорошим вариантом.
Ответ 7
Другим вариантом является библиотека tailhead
, которая предоставляет как версии Python tail
, так и head
утилит и API, которые могут быть используется в вашем собственном модуле.
Первоначально на основе модуля tailer
его основным преимуществом является возможность отслеживания файлов по пути, то есть он может обрабатывать ситуацию, когда файл воссоздается. Кроме того, у него есть некоторые исправления ошибок для различных случаев краев.
Ответ 8
Все ответы, которые используют хвост -f, не являются питоническими.
Вот питонский способ: (без использования внешнего инструмента или библиотеки)
def follow(thefile):
while True:
line = thefile.readline()
if not line:
time.sleep(0.1)
continue
yield line
if __name__ == '__main__':
logfile = open("run/foo/access-log","r")
loglines = follow(logfile)
for line in loglines:
print(line, end='')
Ответ 9
Вы также можете использовать команду "AWK".
См. Больше на: http://www.unix.com/shell-programming-scripting/41734-how-print-specific-lines-awk.html
awk можно использовать для хвоста последней строки, последних нескольких строк или любой строки в файле.
Это можно вызвать из python.
Ответ 10
Если вы используете linux, вы реализуете неблокирующую реализацию в python следующим образом.
import subprocess
subprocess.call('xterm -title log -hold -e \"tail -f filename\"&', shell=True, executable='/bin/csh')
print "Done"
Ответ 11
Python - это "батареи в комплекте" - у него есть хорошее решение для него: https://pypi.python.org/pypi/pygtail
Считывает строки журнала, которые не были прочитаны. Помнит, где он закончил последний раз, и продолжается оттуда.
import sys
from pygtail import Pygtail
for line in Pygtail("some.log"):
sys.stdout.write(line)
Ответ 12
Адаптация ответа Иджаза Ахмада Кхана к выводу строк только тогда, когда они полностью написаны (строки заканчиваются символом новой строки), дает питонное решение без внешних зависимостей:
def follow(file) -> Iterator[str]:
""" Yield each line from a file as they are written. """
line = ''
while True:
tmp = file.readline()
if tmp is not None:
line += tmp
if line.endswith("\n"):
yield line
line = ''
else:
time.sleep(0.1)
if __name__ == '__main__':
for line in follow(open("test.txt", 'r')):
print(line, end='')