正确的日志模块清理fork(多处理模块)

时间:2018-06-08 17:53:30

标签: python logging multiprocessing locking

将'logging'模块与'multiprocessing'模块一起使用时遇到问题。分叉的孩子最终会锁定并挂起任何日志尝试。

记录器正在使用StreamHandler进行sys.stdout和sys.stderr以及SysLogHandler(它显然从其父进程中进行了主动锁定)。

该计划如下:

  • worker被实现为线程(Thread)
  • 工人执行的工作是子流程(流程)

我一直在各个地方获得锁定:logging._lock,SysLogHandler.lock,这取决于我尝试清理模块。

一般来说,我不喜欢与API之外的模块进行交互,但在这种情况下我大胆尝试过:

cur_logger = logging.getLogger()
cur_logger.handlers = []
logging._lock = RLock()
logging._handlerList = []
logging._handlers = {}
logging.Logger.manager.loggingDict = {}

使用以下结果(仍然锁定):

#0  0x00007fc87bceba00 in sem_wait () from /lib64/libpthread.so.0
#1  0x00007fc87bff8428 in PyThread_acquire_lock (lock=0x921970, waitflag=1) at Python/thread_pthread.h:349
#2  0x00007fc87bffc314 in lock_PyThread_acquire_lock (self=0x7fc87c3ce120, args=<value optimized out>) at Modules/threadmodule.c:47
#3  0x00007fc87bfd09d4 in call_function (f=<value optimized out>, throwflag=<value optimized out>) at Python/ceval.c:3794
#4  PyEval_EvalFrameEx (f=<value optimized out>, throwflag=<value optimized out>) at Python/ceval.c:2453
#5  0x00007fc87bfd2647 in PyEval_EvalCodeEx (co=0x7fc87c3f1828, globals=<value optimized out>, locals=<value optimized out>, args=<value optimized out>, argcount=1, kws=0x7fc85c003fd0, kwcount=0, defs=0x7fc87c3666e8, defcount=1, closure=0x0) at Python/ceval.c:3044

我使用相当旧的软件包,所以我的问题是它是否是以后修复的已知错误,或者有一种方法可以清除fork上的日志记录模块,以便它清除所有锁(AFAIK这些不是系统锁在叉子之后他们是无关紧要的,或者我可能做错了事。

谢谢,

版本:

  • python:python-2.6.6-66.el6_8.x86_64
  • 记录。版本:0.5.0.5
  • 多处理。版本:0.70a1

Edit_1:

我在CentOS 7上做了相同的测试,它有更好的python调试器输出,并且由于SysLogHandler.lock(_RLock__count = 1)而挂起:

  • 我应该专注于跟踪带入孩子的SyLogHandler实例
  • 或者还有另一个适合'多处理'的日志记录
  • 或锁是由我的代码
  • 引起的

调试:

#0  0x00007f4fa0e7b89c in __lll_lock_wait_private () from /lib64/libc.so.6
#1  0x00007f4fa0e94fad in _L_lock_774 () from /lib64/libc.so.6
#2  0x00007f4fa0e94d65 in __check_pf () from /lib64/libc.so.6
#3  0x00007f4fa0e56fd9 in getaddrinfo () from /lib64/libc.so.6
#4  0x00007f4f98b85c92 in setipaddr (name=<optimized out>, addr_ret=addr_ret@entry=0x7f4f82ff9cf0, addr_ret_size=addr_ret_size@entry=16, af=af@entry=2)
at /usr/src/debug/Python-2.7.5/Modules/socketmodule.c:921
#5  0x00007f4f98b8781e in getsockaddrarg (s=s@entry=0x7f4f896aadb0, args=<optimized out>, addr_ret=addr_ret@entry=0x7f4f82ff9cf0, len_ret=len_ret@entry=0x7f4f82ff9c80)
at /usr/src/debug/Python-2.7.5/Modules/socketmodule.c:1321
#6  0x00007f4f98b87c63 in sock_sendto (s=0x7f4f896aadb0, 
args=('<159>Jun 11 14:55:08 xdc.py.Distribute file as xdcrm.139979476948736: DEBUG [16722.139979476948736] _put:Timing SFTP stat+put of /tmp/blob.1M.bin.tmp to 10.67.145.141:/tmp/blob.1M.bin.tmp finished:True size:1048576 time:timer: 0.0349\x00', ('localhost', 514))) at /usr/src/debug/Python-2.7.5/Modules/socketmodule.c:2936
#7  0x00007f4fa1b45cf0 in call_function (oparg=<optimized out>, pp_stack=0x7f4f82ff9ea0) at /usr/src/debug/Python-2.7.5/Python/ceval.c:4408
#8  PyEval_EvalFrameEx (
f=f@entry=Frame 0x7f4f824d5a00, for file /usr/lib64/python2.7/logging/handlers.py, line 863, in emit (self=<SysLogHandler(socket=<_socketobject at remote 0x7f4f8900e980>, level=0, lock=<_RLock(_Verbose__verbose=False, _RLock__owner=139979476948736, _RLock__block=<thread.lock at remote 0x7f4fa1fd4210>, _RLock__count=1) at remote 0x7f4f887bf2d0>,

1 个答案:

答案 0 :(得分:1)

TLDR:使用QueueHandler(子级)和QueueListener(父级):https://docs.python.org/3/howto/logging-cookbook.html#dealing-with-handlers-that-block

我使用Python 3.5.2和(现在)内置的多处理和日志记录模块观察到了相同的问题。

经过一番挖掘,事实证明这个问题已经存在了一段时间:https://bugs.python.org/issue6721

在3.7.4中尝试修复Python锁:https://bugs.python.org/issue36533。在测试了“固定”版本之后,我立即在文件I / O对象中偶然发现了另一个死锁。这对刷新数据有一个锁定。问题本质上是相同的-分叉的子进程继承了获得的锁而无法释放它。除了这一点发生在C代码中,这使得调试:o

更加困难

我最终通过内置的队列记录帮助器使用了队列。