我有一个相当大的Python项目,目前在Linux上运行,但我正在尝试扩展到Windows。我已经将代码简化为一个完整的示例,可以运行来说明我的问题:我有两个类,父和子。首先初始化Parent,创建一个记录器,然后生成一个Child来完成工作:
import logging
import logging.config
import multiprocessing
class Parent( object ):
def __init__(self, logconfig):
logging.config.dictConfig(logconfig)
self.logger = logging.getLogger(__name__)
def spawnChild(self):
self.logger.info('One')
c = Child(self.logger)
c.start()
class Child(multiprocessing.Process):
def __init__(self, logger):
multiprocessing.Process.__init__(self)
self.logger = logger
def run(self):
self.logger.info('Two')
if __name__ == '__main__':
p = Parent({
'version':1,
"handlers": {
"console": {
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
},
"root": {
"level": "DEBUG",
"handlers": [
"console",
]
}
}
)
p.spawnChild()
在linux上(特别是ubuntu 12.04),我得到以下(预期)输出:
user@ubuntu:~$ python test.py
One
Two
但是,在Windows(特别是Windows 7)上,它因为酸洗错误而失败:
C:\>python test.py
<snip>
pickle.PicklingError: Can't pickle <type 'thread.lock'>: it's not found as thread.lock
问题归结为Windows缺少真正的fork,因此在线程之间发送时必须对对象进行pickle。但是,记录器不能被腌制。我已经尝试使用__getstate__和__setstate__来避免酸洗,并在Child中按名称引用:
def __getstate__(self):
d = self.__dict__.copy()
if 'logger' in d.keys():
d['logger'] = d['logger'].name
return d
def __setstate__(self, d):
if 'logger' in d.keys():
d['logger'] = logging.getLogger(d['logger'])
self.__dict__.update(d)
这与以前一样在Linux中运行,现在Windows不会因PicklingError而失败。但是,我的输出仅来自Parent:
C:\>python test.py
One
C:\>
似乎孩子无法使用记录器,尽管没有消息抱怨“无法找到处理程序'__main__'的记录器”或任何其他错误消息。我环顾四周,有办法可以完全重构我登录程序的方式,但这显然是最后的手段。我希望我只是遗漏了一些明显的东西,并且人群的智慧可以向我指出。
答案 0 :(得分:2)
在大多数情况下,Logger
个对象不可选,因为它们在内部使用不可推断的theading.Lock
和/或file
个对象。您尝试过的解决方法确实避免了挑选logger
,但它最终会在子进程中创建一个完全不同的Logger
,它恰好与父进程中的Logger
相同;你logging.config
打电话的效果会丢失。要获得您想要的行为,您需要在子进程中重新创建记录器和重新调用logging.config.dictConfig
:
class Parent( object ):
def __init__(self, logconfig):
self.logconfig = logconfig
logging.config.dictConfig(logconfig)
self.logger = logging.getLogger(__name__)
def spawnChild(self):
self.logger.info('One')
c = Child(self.logconfig)
c.start()
class Child(multiprocessing.Process):
def __init__(self, logconfig):
multiprocessing.Process.__init__(self)
self.logconfig = logconfig
def run(self):
# Recreate the logger in the child
logging.config.dictConfig(self.logconfig)
self.logger = logging.getLogger(__name__)
self.logger.info('Two')
或者,如果您想继续使用__getstate__
/ __setstate__
:
class Parent( object ):
def __init__(self, logconfig):
logging.config.dictConfig(logconfig)
self.logger = logging.getLogger(__name__)
self.logconfig = logconfig
def spawnChild(self):
self.logger.info('One')
c = Child(self.logger, self.logconfig)
c.start()
class Child(multiprocessing.Process):
def __init__(self, logger, logconfig):
multiprocessing.Process.__init__(self)
self.logger = logger
self.logconfig = logconfig
def run(self):
self.logger.info('Two')
def __getstate__(self):
d = self.__dict__.copy()
if 'logger' in d:
d['logger'] = d['logger'].name
return d
def __setstate__(self, d):
if 'logger' in d:
logging.config.dictConfig(d['logconfig'])
d['logger'] = logging.getLogger(d['logger'])
self.__dict__.update(d)