Я пишу класс-обертку для использования с менеджером рабочих процессов. Я хочу логировать вывод из приложения (дочерний процесс, выполняемый через subprocess.Popen
) определенным образом:
stdout
дочернего процесса должен идти в лог файл и в stdout
родительского, stderr
дочернего процесса должен идти в другой лог файл, но также в stdout
родительского. Т.е. весь вывод от дочернего процесса должен в итоге сливаться на stdout
(как в subprocess.Popen(..., stderr=subprocess.STDOUT)
, так что я могу зарезервировать stderr
для сообщений журнала от самой обертки. С другой стороны, дочерние потоки должны идти в разные файлы, чтобы обеспечить отдельную валидацию.
Я пробовал использовать вспомогательный класс "Tee", чтобы связать два потока (stdout
и файл журнала) вместе, так что Tee.write
пишет в оба потока. Однако это не может быть передано в Popen
, потому что "подпроцесс" использует функции уровня ОС для записи (см. здесь: http://bugs.python.org/issue1631).
Проблема с моим текущим решением (фрагмент кода ниже, адаптированный в основном из здесь) заключается в том, что вывод на stdout
может появляться не в правильном порядке.
Как я могу это преодолеть? Или я должен использовать совершенно другой подход?
(Если я буду придерживаться приведенного ниже кода, как мне выбрать значение для количества байт в os.read
?)
import subprocess, select, sys, os
call = ... # set this
process = subprocess.Popen(call, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
logs = {process.stdout: open("out.log", "w"), process.stderr: open("err.log", "w")}
done = {process.stdout: False, process.stderr: False}
while (process.poll() is None) or (not all(done.values())):
ready = select.select([process.stdout, process.stderr], [], [])[0]
for stream in ready:
data = os.read(stream.fileno(), 1)
if data:
sys.stdout.write(data)
logs[stream].write(data)
else:
done[stream] = True
logs[process.stdout].close()
logs[process.stderr].close()
Кстати, это решение с использованием "fcntl" у меня не сработало. И я пока не смог понять, как адаптировать это решение к моему случаю, поэтому не пробовал.