Как скопировать sys.stdout в файл журнала?

Просто установите NodeJS на свой локальный компьютер (убедитесь, что соответствующая запись добавлена ​​в PATH) и добавьте

gem 'execjs'

в Gemfile .

139
задан martineau 4 January 2019 в 18:31
поделиться

8 ответов

Так как Вы - удобные мечущие икру внешние процессы от своего кода, Вы могли использовать tee самого. Я не знаю ни о каких системных вызовах 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())

print "\nstdout"
print >>sys.stderr, "stderr"
os.spawnve("P_WAIT", "/bin/ls", ["/bin/ls"], {})
os.execve("/bin/ls", ["/bin/ls"], os.environ)

Вы могли также эмулировать tee использование многопроцессорная обработка пакет (или использование обработка при использовании Python 2.5 или ранее).

Обновление

Вот является Python 3.3 +-compatible версией:

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)
50
ответ дан Jacob Gabrielson 4 January 2019 в 18:31
поделиться

print оператор будет звонить write() метод любого объекта, который Вы присваиваете sys.stdout.

я вращал бы небольшой класс записи к двум местам сразу...

import sys

class Logger(object):
    def __init__(self):
        self.terminal = sys.stdout
        self.log = open("log.dat", "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)  

sys.stdout = Logger()

Теперь print оператор и отзовется эхом на экран и добавит к Вашему файлу журнала:

# prints "1 2" to <stdout> AND log.dat
print "%d %d" % (1,2)

Это очевидно быстро-и-грязно. Некоторые примечания:

  • Вы, вероятно, должны parametize имя файла журнала.
  • необходимо, вероятно, вернуться sys.stdout к <stdout>, если Вы не будете регистрироваться на время программы.
  • можно хотеть способность записать в несколько файлов журнала сразу или обработать различные уровни журнала, и т.д.

Они все достаточно просты, что я - удобный отъезд их как упражнения для читателя. Ключевое понимание здесь - то, что print просто вызовы "подобный файлу объект" это присвоено sys.stdout.

72
ответ дан Triptych 4 January 2019 в 18:31
поделиться

То, что Вы действительно хотите, logging модуль из стандартной библиотеки. Создайте регистратор и присоедините два обработчика, можно было бы записать в файл и другой к stdout или stderr.

См. Регистрироваться нескольким местам назначения для деталей

64
ответ дан Serge Stroobandt 4 January 2019 в 18:31
поделиться

Я имел эту ту же проблему прежде и нашел этот отрывок очень полезным:

class Tee(object):
    def __init__(self, name, mode):
        self.file = open(name, mode)
        self.stdout = sys.stdout
        sys.stdout = self
    def __del__(self):
        sys.stdout = self.stdout
        self.file.close()
    def write(self, data):
        self.file.write(data)
        self.stdout.write(data)
    def flush(self):
        self.file.flush()

от: http://mail.python.org/pipermail/python-list/2007-May/438106.html

132
ответ дан Andrej Debenjak 4 January 2019 в 18:31
поделиться

(А-ч, просто перечитал Ваш вопрос и видит, что это не вполне применяется.)

Вот пример программы, который делает использование Python, регистрирующий модуль . Этот модуль входа был во всех версиях с тех пор 2.3. В этом образце вход настраивается параметрами командной строки.

Во вполне режиме это только зарегистрируется в файл, в нормальном режиме это зарегистрируется и в файл и в консоль.

import os
import sys
import logging
from optparse import OptionParser

def initialize_logging(options):
    """ Log information based upon users options"""

    logger = logging.getLogger('project')
    formatter = logging.Formatter('%(asctime)s %(levelname)s\t%(message)s')
    level = logging.__dict__.get(options.loglevel.upper(),logging.DEBUG)
    logger.setLevel(level)

    # Output logging information to screen
    if not options.quiet:
        hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(formatter)
        logger.addHandler(hdlr)

    # Output logging information to file
    logfile = os.path.join(options.logdir, "project.log")
    if options.clean and os.path.isfile(logfile):
        os.remove(logfile)
    hdlr2 = logging.FileHandler(logfile)
    hdlr2.setFormatter(formatter)
    logger.addHandler(hdlr2)

    return logger

def main(argv=None):
    if argv is None:
        argv = sys.argv[1:]

    # Setup command line options
    parser = OptionParser("usage: %prog [options]")
    parser.add_option("-l", "--logdir", dest="logdir", default=".", help="log DIRECTORY (default ./)")
    parser.add_option("-v", "--loglevel", dest="loglevel", default="debug", help="logging level (debug, info, error)")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet", help="do not log to console")
    parser.add_option("-c", "--clean", dest="clean", action="store_true", default=False, help="remove old log file")

    # Process command line options
    (options, args) = parser.parse_args(argv)

    # Setup logger format and output locations
    logger = initialize_logging(options)

    # Examples
    logger.error("This is an error message.")
    logger.info("This is an info message.")
    logger.debug("This is a debug message.")

if __name__ == "__main__":
    sys.exit(main())
8
ответ дан Atlas1j 4 January 2019 в 18:31
поделиться

Как описано в другом месте, возможно, лучшим решением является использование модуля ведения журнала напрямую:

import logging

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')
logging.info('this should to write to the log file')

Однако в некоторых (редких) случаях вы действительно хочет, чтобы перенаправлял стандартный вывод. У меня была такая ситуация, когда я расширял команду django runserver, которая использует print: я не хотел взламывать исходный код django, но мне нужны были операторы печати для перехода к файлу.

Это способ перенаправления stdout и stderr из оболочки с помощью модуля регистрации:

import logging, sys

class LogFile(object):
    """File-like object to log text using the `logging` module."""

    def __init__(self, name=None):
        self.logger = logging.getLogger(name)

    def write(self, msg, level=logging.INFO):
        self.logger.log(level, msg)

    def flush(self):
        for handler in self.logger.handlers:
            handler.flush()

logging.basicConfig(level=logging.DEBUG, filename='mylog.log')

# Redirect stdout and stderr
sys.stdout = LogFile('stdout')
sys.stderr = LogFile('stderr')

print 'this should to write to the log file'

Вы должны использовать эту реализацию LogFile только в том случае, если вы действительно не можете использовать модуль регистрации напрямую.

12
ответ дан 4 November 2019 в 09:09
поделиться

другое решение с использованием модуля ведения журнала:

import logging
import sys

log = logging.getLogger('stdxxx')

class StreamLogger(object):

    def __init__(self, stream, prefix=''):
        self.stream = stream
        self.prefix = prefix
        self.data = ''

    def write(self, data):
        self.stream.write(data)
        self.stream.flush()

        self.data += data
        tmp = str(self.data)
        if '\x0a' in tmp or '\x0d' in tmp:
            tmp = tmp.rstrip('\x0a\x0d')
            log.info('%s%s' % (self.prefix, tmp))
            self.data = ''


logging.basicConfig(level=logging.INFO,
                    filename='text.log',
                    filemode='a')

sys.stdout = StreamLogger(sys.stdout, '[stdout] ')

print 'test for stdout'
4
ответ дан 4 November 2019 в 09:09
поделиться

Я написал реализацию tee() в Python, которая должна работать для большинства случаев, и она также работает на Windows.

https://github.com/pycontribs/tendo

Также, при желании, вы можете использовать его в сочетании с logging модулем из Python.

11
ответ дан 4 November 2019 в 09:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: