Мне нужно что-то вроде того, что делает команда tee в системах на базе Linux, но в чистом коде Python.
Я попробовал использовать этот фрагмент:
import sys
# Define the file name for logging
log_file_name = 'output.log'
# Open the file in append mode
log_file = open(log_file_name, 'a')
# Function to log output to both console and file
def log(message):
# Write to console
print(message)
# Write to file
log_file.write(message + '\n')
# Flush the buffer to ensure the message is written immediately
log_file.flush()
# Redirect stdout and stderr to the log function
sys.stdout.write = log
sys.stderr.write = log
print("Hello world!")
Однако я получаю эту ошибку:
object address : 0x7f88e6b1c1c0
object refcount : 2
object type : 0x9e14c0
object type name: RecursionError
object repr : RecursionError('maximum recursion depth exceeded')
lost sys.stderr
и всякий раз, когда я комментирую оператор печати в функции log, он записывает сообщение в файл журнала, но не выводит сообщение на консоль. Почему это происходит и как я могу это исправить?






Вот один из способов:
import sys
_print = print
def print(*args, **kwargs):
_print(*args, **kwargs, file=open('output.log','w'))
_print(*args, **kwargs)
print("Hello world!")
Функция print() уже имеет аргумент для назначения файла. https://docs.python.org/3/library/functions.html#print
Мы сохранили исходную функцию печати в _print() и вызываем ее из нашей функции печати, чтобы удовлетворить обе наши потребности. Таким образом, вызывающий код не нужно менять.
print в Python записывает данное сообщение в стандартный вывод.
Причина, по которой вы получаете эту ошибку, заключается в том, что вы вернулись sys.stdout.write к своей log функции, но внутри этой log функции также есть вызов print. Строка print(message) теперь снова вызывает stdout.write, которая является функцией log. Это происходит рекурсивно, пока вы не достигнете максимальной глубины рекурсии, которая по умолчанию в Python равна 1000.
Я не думаю, что вам стоит заниматься тем, чем вы сейчас занимаетесь. Перезаписывая sys.stdout.write, вы взламываете свой собственный вход в Python, что вполне может сломать любые используемые вами пакеты или, по крайней мере, сильно запутать всех, с кем вы работаете.
Вероятно, было бы проще просто написать что-то вроде следующего и заменить операторы print на операторы output в вашем коде.
def output(message):
# To console
print(message)
# To file
with open(log_file_name, 'a') as file:
file.write(message + '\n')
Обратите внимание, что вы также не закрывали файл после записи в него, что потенциально может привести к повреждению данных. См., например, это объяснение, почему with высказывания — ваш лучший друг!
Но если возможно, использование tee, вероятно, будет самым простым решением.
Обновлено:
loggingНа самом деле, такая функциональность также возможна с использованием модуля logging из стандартной библиотеки, для более готового к использованию решения.
Следующая конфигурация записывает данные как в консоль, так и в файл каждый раз, когда используется регистратор test:
LOGGING = {
"version": 1,
"handlers": {
"console": {
"class": "logging.StreamHandler",
},
"file": {
"class": "logging.FileHandler",
"filename": "debug.log",
},
},
"loggers": {
"test": {
"handlers": ["console", "file"],
},
},
}
The Problem:Проблема возникает из-за того, что оператор печати также выводит данные в sys.stdout, что приводит к рекурсивному вызову функции при обнаружении вывода в потоке stdout.
Solution:Чтобы решить эту проблему, создается класс LoggingWrapper, сохраняющий исходные потоки stdout и stderr. Этот класс изменяет stdout и stderr для использования оболочки, гарантируя, что данные, выводимые в эти потоки, направляются в исходные потоки, а также регистрируются в файле журнала.
import sys
class LoggingWrapper:
def __init__(self, original_stream):
self.original_stream = original_stream
def write(self, message):
# Write to the original stream
self.original_stream.write(message)
# Write to the log file
log_file.write(message)
# Flush the buffer to ensure the message is written immediately
log_file.flush()
def flush(self):
# Ensure the original stream is flushed
self.original_stream.flush()
# Define the file name for logging
log_file_name = 'output.log'
# Open the file in append mode
log_file = open(log_file_name, 'a')
# Create wrappers for stdout and stderr
stdout_wrapper = LoggingWrapper(sys.stdout)
stderr_wrapper = LoggingWrapper(sys.stderr)
# Redirect stdout and stderr to the wrappers
sys.stdout = stdout_wrapper
sys.stderr = stderr_wrapper
Это решение гарантирует, что сообщения будут записываться в указанный файл журнала, сохраняя при этом исходное поведение stdout и stderr. Это предотвращает ошибки рекурсии, отделяя процесс регистрации от стандартного потока вывода.