2014-10-02 3 views
3

У меня довольно большой проект Python, который в настоящее время работает на Linux, но я пытаюсь перейти на Windows. Я сократил код до полного примера, который можно запустить, чтобы проиллюстрировать мои проблемы: у меня есть два класса: «Родительский и детский». Родитель инициализируется первым, создает регистратор, и порождает ребенка, чтобы сделать работу:Python Logging with Multiprocessing in Windows

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 (в частности, убунту 12,04), я получаю следующее (ожидаемый) результат:

[email protected]:~$ 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' истинной вилки, так предметы должны быть маринованными при отправке между потоками. Но регистратор не может быть маринован. Я попытался с помощью __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

'if key in some_dict.keys()' - это точно * неправильный способ выполнения этой проверки. Это займет время O (n) в python2. Просто используйте 'if key in some_dict'. Что касается вашей проблемы. Подпроцесс может иметь различный stdout и, следовательно, вы не видите вывод. Попробуйте добавить обработчик файла и проверить правильность вывода в файле. – Bakuriu

+0

Оцените примечание о ключе, которое было просто скопировано из другого сообщения SO для тестирования. Все мои фактические протоколирования выполняются в файлах, и проблема все еще возникает. Для создания вышеуказанного скрипта stdout было просто проще протестировать. Использование "multi_file_handler": { "класс": "logging.handlers.RotatingFileHandler", "имя файла": "output.log" }, приводит к той же проблеме - "One \ Ntwo" на Linux, «One "на окнах – user2093082

+1

Проблема может заключаться в том, что при распаковке' __init__' обычно вызывается * not *. Это означает, что подпроцесс * не * вызывает 'logging.config.dictConfig (...) 'и, следовательно, он может использовать конфигурацию по умолчанию. Попытайтесь изменить метод '__setstate__', чтобы он вызывал' dictConfig' с правильными настройками и видел, что-то меняется. – Bakuriu

ответ

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) 
+0

Это похоже на комментарий Бакуриу выше, хотя он также указал, что unpickling не вызывает \ __ init__, поэтому мое размещение logging.config .dictConfig (self.logconfig) в \ __ init__ не работает. Он не работает в моем фактическом проекте, но работает в моем тестовом коде. Я продолжу настройку и закрою это, если найду решение здесь. – user2093082

+0

@ user2093082 Хмм, я не вижу нигде в вашем вопросе или комментариев, где вы сказали, что вы поместили вызов 'logging.config.dictConfig' в' __init__' 'Child'. Я что-то упустил, или вы попробовали это без фактического упоминания здесь здесь? – dano

+0

Я не сказал этого явно, я сначала его протестировал в ответ на комментарии от Bakuriu. – user2093082