2

我做一个

python py_prog.py | java scalaProg.Pkg

python 程序必须从 DB 中获取内容并将其通过管道传输到 Scala 程序。我们对 python 进行了集中式错误监控,但 scala 程序可能会默默地失败。因此,如果 Scala 程序失败,我希望它的标准错误转移到我们的监控系统,并使用第三个程序,如 bash 伪代码:

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

(编写logging_program并使其与我们的错误监控对话很容易,为 scala 程序设置类似的系统很难)。

那么我该如何完成:

  1. 当 scalaProg 失败时,将其标准错误传递给第三个程序
  2. IOError: [Errno 32] Broken pipescalaProg 失败时在 py_prog 中的处理
4

3 回答 3

1

使用 bash,您可以使用过程替代,您仍然会看到以下输出java scalaProg.Pkg

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

或者您也可以将其放在 tee 上以在终端上查看 stderr:

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

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

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

如果您需要获取所有内容(stdout 和 stderr都可以java scalaProg.Pkg这样做:

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

如果您想同时获取所有 stdout 和 stderrpython py_prog.pyjava scalaProg.Pkg执行以下操作:

{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

如果您只想从会话中获取标准错误,那么只需使用2>

(python py_prog.py | java scalaProg.Pkg;) 2> >(tee >(python logging_program.py))
于 2013-08-08T06:48:52.120 回答
1

Tadeck 的回答可以帮助您完成大部分工作;但是剩下的一个大问题是破坏管道错误杀死了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.writeafter catchEPIPE和 changed value 主要是为了说明——123没什么特别的ret。另外我不知道最终是否raise正确,因为我没有测试过。

运行这个给出:

$ python badpipe.py | (exit 0)
caught pipe-based IO-error
$ python badpipe.py | (head -1)
line 0
caught pipe-based IO-error
$ 

(注意:这一切都在 python 2.7 中,但 3.2 的行为类似。)


如果您可以修改,这一切都很好py_prog.py,但是如果您不能修改呢?

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

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

python py_prog.py | python wrap_java_thing.py

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


顺便说一句,这lost sys.stderr是一个 Python 错误,问题 11380。一个简单的方法来激怒它:python -c 'print "foo\n"*10000' | head -1

于 2013-08-08T07:19:22.863 回答
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 脚本。

那对你有用吗?

于 2013-08-08T06:20:24.927 回答