Редактировать: Поскольку кажется, что либо решения нет, либо я делаю что-то настолько нестандартное, что никто не знает - я пересмотрю свой вопрос, чтобы также задать: каков наилучший способ ведения журнала, когда приложение 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())
Самое приятное в этом то, что он не требует специальных вызовов print от остальной части кода. Код также выполняет некоторые команды оболочки, так что приятно не иметь дело с каждым их выводом по отдельности.
Просто я хочу сделать то же самое, за исключением дублирования вместо перенаправления.
Сначала я подумал, что простое изменение dup2's должно сработать. Почему это не так? Вот мой тест:
import os, sys
### my broken solution: so = se = open("a.log", 'w', 0) sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
Теперь оператор print будет одновременно выводиться на экран и добавляться в ваш файл журнала:
# prints "1 2" to <stdout> AND log.dat print"%d %d" % (1,2)
Очевидно, что это быстро и грязно. Некоторые примечания:
Вероятно, вам следует параметризовать имя файла журнала.
Вероятно, вам следует вернуть значение sys.stdout в <stdout>, если вы не будете вести журнал в течение всего времени работы программы.
Возможно, вам понадобится возможность записи в несколько файлов журнала одновременно или обрабатывать разные уровни журналов и т.д.
Все это достаточно просто, поэтому мне удобно оставить их в качестве упражнений для читателя. Ключевым моментом здесь является то, что print просто вызывает "файлоподобный объект", который назначен sys.stdout.
Ответ 3
Поскольку вам удобно создавать внешние процессы из своего кода, вы могли бы использовать tee itself . Я не знаю ни одного системного вызова Unix, который делал бы именно то, что делает tee.
# Note this version was written circa Python 2.6, see below for # an updated 3.3+-compatible version. import subprocess, os, sys
# Unbuffer output (this ensures the output is in the correct order) sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE) os.dup2(tee.stdin.fileno(), sys.stdout.fileno()) os.dup2(tee.stdin.fileno(), sys.stderr.fileno())
Вы также можете эмулировать tee с помощью многопроцессорного пакета (или использовать processing, если вы используете Python 2.5 или более ранней версии).
Обновить
Вот версия, совместимая с Python 3.3+:
import subprocess, os, sys
tee = subprocess.Popen(["tee", "log.txt"], stdin=subprocess.PIPE) # Cause tee's stdin to get a copy of our stdin/stdout (as well as that # of any child processes we spawn) os.dup2(tee.stdin.fileno(), sys.stdout.fileno()) os.dup2(tee.stdin.fileno(), sys.stderr.fileno())
# The flush flag is needed to guarantee these lines are written before # the two spawned /bin/ls processes emit any output print("\nstdout", flush=True) print("stderr", file=sys.stderr, flush=True)
# These child processes' stdin/stdout are os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {}) os.execve("/bin/ls", ["/bin/ls"], os.environ)
Ответ 4
Что вам действительно нужно, так это logging модуль из стандартной библиотеки. Создайте регистратор и прикрепите два обработчика, один будет выполнять запись в файл, а другой - в stdout или stderr.