2014-10-02 15 views
3

현재 Linux에서 실행되는 다소 큰 Python 프로젝트가 있지만 Windows로 확장하려고합니다. 코드를 내 문제를 설명하기 위해 실행할 수있는 전체 예제로 축소했습니다. 두 클래스 인 Parent and Child가 있습니다. 부모가 먼저 초기화 된 로거를 생성하고 할 수있는 아이로 급부상되는 작업 :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() 

(특히, 우분투 12.04), 나는 다음 (예상) 출력을 얻을 :

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

그러나 (특히, 윈도우 7) 윈도우에서, 그것은 산세 오류와 함께 실패합니다 : 객체가 스레드간에 보낼 때 절인 할 필요가 있으므로

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

문제는 진정한 포크의 윈도우의 부족으로 내려 온다. 그러나 로거는 절인 될 수 없습니다. 나는 아이의 이름으로 산 세척 및 참조를 피하기 위해 __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) 

이것은 이전과 마찬가지로 리눅스에서 작동, 이제 Windows는 PicklingError 실패하지 않을 것이다. 하지만, 내 출력은 부모입니다 :

C:\>python test.py 
One 

C:\> 

아이가 또는 다른 오류 메시지 "어떤 로거 처리기 '__main__'를 찾을 수 없습니다"불평없이 메시지에도 불구하고, 로거를 사용할 수없는 것 같다. 나는 주변을 둘러 보았고 내 프로그램에 로그인하는 방법을 완전히 재구성 할 수있는 방법이 있지만 분명히 마지막 수단입니다. 나는 분명히 무언가를 놓치고 군중의 지혜가 그것을 나에게 지적 할 수 있기를 바라고있다.

+0

'some_dict.keys()'의 key가 정확히이 검사를 수행하는 잘못된 방법입니다. 그것은 python2에서 O (n) 시간이 걸립니다. 그냥'key in some_dict'를 사용하십시오. 문제에 관해서. 하위 프로세스의 stdout이 다를 수 있으므로 출력을 볼 수 없습니다. 파일 처리기를 추가하고 파일의 출력이 올바른지 확인하십시오. – Bakuriu

+0

테스트를 위해 다른 SO 포스트에서 복사 된 키에 대한 메모를 이해합니다. 내 모든 실제 로깅 파일에서 수행되며 문제가 여전히 발생합니다. stdout은 위 스크립트를 작성하기 위해 테스트하기 쉽습니다. "multi_file_handler"를 사용 : { "클래스": "logging.handlers.RotatingFileHandler" "파일 이름": "output.log" 을} - ","하나 \ nTwo "리눅스에 하나, 같은 문제로 연결 "on windows – user2093082

+1

문제는,''__init__''을 unpickling 할 때 보통 * 호출되지 않을 수 있습니다. 즉, 하위 프로세스가'logging.config.dictConfig (...를 호출하지 않았습니다.)'따라서 기본 구성을 사용할 수도 있습니다. '__setstate__' 메소드를 변경하여 올바른 설정으로'dictConfig'를 호출하고 변경 사항이 있는지 확인하십시오. – Bakuriu

답변

2

대부분의 경우 Logger 개체는 unpicklable theading.Lock 및/또는 file 개체를 내부적으로 사용하기 때문에 picklable이 아닙니다. 시도한 해결 방법은 logger을 제거하지 않지만 자식 프로세스에서 완전히 다른 Logger을 생성합니다. 부모 프로세스의 이름은 Logger입니다. logging.config 전화의 영향은 사라집니다.

: 당신이 __getstate__/ __setstate__ 계속 사용하려는 경우,

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') 

을 또는 : 당신이 자식 프로세스에서 로거를 다시 할 필요가해야 할 동작을 얻으려면 logging.config.dictConfig 재 호출

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

+0

@ user2093082 흠, 나는 당신의 질문이나 어디에서 당신이'logging.config.dictConfig'에'Child'의'__init__'에 대한 호출을했다는 언급을 보지 못했습니다. 제가 뭔가를 놓치고 있습니까? 아니면 실제로 여기에 명시 적으로 언급하지 않고 시도 했습니까? – dano

+0

나는 명시 적으로 말하지 않았고, Bakuriu의 의견에 대한 응답으로 먼저 테스트했습니다. – user2093082

관련 문제