【问题标题】:Python try/finally wtih sys.exc_info()Python 尝试/最终使用 sys.exc_info()
【发布时间】:2021-10-02 15:40:31
【问题描述】:

我正在尝试编写一个 python 作业,我将在所有其他作业中使用它来捕获作业开始时间,当程序成功完成或出现一些问题时,我会捕获错误详细信息并将详细信息加载到一张桌子。

job_status

def srt(jobname):
   sts = 'running'
   df_srt = select(f'''select /'{job_name}/' as job_name, /'{strt_time}/' as strt_time, /'{sts}/' as sts''')
   df_srt.write.to_csv(/path_name)

def end(jobname):
   df_end = select(f'''select /'{job_name}/' as job_name, /'{strt_time}/' as strt_time, /'{end_time}/', /'{log}/' as log, /'{sts}/' as sts ''')
   df_end.write.to_csv(/path_name)

我正在使用的程序

from job_status import *

    def main_program:
       try:
          # some operation
          print(1/0)
       except:
         pass
       finally:
          if sys.exc_info()[0] is not None:
                status = 'Failed'
                log = concat(sys.exc_info()[0],' , ', sys.exc_info()[1], ' , ', sys.exc_info()[2])
                end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                end(job_name=__name__, strt_time=start_time, end_time=end_time, sts=sts, log=log)
            else:
                sts = 'Success'
                end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                end(job_name=__name__, start_time=start_time, end_time=end_time, status=status)

每当发生问题时,我都会尝试捕获错误详细信息。但无论错误的性质如何, sys.exc_info()[0] 都会进入成功路径。任何想法,我该如何实现?

【问题讨论】:

标签: python exception logging


【解决方案1】:

(注意:注释表明 OP 中的代码并非旨在捕获异常;而是仅记录成功或失败,并让原始异常传播。这里的代码有已被修改以实现这一点。

try 命令包括三种类型的附加子句:exceptelsefinally

  • "except" [expression ["as" identifier]]

    如果提供了表达式,则如果抛出与表达式匹配的异常,则执行该子句。如果没有提供表达式,则在抛出任何异常时执行该子句。

  • else

    如果没有抛出异常,则执行该子句。

  • finally

    该子句在try 块的末尾执行,在任何匹配的exceptelse 子句之后。

(请参阅 [注 1] 了解准确的语法。有关详细信息,请参阅Python docs。)

sys.exc_info 可用于except 子句(或在执行except 子句期间调用的函数中)以提取有关异常的信息。一旦except 子句完成,sys.exc_info 将不再返回有用的信息。由于finally 子句在except 子句完成后执行,您的代码就是这种情况。

但是你试图用if exc_info is not None: 做的正是try 语句的else 子句所做的。因此,您可以使用else 子句更简单地编写代码。我做了一些重构,以便能够将通用代码放在 finally 子句中。

为了传播异常,必须使用不带表达式的 raise 语句来结束 except 子句。这将导致异常继续“就好像它是由 try 块引发的一样”。但是,finally 子句在传播之前仍会执行。

def main_program:
    start_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    try:
        # some operation
        print(1/0)
    except:
        status = 'Failed'
        log = concat(sys.exc_info()[0],' , ', sys.exc_info()[1], ' , ', sys.exc_info()[2])
        raise
    else:
        status = 'Success'
        log = None
    finally:
        end_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        end(job_name=__name__, start_time=start_time, end_time=end_time, sts=sts, log=log)

注意事项:

  1. try 语句的精确语法表明,您必须至少有一个except 子句或一个finally 子句,并且如果您有一个except,则只能有一个else 子句条款。从文档:
    try_stmt  ::=  try1_stmt | try2_stmt
    try1_stmt ::=  "try" ":" suite
                   ("except" [expression ["as" identifier]] ":" suite)+
                   ["else" ":" suite]
                   ["finally" ":" suite]
    try2_stmt ::=  "try" ":" suite
                   "finally" ":" suite
    
  2. 我不知道concat 应该在这里做什么,所以我就让它保持原样。它不能是一个简单的字符串连接,因为sys.exc_info 返回的元组的组件不是字符串,如果那是您正在寻找的,', '.join(map(str, exc_info())) 会起作用。如果是某个函数对sys.exc_info 返回的对象进行了很好的格式化,那么您可以考虑将sys.exc_info 返回的对象传递给它。

【讨论】:

  • 谢谢,我的工作中一个非常重要的功能是,我想捕获所有这些错误消息并将它们记录下来,但我仍然想让工作失败。由于这些作业由调度程序运行,因此如果其中一个作业失败,则必须停止整个工作流程。
  • @user7343922:您的问题并不清楚该要求,因此您可能应该将其添加到问题中。您的代码也不满足该要求,因为一旦您捕获到异常,它就会被捕获; except: pass 表示异常不会通过。如果您想传递异常,则需要对我的代码稍作修改。
  • 结构有效,但是当我尝试捕获错误消息时,例如“除了:status = 'Failed' log = ''.join(traceback.format_stack())”,我没有得到“ZeroDivisionError:除以零”和行号。我究竟做错了什么?我只是得到调用程序名称(我正在使用驱动程序调用这个程序)和这条消息“log = .join(traceback.format_stack())”
  • @user7343922: log = ".join(...)"log 设置为字符串常量;它不执行任何操作。我认为您正在寻找的是log = '\n'.join(traceback.format_stack());除了您可能想从异常中获得更多信息。
  • 但最简单的可能是log = traceback.format_exc()。这将为您提供一个包含回溯和异常消息的字符串(因此不需要join)。
【解决方案2】:

sys.exc_info() 包含当前正在处理的异常的信息。来自docs(强调我的):

此函数返回一个包含三个值的元组,这些值提供有关当前正在处理的异常的信息。 [...]如果当前堆栈帧未处理异常,则从调用堆栈帧或其调用者获取信息,依此类推,直到找到正在处理异常的堆栈帧。 这里,“处理异常”被定义为“执行一个 except 子句”。 对于任何堆栈帧,只有有关当前正在处理的异常的信息是可访问的。如果堆栈上的任何地方都没有处理异常,则返回一个包含三个 None 值的元组。

finally 子句中,您已经过了处理异常的点。因此,您需要从except 子句的inside 中保存有关异常的相关信息。例如:

exc_info = None
try:
    # some operation
    print(1/0)
except:
    exc_info = sys.exc_info()
finally:
    if exc_info is not None:
        status = 'Failed'
        # ...
    else:
        status = 'Success'
        # ...

【讨论】:

  • 如果我将“finallly”中的代码块移动到“except”,那么我只会在出现异常时捕获状态和日志,不是吗?但是,即使作业成功运行,我也想记录状态和日志。关于如何做到这一点的任何建议?
  • @user7343922 您可以通过多种方式构建它 - 以我的编辑为例。
  • 结构有效,但是,我无法正确捕获 sys.exc_info 消息。我试图“str(sys.exc_info()[0])”,它不工作。还有一个重要的问题,这可能是一个奇怪的请求,即使在捕获所有错误消息之后,我怎么能让工作仍然失败。因为,所有这些文件都由调度程序运行,如果作业失败,则必须停止整个工作流程。我只想捕获错误消息并让作业失败。
猜你喜欢
  • 1970-01-01
  • 2010-09-21
  • 2013-11-17
  • 1970-01-01
  • 2021-05-17
  • 2014-11-05
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
相关资源
最近更新 更多