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

Python 如何确保报告管道中的错误,python,bash,Python,Bash,我做一个 python程序必须从DB中获取内容并通过管道将其传输到Scala程序。我们已经为python集中了错误监控,但是scala程序可能会自动失败。因此,如果Scala程序失败,我希望它的stderr通过第三个程序(如bash伪代码)传输到我们的监控系统: python py_prog.py | java scalaProg.Pkg (编写logging_程序并使其与我们的错误监控对话很容易,为scala程序设置类似的系统很难) 那么,我如何做到: 当scalaProg失败时,将其std

我做一个

python程序必须从DB中获取内容并通过管道将其传输到Scala程序。我们已经为python集中了错误监控,但是scala程序可能会自动失败。因此,如果Scala程序失败,我希望它的stderr通过第三个程序(如bash伪代码)传输到我们的监控系统:

python py_prog.py | java scalaProg.Pkg
(编写
logging_程序
并使其与我们的错误监控对话很容易,为scala程序设置类似的系统很难)

那么,我如何做到:

  • 当scalaProg失败时,将其stderr管道传输到第三个程序
  • scalaProg失败时在py_prog中处理
    IOError:[Errno 32]管道破裂
  • 请执行以下操作:

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


    这对您有用吗?

    使用bash,您可以使用进程子替换,并且仍然可以看到
    java scalaProg.Pkg的输出:

    python py_prog.py | java scalaProg.Pkg 2>&1 >/dev/null | python logging_program.py
    
    或者,您也可以将其放置在T形三通上,以查看终端上的stderr:

    python py_prog.py | java scalaProg.Pkg 2> >(python logging_program.py)
    
    {python py_prog.py | java scalaProg.Pkg;} > >(tee >(python logging_program.py)) 2>&1
    
    如果是运行
    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;) 2> >(tee >(python logging_program.py))
    
    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.py
    java scalaProg.Pkg
    获取所有stdout和stderr,请执行以下操作:

    python py_prog.py | (java scalaProg.Pkg;) 2> >(tee >(python logging_program.py))
    
    python py_prog.py | (java scalaProg.Pkg;) > >(tee >(python logging_program.py)) 2>&1
    
    或者这也包括可能由调用shell生成的错误:

    python py_prog.py | java scalaProg.Pkg 2> >(python logging_program.py)
    
    {python py_prog.py | java scalaProg.Pkg;} > >(tee >(python logging_program.py)) 2>&1
    
    如果您只想从会话中获取stderr,那么只需使用
    2>

    (python py_prog.py | java scalaProg.Pkg;) > >(tee >(python logging_program.py)) 2>&1
    
    可以让您完成大部分工作;但仍然存在的一个大问题是断管错误导致
    py_prog.py

    事实证明,为sys.stdout
    捕获损坏的管道是一件棘手的事情,因为有时它们会在关机时发生,但为时已晚

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

    (python py_prog.py | java scalaProg.Pkg;) 2> >(tee >(python logging_program.py))
    
    最后的
    \uuuu name\uuuu='\uuuuu main\uuuu'
    测试下的代码是或已经是我的独立python程序的常用样板文件。根据这个答案,我可能需要更改它

    无论如何,如果我试着用两个“坏”案例运行它,一个是立即退出,另一个是读一点然后退出,它的行为有两种不同的方式。首先,管道进入“立即退出”:

    哇!奇怪!:-)

    事实证明,我可以通过稍微调整一下包装器,使头-1的反常行为(
    lost sys.stderr
    )消失。在调用
    sys.exit
    之前,我需要调用
    sys.stdout.flush()
    (理想情况下,也可以调用
    sys.stderr.flush()
    ,但到目前为止我只测试了这么多):

    $ python badpipe.py | head -1
    line 0
    close failed in file object destructor:
    sys.excepthook is missing
    lost sys.stderr
    
    有了这个,我现在可以可靠地捕捉到最外层的
    IOError
    ,并检查是否有破损的管壳。以下是最终(或多或少)版本,再次包括
    main

    if __name__ == '__main__':
        try:
            ret = main()
        except KeyboardInterrupt:
            ret = '\nInterrupted'
        try:
            sys.stdout.flush()
        finally:
            sys.exit(ret)
    
    捕获
    EPIPE
    后的
    sys.stderr.write
    ,以及更改的
    ret
    值,主要用于说明123没有什么特别之处。另外,我也不知道最终的
    raise
    是否正常工作,因为我还没有测试它

    运行此命令将提供:

    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)
    
    (注意:这都在Python2.7中,但3.2的行为类似。)


    如果您可以修改
    py\u prog.py
    ,那么这一切都很好,但是如果您不能修改呢

    在这种情况下,我建议编写一个包装器脚本(无论用哪种语言,Python都可以)。让您的包装器脚本读取其所有stdin并将其全部复制(即写入)到stdout,但检查(捕获)断开的管道错误。如果出现这种情况,改变策略:阅读stdin的其余部分并将其扔掉,这样
    py_prog.py
    就会愉快地相信它成功地将所有内容发送到stdout并完成。您甚至可以将其写入一个
    subprocess.Popen
    ,该程序运行
    java scalaProg.pkg
    命令,并为您执行所有需要的特殊日志情况

    即使可以修改
    py\u prog.py
    ,您也可能希望编写此包装,具体取决于您希望发生的事情

    $ python badpipe.py | (exit 0)
    caught pipe-based IO-error
    $ python badpipe.py | (head -1)
    line 0
    caught pipe-based IO-error
    $ 
    
    (不过我不会给你写包装纸:-)



    顺便说一句,
    丢失的sys.stderr
    是一个Python错误。一个简单的方法引发它:
    python-c'print“foo\n”*10000'| head-1

    scalaProg
    中的错误与
    py\u prog
    中的管道破裂有什么关系?它发生在
    sys.stdout.write()中
    因为
    scalaProg
    已崩溃,但我希望
    logging\u程序
    仅在
    scalaProg
    失败时运行请解释它检查失败的部分,然后再将其重定向到
    logging\u程序
    请注意scalaProg始终返回stderr,但我们仅在它崩溃时才需要它,在这种情况下,我们需要将其发送到日志记录_program@aitchnyu如果您只能在退出时检测到
    java scalaProg.Pkg
    是否已经出现问题,那么您必须将错误输出临时发送到某个地方,比如缓冲区或文件上。如果您想运行
    python logging\u program.py
    并在它不再运行后读取它的错误消息,这几乎是不可能的。如果你想有一个临时文件解决方案,它可以提供。即使在读取命名管道时,也不能继续或停止正在运行的程序,除非另一端有人从中读取。