2014-10-02 25 views
3

我有一個相當大的Python項目,當前在Linux上運行,但我試圖擴展到Windows。我已經將代碼縮減爲可以運行以說明我的問題的完整示例:我有兩個類,Parent和Child。家長首先被初始化,創建一個記錄器,併產生一個子做的工作:在Windows中使用多處理功能進行Python日誌記錄

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),我得到以下(預期)輸出:

[email protected]:~$ python test.py 
One 
Two 

但在Windows(具體地,視窗7),它失敗酸洗錯誤:

C:\>python test.py 
<snip> 
pickle.PicklingError: Can't pickle <type 'thread.lock'>: it's not found as thread.lock 

的問題歸結爲Windows的缺乏真正的叉的,所以對象具有線程之間發送時,進行酸洗。但是,記錄器不能被醃製。我已經使用__getstate__和__setstate__避免兒童酸洗和參考的名字嘗試:

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。然而,我的產量也只有從父:

C:\>python test.py 
One 

C:\> 

看來,孩子是無法使用記錄儀,儘管沒有消息抱怨「沒有記錄可以發現處理‘__main__’」或任何其他錯誤消息。我環顧四周,我有辦法徹底改變我登錄程序的方式,但這顯然是最後的手段。我希望我只是錯過了一些明顯的東西,而且人羣的智慧可以向我指出。

+0

'如果some_dict.keys()中的鍵正好是執行該檢查的錯誤方式。它在python2中花費O(n)時間。只需使用'if key in some_dict'。關於你的問題。子進程可能有不同的標準輸出,因此你看不到輸出。嘗試添加文件處理程序並檢查文件中的輸出是否正確。 – Bakuriu 2014-10-02 19:25:45

+0

欣賞關於該鍵的註釋,該註釋剛剛從另一個SO帖子複製以進行測試。 我所有的實際日誌都是在文件中完成的,問題仍然存在。對於創建上述腳本,stdout更容易測試。使用 「multi_file_handler」:{ 「類」: 「logging.handlers.RotatingFileHandler」, 「文件名」: 「output.log」 }, 導致了同樣的問題 - 在Linux上 「一\ nTwo」,「一「在窗口 – user2093082 2014-10-02 19:31:38

+1

問題可能是,通常不會調用'__init__'時取消調用。這意味着子進程沒有*調用'logging.config.dictConfig(...)',因此它可能使用默認配置。嘗試改變'__setstate__'方法,以便使用正確的設置調用'dictConfig'並查看是否有更改。 – Bakuriu 2014-10-02 19:35:47

回答

2

在大多數情況下,Logger對象不是picklable,因爲它們使用unpicklable 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) 
+0

這是類似於@ Bakuriu的評論上面,雖然他還指出unpickling不會調用\ __ init__,這就是爲什麼我放置logging.config \ __ init__中的.dictConfig(self.logconfig)失敗。這不是在我的實際項目中工作,而是在測試代碼中工作。如果我能從這裏找到解決方案,我會繼續調整並關閉它。 – user2093082 2014-10-02 19:51:53

+0

@ user2093082嗯,我沒有在你的問題中看到任何地方,或者你說你在'Child'的'__init__'中調用'logging.config.dictConfig'的註釋。我是否錯過了一些東西,或者你沒有在這裏明確提及它而嘗試過? – dano 2014-10-02 20:00:02

+0

我沒有這麼明確地說,我首先對Bakuriu的評論進行了測試 – user2093082 2014-10-02 20:10:06

相關問題