如何确保报告管道中的错误

时间:2013-08-08 05:53:32

标签: python bash

我做了

python py_prog.py | java scalaProg.Pkg

python程序必须从DB中获取内容并将其传递给Scala程序。我们为python集中了错误监控但scala程序可能默默地失败。因此,如果Scala程序失败,我希望它的stderr转移到我们的监控系统,使用第三个程序,如bash伪代码:

(python py_prog.py | java scalaProg.Pkg) || python logging_program.py

(写logging_program并让它与我们的错误监控对话很容易,为scala程序设置一个类似的系统很难。

那我该如何完成:

  1. 当scalaProg失败时,将其stderr传递给第三个程序
  2. 在scalaProg失败时在py_prog中处理IOError: [Errno 32] Broken pipe

3 个答案:

答案 0 :(得分:1)

使用bash,您可以使用流程替代,但仍会看到java scalaProg.Pkg的输出:

python py_prog.py | java scalaProg.Pkg 2> >(python logging_program.py)

或者你可以将它放在发球台上以便在终端上看到stderr:

python py_prog.py | java scalaProg.Pkg 2> >(tee >(python logging_program.py))

如果运行java scalaProg.Pkg的shell发送错误消息,您可以将其封装在子shell中以获取错误:

python py_prog.py | (java scalaProg.Pkg;) 2> >(tee >(python logging_program.py))

如果您需要获取所有内容(java scalaProg.Pkg的stdout和stderr都执行此操作:

python py_prog.py | java scalaProg.Pkg > >(tee >(python logging_program.py)) 2>&1

或者这个:

python py_prog.py | (java scalaProg.Pkg;) > >(tee >(python logging_program.py)) 2>&1

如果你想从python py_prog.pyjava scalaProg.Pkg获取所有stdout和stderr,请执行以下操作:

{python py_prog.py | java scalaProg.Pkg;} > >(tee >(python logging_program.py)) 2>&1

或者这包括可能从调用shell生成的错误:

(python py_prog.py | java scalaProg.Pkg;) > >(tee >(python logging_program.py)) 2>&1

如果您只想从会话中获取stderr,那么只需使用2>

(python py_prog.py | java scalaProg.Pkg;) 2> >(tee >(python logging_program.py))

答案 1 :(得分:1)

Tadeck's answer可以帮助你完成大部分工作;但剩下的一个大问题就是管道错误导致py_prog.py失效。

事实证明,抓住sys.stdout的破损管道很棘手,因为有时它们会在关机时发生,因为它太晚了。

如果my_prog.py相对干净,你可以用一些特殊的样板包装它。让我们说,为了便于说明,它看起来像这个简单的程序:

$ cat badpipe.py
import sys

def main():
    for i in range(1000):
        print 'line', i
    return 0

if __name__ == '__main__':
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        sys.exit('\nInterrupted')

最后__name__ == '__main__'测试下的代码是 - 或者一直是 - 我常用的独立python程序的样板。事实证明,根据这个答案,我可能需要改变它。

无论如何,如果我尝试用两个“坏”情况运行它,一个立即退出,一个读取然后退出,它表现出两种不同的方式。首先,管道进入“立即退出”:

$ python badpipe.py | (exit 0)
Traceback (most recent call last):
  File "badpipe.py", line 10, in <module>
    sys.exit(main())
  File "badpipe.py", line 5, in main
    print 'line', i
IOError: [Errno 32] Broken pipe

这就是我的预期。但是:

$ python badpipe.py | head -1
line 0
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

哇!辣妈! : - )

事实证明,通过稍微调整我的包装,我可以通过lost sys.stderr获得奇怪的行为(head -1)。而不仅仅是sys.exit(main()),我需要调用sys.stdout.flush()(理想情况下,也可能sys.stderr.flush(),但到目前为止我只测试了这么多),然后再调用sys.exit

if __name__ == '__main__':
    try:
        ret = main()
    except KeyboardInterrupt:
        ret = '\nInterrupted'
    try:
        sys.stdout.flush()
    finally:
        sys.exit(ret)

有了这个,我现在可以可靠地抓住最外面的IOError并检查一个破裂的情况。这是最终的(或多或少)版本,包括main

import errno, sys

def main():
    for i in range(1000):
        print 'line', i
    return 0

if __name__ == '__main__':
    ret = 0
    try:
        try:
            ret = main()
        except KeyboardInterrupt:
            ret = '\nInterrupted'
        finally:
            sys.stdout.flush()
    except IOError as err:
        if err.errno == errno.EPIPE:
            sys.stderr.write('caught pipe-based IO-error\n')
            ret = 123 # or whatever
        else:
            raise # some other I/O error; attempt to get a traceback
    finally:
        sys.exit(ret)

sys.stderr.write之后EPIPE以及ret值的变化,主要是为了说明 - 123没有什么特别之处。另外我不知道最后的{{1}因为我没有测试它,所以工作正确。

运行此命令:

raise

(注意:这完全在python 2.7中,但3.2表现相似。)


如果您可以修改$ python badpipe.py | (exit 0) caught pipe-based IO-error $ python badpipe.py | (head -1) line 0 caught pipe-based IO-error $ ,这一切都很好,但是如果你不能做什么呢?

在这种情况下,我建议编写一个包装器脚本(无论使用何种语言,Python都可以正常工作)。让你的包装器脚本读取它的所有stdin并将它全部复制(即写入)到stdout,但检查(捕获)损坏的管道错误。如果发生这种情况,更改策略:读取stdin的其余部分并将其丢弃,以便py_prog.py愉快地相信它设法将所有内容发送到stdout并完成。您甚至可以将其写入运行py_prog.py命令的subprocess.Popen,并为您执行所有所需的特殊日志记录。

即使可以修改java scalaProg.pkg,您也可能想要编写此包装器,具体取决于您希望发生的事情。

py_prog.py

(我不会为你编写包装器:-))


顺便说一下,python py_prog.py | python wrap_java_thing.py 是一个Python错误,issue 11380。激发它的一种简单方法:lost sys.stderr

答案 2 :(得分:0)

执行以下操作:https://stackoverflow.com/a/2342841/548696

python py_prog.py | java scalaProg.Pkg 2>&1 >/dev/null | python logging_program.py

这将从stdout中删除任何内容,并且只将stderr传递给Python脚本。

这对你有用吗?