这是我的示例代码
class StreamToLogger(object):
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''
def write(self, buf):
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s:%(levelname)s:%(name)s:%(message)s',
filename="logger.log",
filemode='w'
)
stdout_logger = logging.getLogger('STDOUT')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
stderr_logger = logging.getLogger('STDERR')
sl = StreamToLogger(stderr_logger, logging.ERROR)
sys.stderr = sl
suite = suite()
tests = unittest.TextTestRunner(descriptions=True, verbosity=2).run(suite)
默认情况下,TextTestRunner
将流用作stderr
。我已经使用stderr在日志文件中写入输出并且它正在工作。我们可以在同一个脚本中多次使用stderr
吗?
答案 0 :(得分:1)
我对您的代码进行了新的分析。那里的问题是
当您调用TextTextRunner()而不是默认值时,情况会发生变化,但是如果您通过显式重新定义了sys.stderr。现在输出将被重定向到您的文件:
# does not at all what you want (writes output to stderr):
tests = unittest.TextTestRunner( descriptions=True, verbosity=2 ).run(suite)
# does not everything what you want (writes output to file only):
tests = unittest.TextTestRunner\
( stream=sys.stderr, descriptions=True, verbosity=2 ).run(suite)
满足一个要求(写入文件)但不满足另一个要求。这是因为只使用了一个已定义的流(通过流传递给TextTestRunner的流)。对于第二个要求(也要写入控制台),必须有一种方法可以将一个流(重定向的stderr)写入两个输出设备。我认为只有以适当的方式更改StreamToLogger.write()
方法才能实现这一点:
def write( self, buf ) :
my_console_stream_instance.write(buf)
my_file_stream_instance.write(buf)
其中my_console_stream_instance
是写入控制台的流,my_file_stream_instance
是写入文件的流。所以请看下面的例子,它应该做你想要的:
class StreamToLogger( object ):
def __init__( self ):
self.terminal = sys.stderr
self.log = open( "logger.log", "w" )
def write( self, buf ):
self.terminal.write(buf) # write to stderr
self.log.write(buf) # write to file
sys.stderr = StreamToLogger()
suite = suite()
tests = unittest.TextTestRunner\
( stream=sys.stderr, descriptions=True, verbosity=2 ).run( suite )
现在确实有两个输出:一个到屏幕,一个到文件。
但是为什么需要将sys.stderr传递给TextTestRunner的stream参数?乍一看似乎是一样的。但是在章节function definitions中的Python语言参考中是解释:
当函数定义为时,将计算默认参数值 执行。这意味着表达式被评估一次,当时 定义函数,并使用相同的“预先计算”值 每次通话。这对了解何时来说尤为重要 default参数是一个可变对象。
在执行stderr重定义之前,将评估TextTestRunner中stream参数的默认sys.stderr。这就是为什么我们必须通过我们重新命名的sys.stderr。一个小小的证据是:在导入unittest之前重新定义sys.stderr :代码运行良好而不传递重新定义的sys.stderr。
答案 1 :(得分:0)
对我来说,这不是很清楚,你想做什么。将日志记录模块的所有控制台输出写入文件和控制台?如果是这样的话:
# Create a logging object
logger = logging.getLogger ( 'STDOUT+FILE' )
logger.setLevel ( logging.DEBUG )
# Create a file handler and set level to INFO (or other value)
file_ch = logging.FileHandler( 'logger.log', 'w' )
file_ch.setLevel ( logging.DEBUG )
# ... and add it to the logging object
logger.addHandler( file_ch )
# Create a stream handler and set level to INFO
console_ch = logging.StreamHandler( )
console_ch.setLevel ( logging.DEBUG )
# ... and add it to the logging object
logger.addHandler( console_ch )
# Do some logging
logger.debug( 'Logging Test' )
logger.debug( '------------' )
logger.info( 'this is an info log' )
logger.warn( 'this is a warn log' )
logger.debug( 'this is a debug log' )
logger.error( 'this is an error log' )
logger.critical( 'this is a critical log' )
myLogFile.log 的内容现在是
Logging Test
------------
this is an info log
this is a warn log
this is a debug log
this is a critical log
控制台上显示相同的文字
更多信息here