问题描述
我一直在尝试编写一个程序来记录子进程的未捕获异常和语法错误.容易,对吧?只需将 stderr
管道传输到正确的位置.
I've been trying to write a program that logs uncaught exceptions and syntax errors for a subprocess. Easy, right? Just pipe stderr
to the right place.
然而,子进程是另一个 python 程序——我将其称为 test.py
——它需要像没有捕获其输出/错误一样运行.也就是说,运行记录器程序需要让用户看起来像正常运行 python test.py
.
However, the subprocess is another python program- I'll call it test.py
- that needs to run as if its output/errors are not being captured. That is, running the logger program needs to seem like the user has just run python test.py
as normal.
使问题更复杂的是 问题 raw_input
实际上被发送如果未使用 readline
,则转至 stderr
.不幸的是,我不能只import readline
,因为我无法控制正在使用我的错误记录器运行的文件.
Further complicating the issue is the problem that raw_input
actually gets sent to stderr
if readline
is not used. Unfortunately, I can't just import readline
, since I don't have control over the files that are being run using my error logger.
注意事项:
- 我在运行此代码的机器上受到相当大的限制.我无法安装
pexpect
或编辑*customize.py
文件(因为该程序将由许多不同的用户运行).我真的觉得无论如何应该有一个标准库解决方案...... - 这只适用于 Mac.
- 这样做的动机是,我是一个研究新程序员遇到的错误的团队的一员.
- I am fairly restricted on the machines that this code will run on. I can't install
pexpect
or edit the*customize.py
files (since the program will be run by a lot of different users). I really feel like there should be a stdlib solution anyway though... - This only has to work on macs.
- The motivation for this is that I'm part of a team researching the errors that new programmers get.
我尝试了以下方法,没有成功:
I've tried the following methods, without success:
- 只使用
tee
作为问题 如何在使用tee"时将标准错误写入文件使用管道?(未能产生raw_input
提示);我在几个 SO 问题中发现的tee
的 python 实现有类似的问题 - 覆盖
sys.excepthook
(未能使其适用于子进程) - 这个问题的最佳答案似乎很有希望,但未能正确显示
raw_input
提示. - 日志模块 似乎对实际写入日志文件很有用,但并没有似乎没有抓住问题的症结
- 自定义标准错误阅读器
- 无休止的谷歌搜索
- just using
tee
as in the question How do I write stderr to a file while using "tee" with a pipe? (failed to produceraw_input
prompts); python implementations oftee
that I found in several SO questions had similar issues - overwriting
sys.excepthook
(failed to make it work for a subprocess) - this question's top answer seemed promising, but it failed to display
raw_input
prompts correctly. - the logging module seems useful for actually writing to a log file, but doesn't seem to get at the crux of the issue
- custom stderr readers
- endless googling
推荐答案
根据@nneonneo 在问题评论中的建议,我制作了这个似乎可以完成工作的程序.(请注意,目前,记录器文件的名称必须使用pylog"才能将错误正确打印给最终用户.)
Based on @nneonneo 's advice in the question comments, I made this program that seems to get the job done. (Note that currently, the name of the logger file has to by "pylog" for the errors to be printed correctly to the end user.)
#!/usr/bin/python
'''
This module logs python errors.
'''
import socket, os, sys, traceback
def sendError(err):
# log the error (in my actual implementation, this sends the error to a database)
with open('log','w') as f:
f.write(err)
def exceptHandler(etype, value, tb):
"""An additional wrapper around our custom exception handler, to prevent errors in
this program from being seen by end users."""
try:
subProgExceptHandler(etype, value, tb)
except:
sys.stderr.write('Sorry, but there seems to have been an error in pylog itself. Please run your program using regular python.
')
def subProgExceptHandler(etype, value, tb):
"""A custom exception handler that both prints error and traceback information in the standard
Python format, as well as logs it."""
import linecache
errorVerbatim = ''
# The following code mimics a traceback.print_exception(etype, value, tb) call.
if tb:
msg = "Traceback (most recent call last):
"
sys.stderr.write(msg)
errorVerbatim += msg
# The following code is a modified version of the trackeback.print_tb implementation from
# cypthon 2.7.3
while tb is not None:
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
# Filter out exceptions from pylog itself (eg. execfile).
if not "pylog" in filename:
msg = ' File "%s", line %d, in %s
' % (filename, lineno, name)
sys.stderr.write(msg)
errorVerbatim += msg
linecache.checkcache(filename)
line = linecache.getline(filename, lineno, f.f_globals)
if line:
msg = ' ' + line.strip() + '
'
sys.stderr.write(msg)
errorVerbatim += msg
tb = tb.tb_next
lines = traceback.format_exception_only(etype, value)
for line in lines:
sys.stderr.write(line)
errorVerbatim += line
# Send the error data to our database handler via sendError.
sendError(errorVerbatim)
def main():
"""Executes the program specified by the user in its own sandbox, then sends
the error to our database for logging and analysis."""
# Get the user's (sub)program to run.
try:
subProgName = sys.argv[1]
subProgArgs = sys.argv[3:]
except:
print 'USAGE: ./pylog FILENAME.py *ARGS'
sys.exit()
# Catch exceptions by overriding the system excepthook.
sys.excepthook = exceptHandler
# Sandbox user code exeuction to its own global namespace to prevent malicious code injection.
execfile(subProgName, {'__builtins__': __builtins__, '__name__': '__main__', '__file__': subProgName, '__doc__': None, '__package__': None})
if __name__ == '__main__':
main()
这篇关于记录 python 子进程的语法错误和未捕获的异常并将它们打印到终端的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!