Изменить: так как кажется, что либо нет решения, или я делаю что-то настолько нестандартное, что никто не знает - я пересмотрю свой вопрос, чтобы также спросить: Каков наилучший способ ведения журнала, когда приложение python много системных вызовов?
Мое приложение имеет два режима. В интерактивном режиме я хочу, чтобы весь вывод выводился на экран, а также в файл журнала, включая вывод любых системных вызовов. В режиме демона весь вывод идет в журнал. Режим демона прекрасно работает, используя os.dup2()
. Я не могу найти способ «все» выводить в журнал в интерактивном режиме, не изменяя каждый системный вызов.
Другими словами, мне нужна функциональность командной строки 'tee' для любого вывода, генерируемого приложением python, включая вывод системного вызова .
Чтобы уточнить:
Чтобы перенаправить весь вывод, я делаю что-то вроде этого, и это прекрасно работает:
# open our log file
so = se = open("%s.log" % self.name, 'w', 0)
# re-open stdout without buffering
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
# redirect stdout and stderr to the log file opened above
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
Хорошая вещь об этом - то, что это не требует специальных вызовов печати от остальной части кода. Код также запускает некоторые команды оболочки, поэтому хорошо не иметь дело с каждым их выводом в отдельности.
Просто я хочу сделать то же самое, за исключением дублирования вместо перенаправления.
Сначала подумал, я подумал, что просто задний dup2
ход должен работать. Почему не так? Вот мой тест:
import os, sys
### my broken solution:
so = se = open("a.log", 'w', 0)
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
os.dup2(sys.stdout.fileno(), so.fileno())
os.dup2(sys.stderr.fileno(), se.fileno())
###
print("foo bar")
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)
Файл «a.log» должен быть идентичен тому, что отображался на экране.
Ответы:
Поскольку вы можете создавать внешние процессы из своего кода, вы можете использовать его
tee
сами. Я не знаю ни одного системного вызова Unix, который делает именно то, чтоtee
делает.Вы также можете эмулировать,
tee
используя многопроцессорный пакет (или использовать обработку, если вы используете Python 2.5 или более раннюю версию).Обновить
Вот Python 3.3 + -совместимая версия:
источник
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
больше не работает с python 3.3 (см. PEP 3116)tee.stdin.close()
в конце моей программы. Я также получаю «ResourceWarning: подпроцесс 1842 все еще работает», и добавлениеsys.stdout.close(); sys.stderr.close()
в конце программы исправляет это.У меня была такая же проблема, и я нашел этот фрагмент очень полезным:
от: http://mail.python.org/pipermail/python-list/2007-May/438106.html
источник
__del__
не вызывается до конца исполнения. См. Stackoverflow.com/questions/6104535/…print
Заявление будет вызыватьwrite()
метод любого объекта , присвоенный sys.stdout.Я бы раскрутил небольшой класс, чтобы написать в двух местах одновременно ...
Теперь
print
оператор будет отображаться на экране и добавляться в ваш файл журнала:Это очевидно быстро и грязно. Некоторые заметки:
<stdout>
если вы не будете входить в систему на протяжении всей программы.Все это достаточно просто, так что мне удобно оставлять их как упражнения для читателя. Ключевым моментом здесь является то, что
print
просто вызывается «подобный файлу объект», который назначенsys.stdout
.источник
The print statement will call the write() method of any object you assign to sys.stdout
. А как насчет других функций, отправляющих данные на стандартный вывод, не использующийсяprint
? Например, если я создаю процесс, используяsubprocess.call
его вывод, он идет в консоль, а не вlog.dat
файл ... есть ли способ это исправить?То, что вы действительно хотите, это
logging
модуль из стандартной библиотеки. Создайте регистратор и присоедините два обработчика, один будет записывать в файл, а другой - в stdout или stderr.См Logging несколько адресатов для получения подробной информации
источник
logging
модуль не будет перенаправлять вывод из системных вызовов, таких какos.write(1, b'stdout')
Вот еще одно решение, которое является более общим, чем другие - оно поддерживает разделение вывода (записанного
sys.stdout
) на любое количество файловоподобных объектов. Там нет требования, которое__stdout__
само включено.ПРИМЕЧАНИЕ. Это подтверждение концепции. Реализация здесь не завершена, так как она только оборачивает методы
write
файловоподобных объектов (например ), опуская members / properties / setattr и т. Д. Однако, это, вероятно, достаточно хорошо для большинства людей в нынешнем виде.Что мне нравится об этом, кроме своей общности, является то , что он чист в том смысле , что не делает никаких прямых вызовов
write
,flush
,os.dup2
и т.д.источник
_wrap
здесь вообще? Не могли бы вы скопировать туда код,__getattr__
и он работает так же?multifile([])
создает файл, который вызываетUnboundLocalError
каждый раз, когда вы вызываете один из его методов. (res
возвращается без назначения)Как описано в другом месте, возможно, лучшее решение - использовать модуль регистрации напрямую:
Однако есть некоторые (редкие) случаи, когда вы действительно хотите перенаправить стандартный вывод. У меня была такая ситуация, когда я расширял команду runserver в django, которая использует print: я не хотел взламывать источник django, но нуждался в операторах print, чтобы перейти в файл.
Это способ перенаправить stdout и stderr из оболочки с помощью модуля logging:
Эту реализацию LogFile следует использовать только в том случае, если вы действительно не можете использовать модуль ведения журнала напрямую.
источник
Я написал
tee()
реализацию на Python, которая должна работать в большинстве случаев, и она также работает в Windows.https://github.com/pycontribs/tendo
Также вы можете использовать его в сочетании с
logging
модулем из Python, если хотите.источник
(Ах, просто перечитайте свой вопрос и увидите, что это не совсем применимо.)
Вот пример программы, которая использует модуль регистрации Python . Этот модуль регистрации был во всех версиях с 2.3. В этом примере регистрация настраивается с помощью параметров командной строки.
В тихом режиме он будет входить только в файл, в обычном режиме - как в файл, так и на консоль.
источник
Чтобы завершить Джон Т ответ: https://stackoverflow.com/a/616686/395687
Я добавил
__enter__
и__exit__
методы, чтобы использовать его в качестве менеджера контекста сwith
ключевым словом, которое дает этот кодЗатем его можно использовать как
источник
__del__
функциональность в__exit__
__del__
это плохая идея. Его следует переместить в функцию «close», которая вызывается в__exit__
.Я знаю, что на этот вопрос неоднократно отвечали, но для этого я взял основной ответ из ответа Джона Т. и изменил его так, чтобы он содержал предложенный сброс и следовал за связанной пересмотренной версией. Я также добавил вход и выход, как указано в ответе cladmi, для использования с оператором with. Кроме того, в документации упоминается очистка файлов с использованием,
os.fsync()
поэтому я также добавил это. Я не знаю, действительно ли вам это нужно, но оно есть.Вы можете использовать его
или
источник
mode="ab"
и вwrite
функцииself.file.write(message.encode("utf-8"))
другое решение с использованием модуля логирования:
источник
Похоже, что ни один из приведенных выше ответов не отвечает поставленной проблеме. Я знаю, что это старый поток, но я думаю, что эта проблема намного проще, чем все делают это:
Теперь это будет повторять все до обычного обработчика sys.stderr и вашего файла. Создайте другой класс
tee_out
дляsys.stdout
.источник
tee=tee_err();tee.write('');tee.write('');...
opens + закрывает файл для каждогоwrite
. См. Stackoverflow.com/q/4867468 и stackoverflow.com/q/164053 для аргументов против этой практики.По просьбе @ user5359531 в комментариях под @John T в ответ , вот копия ссылочного поста к пересмотренной версии связанного обсуждения в этом ответе:
источник
Я пишу скрипт для запуска скриптов cmd-line. (Поскольку в некоторых случаях просто невозможно заменить команду Linux - например, в случае rsync.)
Что я действительно хотел, так это использовать механизм регистрации Python по умолчанию в каждом случае, когда это было возможно, но все равно фиксировать любую ошибку, когда что-то пошло не так, как было непредвиденно.
Этот код, кажется, делает свое дело. Это может быть не особенно элегантно или эффективно (хотя в нем не используется строка + = строка, так что, по крайней мере, у него нет такого потенциального узкого места). Я публикую это на тот случай, если это даст кому-то еще какие-нибудь полезные идеи.
Очевидно, что если вы не так подвержены прихотям, как я, замените LOG_IDENTIFIER другой строкой, которую вы никогда не увидите, чтобы кто-то записывал в журнал.
источник
Если вы хотите записать весь вывод в файл И вывести его в текстовый файл, то вы можете сделать следующее. Это немного глупо, но это работает:
РЕДАКТИРОВАТЬ: Обратите внимание, что это не регистрирует ошибки, если вы не перенаправите sys.stderr в sys.stdout
РЕДАКТИРОВАТЬ 2: Вторая проблема заключается в том, что вы должны передать 1 аргумент в отличие от встроенной функции.
РЕДАКТИРОВАТЬ 3: см. Код, прежде чем записать stdin и stdout в консоль и файл с stderr только собирается в файл
источник
Я написал полную замену
sys.stderr
и просто дублируется код переименованияstderr
для ,stdout
чтобы сделать его также можно заменитьsys.stdout
.Для этого я создаю тот же тип объекта , как текущие
stderr
иstdout
, и вперед все методы к исходной системеstderr
иstdout
:Чтобы использовать это, вы можете просто позвонить
StdErrReplament::lock(logger)
иStdOutReplament::lock(logger)
передать регистратор, который вы хотите использовать для отправки выходного текста. Например:Запустив этот код, вы увидите на экране:
И по содержанию файла:
Если вы также хотите видеть содержимое
log.debug
вызовов на экране, вам нужно добавить обработчик потока в ваш логгер. В этом случае это будет так:Который будет выводить как это при запуске:
Хотя это все равно сохранит это в файл
my_log_file.txt
:При отключении этого с помощью
StdErrReplament:unlock()
он только восстановит стандартное поведениеstderr
потока, поскольку подключенный регистратор никогда не может быть отсоединен, потому что кто-то может иметь ссылку на его более старую версию. Вот почему это глобальный синглтон, который никогда не умрет. Следовательно, в случае перезагрузки этого модуля с помощьюimp
чего-либо еще, он никогда не будет повторно захватывать ток, такsys.stderr
как он уже был введен в него, и сохранять его внутри.источник