从另一个Python脚本调用Python脚本时,Python日志记录挂起
我在python日志类中观察到了这个奇怪的问题,我有两个脚本,一个是从另一个脚本调用的。第一个脚本等待其他脚本结束,而其他脚本使用从另一个Python脚本调用Python脚本时,Python日志记录挂起,python,python-2.7,logging,subprocess,deadlock,Python,Python 2.7,Logging,Subprocess,Deadlock,我在python日志类中观察到了这个奇怪的问题,我有两个脚本,一个是从另一个脚本调用的。第一个脚本等待其他脚本结束,而其他脚本使用logging.info 下面是代码片段 #!/usr/bin/env python import subprocess import time import sys chars = ["/","-","\\","|"] i = 0 command = 'sudo python /home/tejto/test/writeIssue.py' process = s
logging.info
下面是代码片段
#!/usr/bin/env python
import subprocess
import time
import sys
chars = ["/","-","\\","|"]
i = 0
command = 'sudo python /home/tejto/test/writeIssue.py'
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
while process.poll() is None:
print chars[i],
sys.stdout.flush()
time.sleep(.3)
print "\b\b\b",
sys.stdout.flush()
i = (i + 1)%4
output = process.communicate()
另一个脚本是
#!/usr/bin/env python
import os
import logging as log_status
class upgradestatus():
def __init__(self):
if (os.path.exists("/tmp/updatestatus.txt")):
os.remove("/tmp/updatestatus.txt")
logFormatter = log_status.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
logger = log_status.getLogger()
logger.setLevel(log_status.DEBUG)
fileHandler = log_status.FileHandler('/tmp/updatestatus.txt', "a")
fileHandler.setLevel(log_status.DEBUG)
fileHandler.setFormatter(logFormatter)
logger.addHandler(fileHandler)
consoleHandler = log_status.StreamHandler()
consoleHandler.setLevel(log_status.DEBUG)
consoleHandler.setFormatter(logFormatter)
logger.addHandler(consoleHandler)
def status_change(self, status):
log_status.info(str(status))
class upgradeThread ():
def __init__(self, link):
self.upgradethreadstatus = upgradestatus()
self.upgradethreadstatus.status_change("Entered upgrade routine")
procoutput = 'very huge logs, mine were 145091 characters'
self.upgradethreadstatus.status_change(procoutput)
self.upgradethreadstatus.status_change("Exiting upgrade routine")
if __name__ == '__main__':
upgradeclass = upgradeThread(sys.argv[1:]
如果我运行第一个脚本,两个脚本都会挂起,问题似乎在于process.poll()为None时的代码,如果我对这段代码进行注释,一切都会正常工作。(无法将此与我的问题联系起来!!)
PS我也试着调试python日志类,在其中我发现进程被StreamHandler
类的emit函数卡住了,它在stream.write
函数调用中卡住了,并且在写了大量日志后没有出现,但是我的退出日志没有出现
那么,这些脚本中出现死锁情况的问题是什么呢
编辑1(带线程的代码)
script.py
脚本2.py
在这种情况下,t.is_alive函数没有返回false(不知道,但是启动函数已经返回,所以理想情况下它应该返回false!!:()标准输出缓冲区阻塞
process.communicate()
调用未执行,因为它在
while process.poll()为None:
。因此writeIssue.py
试图将太多字节写入stdout
,并且所有字节都在子流程.PIPE
中得到缓冲,在调用communicate
之前不会从管道中拉出
缓冲区大小有限。当缓冲区已满时,stream.write
将被阻止
直到缓冲区有空间。如果缓冲区从未清空(如中发生的情况)
然后进程会死锁
解决方法是在缓冲区完全填满之前调用communicate()
。您可以通过在线程中启动writeIssue.py
并在主线程中运行while thread is alive循环时并发调用communicate()
来实现
script.py:
import subprocess
import time
import sys
import threading
def launch():
command = ['python', 'script2.py']
process = subprocess.Popen(command)
process.communicate()
t = threading.Thread(target=launch)
t.start()
chars = ["/","-","\\","|"]
i = 0
while t.is_alive():
print chars[i],
sys.stdout.flush()
time.sleep(.3)
print "\b\b\b",
sys.stdout.flush()
i = (i + 1)%4
t.join()
import sys
import logging
import time
class UpgradeStatus():
def __init__(self):
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
self.logger = logging.getLogger()
self.logger.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
consoleHandler.setFormatter(logFormatter)
self.logger.addHandler(consoleHandler)
def status_change(self, status):
self.logger.info(str(status))
class UpgradeThread():
def __init__(self, link):
self.upgradethreadstatus = UpgradeStatus()
self.upgradethreadstatus.status_change("Entered upgrade routine")
for i in range(5):
procoutput = 'very huge logs, mine were 145091 characters'
self.upgradethreadstatus.status_change(procoutput)
time.sleep(1)
self.upgradethreadstatus.status_change("Exiting upgrade routine")
if __name__ == '__main__':
upgradeclass = UpgradeThread(sys.argv[1:])
script2.py:
import subprocess
import time
import sys
import threading
def launch():
command = ['python', 'script2.py']
process = subprocess.Popen(command)
process.communicate()
t = threading.Thread(target=launch)
t.start()
chars = ["/","-","\\","|"]
i = 0
while t.is_alive():
print chars[i],
sys.stdout.flush()
time.sleep(.3)
print "\b\b\b",
sys.stdout.flush()
i = (i + 1)%4
t.join()
import sys
import logging
import time
class UpgradeStatus():
def __init__(self):
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
self.logger = logging.getLogger()
self.logger.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
consoleHandler.setFormatter(logFormatter)
self.logger.addHandler(consoleHandler)
def status_change(self, status):
self.logger.info(str(status))
class UpgradeThread():
def __init__(self, link):
self.upgradethreadstatus = UpgradeStatus()
self.upgradethreadstatus.status_change("Entered upgrade routine")
for i in range(5):
procoutput = 'very huge logs, mine were 145091 characters'
self.upgradethreadstatus.status_change(procoutput)
time.sleep(1)
self.upgradethreadstatus.status_change("Exiting upgrade routine")
if __name__ == '__main__':
upgradeclass = UpgradeThread(sys.argv[1:])
请注意,如果有两个线程同时写入标准输出,则输出
如果您想避免这种情况,那么所有输出都应该由
带有队列的单线程。所有其他希望
写入输出应将字符串或日志记录推送到
要处理的专用输出线程
然后,该输出线程可以使用for循环从队列中提取输出:
for message from iter(queue.get, None):
print(message)
标准液缓冲液便秘
process.communicate()
调用未执行,因为它在
while process.poll()为None:
。因此writeIssue.py
试图将太多字节写入stdout
,并且所有字节都在子流程.PIPE
中得到缓冲,在调用communicate
之前不会从管道中拉出
缓冲区大小有限。当缓冲区已满时,stream.write
将被阻止
直到缓冲区有空间。如果缓冲区从未清空(如中发生的情况)
然后进程会死锁
解决方法是在缓冲区完全填满之前调用communicate()
。您可以通过在线程中启动writeIssue.py
并在主线程中运行while thread is alive循环时并发调用communicate()
来实现
script.py:
import subprocess
import time
import sys
import threading
def launch():
command = ['python', 'script2.py']
process = subprocess.Popen(command)
process.communicate()
t = threading.Thread(target=launch)
t.start()
chars = ["/","-","\\","|"]
i = 0
while t.is_alive():
print chars[i],
sys.stdout.flush()
time.sleep(.3)
print "\b\b\b",
sys.stdout.flush()
i = (i + 1)%4
t.join()
import sys
import logging
import time
class UpgradeStatus():
def __init__(self):
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
self.logger = logging.getLogger()
self.logger.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
consoleHandler.setFormatter(logFormatter)
self.logger.addHandler(consoleHandler)
def status_change(self, status):
self.logger.info(str(status))
class UpgradeThread():
def __init__(self, link):
self.upgradethreadstatus = UpgradeStatus()
self.upgradethreadstatus.status_change("Entered upgrade routine")
for i in range(5):
procoutput = 'very huge logs, mine were 145091 characters'
self.upgradethreadstatus.status_change(procoutput)
time.sleep(1)
self.upgradethreadstatus.status_change("Exiting upgrade routine")
if __name__ == '__main__':
upgradeclass = UpgradeThread(sys.argv[1:])
script2.py:
import subprocess
import time
import sys
import threading
def launch():
command = ['python', 'script2.py']
process = subprocess.Popen(command)
process.communicate()
t = threading.Thread(target=launch)
t.start()
chars = ["/","-","\\","|"]
i = 0
while t.is_alive():
print chars[i],
sys.stdout.flush()
time.sleep(.3)
print "\b\b\b",
sys.stdout.flush()
i = (i + 1)%4
t.join()
import sys
import logging
import time
class UpgradeStatus():
def __init__(self):
logFormatter = logging.Formatter("%(asctime)s [%(levelname)-5.5s] %(message)s")
self.logger = logging.getLogger()
self.logger.setLevel(logging.DEBUG)
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.DEBUG)
consoleHandler.setFormatter(logFormatter)
self.logger.addHandler(consoleHandler)
def status_change(self, status):
self.logger.info(str(status))
class UpgradeThread():
def __init__(self, link):
self.upgradethreadstatus = UpgradeStatus()
self.upgradethreadstatus.status_change("Entered upgrade routine")
for i in range(5):
procoutput = 'very huge logs, mine were 145091 characters'
self.upgradethreadstatus.status_change(procoutput)
time.sleep(1)
self.upgradethreadstatus.status_change("Exiting upgrade routine")
if __name__ == '__main__':
upgradeclass = UpgradeThread(sys.argv[1:])
请注意,如果有两个线程同时写入标准输出,则输出
如果您想避免这种情况,那么所有输出都应该由
带有队列的单线程。所有其他希望
写入输出应将字符串或日志记录推送到
要处理的专用输出线程
然后,该输出线程可以使用for循环从队列中提取输出:
for message from iter(queue.get, None):
print(message)
明白了!这就是我要找的信息…你知道我是否可以清除(清空)已经运行的进程的缓冲区,以便stream.write可以继续运行吗?当使用stdout=subprocess.PIPE
调用subprocess.Popen
时,通过调用process.stdout.read()可以“清除”stdout管道
。直接调用read()
的问题(例如在主线程的循环中)解决这个问题至少有两种方法:在线程中调用read
,这就是communicate所做的,也就是上面的代码,或者使用select。select
轮询stdout和stderr以确定是否有要读取的内容。有一个例子使用后一种方法。@unutbu…我试过你的example但它对我不起作用,,我的代码和你的代码之间的区别是我没有使用“范围(5)中的for i:”,而是在procoutput中使用一个非常大的字符串并记录它…script2.py仍然被卡住…但是在记录最后一个日志“退出升级例程”后,脚本现在被卡住了尽可能避免使用shell=True
,因为它可能是。如果使用shell=True
,则command
应为字符串。如果使用shell=False
,则command
应为列表。此外,scipt2.py中存在缩进错误。shell=True
和command=['python','script2.py']
,在Unix上执行的命令是/bin/sh-c python script2.py
。试试看;你会看到它挂起,因为script2.py
正在传递给/bin/sh
,而不是python
,因此你只需要一个python解释器等待输入。python过程永远不会结束。如果你使用shell=True
,但要使用command=“python script2.py”
,则执行的命令是/bin/sh-c“python脚本”