Python异常信息捕获

910
import ftplib
import urllib2
import os
import logging
logger = logging.getLogger('ftpuploader')
hdlr = logging.FileHandler('ftplog.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)
FTPADDR = "some ftp address"

def upload_to_ftp(con, filepath):
    try:
        f = open(filepath,'rb')                # file to send
        con.storbinary('STOR '+ filepath, f)         # Send the file
        f.close()                                # Close file and FTP
        logger.info('File successfully uploaded to '+ FTPADDR)
    except, e:
        logger.error('Failed to upload to ftp: '+ str(e))

这似乎不起作用,我得到了语法错误,记录所有类型的异常到文件的正确方法是什么?


3
你的缩进有问题。并且在except后面省略逗号。 - Sven Marnach
4
如果你在except之后省略逗号,你会得到global name 'e' is not defined的错误提示,这并没有比语法错误好多少。请注意不要改变原意。 - Val
20
@Val: 根据Python的版本,应该是 except Exception as eexcept Exception, e - Sven Marnach
1
可能在这8个答案中的某个地方,但是当你打开一个文件时,关闭部分不应该在try语句中,而应该在finally语句中或者被with语句包装。 - user4396006
你可以像requests包中的UnitTests一样做 https://fixexception.com/requests/expected-exception/ - Ivan Borshchov
15个回答

1202
你必须定义你想要捕获的异常类型。所以写成except Exception, e:而不是except, e:来捕获一般的异常(无论如何都会被记录)。
另一种可能性是以这种方式编写整个try/except代码块:
try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to %s', FTPADDR)
except Exception, e: # work on python 2.x
    logger.error('Failed to upload to ftp: %s', e)

在Python 3.x和现代版本的Python 2.x中,使用except Exception as e代替except Exception, e
try:
    with open(filepath,'rb') as f:
        con.storbinary('STOR '+ filepath, f)
    logger.info('File successfully uploaded to '+ FTPADDR)
except Exception as e: # work on python 3.x
    logger.error('Failed to upload to ftp: %s', e)

255
repr(e)返回异常(以及消息字符串);str(e)仅返回消息字符串。 - whitebeard
20
作为替代记录异常的方法,您可以使用 logger.exception(e)。它将在相同的 logging.ERROR 级别下记录带有回溯信息的异常。 - mbdevpl
9
在Python 3中,写作except Exception as e:才是正确的语法。你遇到的语法错误是预期的行为。 - Charlie Parker
36
@CharlieParker 在 Python3 中写成 except Exception as e: - eumiro
3
在2020年,这有点误导人,因为没有人再使用需要你的第一种方式的Python版本(没有人使用Python <2.7),然而你的回答让它看起来像是在Python 2中默认的方法,尽管第二种方法几乎总是更好。你应该删除第一种方式,从而使答案更清晰。 - Alex
显示剩余2条评论

383

这个语法在Python 3中不再支持,应使用以下语法代替。

try:
    do_something()
except BaseException as e:
    logger.error('Failed to do something: ' + str(e))

3
实际上,你应该使用 logger.error('无法执行某些操作:%s',str(e)) 这样,如果你的日志记录器级别高于错误级别,它就不会执行字符串插值。 - avyfain
12
@avyfain,你的说法是错误的。语句“logging.error('foo %s', str(e))”将始终将'e'转换为字符串。要实现你想要的功能,应该使用“logging.error('foo %s', e)”这样的语句,这样允许日志框架进行(或不进行)转换。 - user1898811
4
作为替代记录异常的方法,您可以改用logger.exception(e)。它将以相同的logging.ERROR级别记录带有回溯信息的异常。 - mbdevpl
2
我认为他/她的意思是“除了 Exception,e:”。 - Roy
18
请注意,except BaseExceptionexcept Exception不是同一级别的。在Python3中,except Exception确实可以工作,但它无法捕获例如KeyboardInterrupt这样的异常(如果您想能够中断代码,则这可能非常方便!),而BaseException则可以捕获任何异常。有关异常层次结构,请参见此链接。 - jeannej
显示剩余3条评论

94

如果你想要错误类、错误信息和堆栈跟踪,使用 sys.exc_info()

一些格式化的最小工作代码:

import sys
import traceback

try:
    ans = 1/0
except BaseException as ex:
    # Get current system exception
    ex_type, ex_value, ex_traceback = sys.exc_info()

    # Extract unformatter stack traces as tuples
    trace_back = traceback.extract_tb(ex_traceback)

    # Format stacktrace
    stack_trace = list()

    for trace in trace_back:
        stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))

    print("Exception type : %s " % ex_type.__name__)
    print("Exception message : %s" %ex_value)
    print("Stack trace : %s" %stack_trace)

生成以下输出:

Exception type : ZeroDivisionError
Exception message : division by zero
Stack trace : ['File : .\\test.py , Line : 5, Func.Name : <module>, Message : ans = 1/0']

函数sys.exc_info()可以向您提供有关最近异常的详细信息,它返回一个元组(type, value, traceback)

traceback是traceback对象的一个实例。您可以使用提供的方法格式化跟踪信息,在traceback文档中找到更多信息。


6
使用 e.__class__.__name__ 可以返回异常类。 - kenorb
1
谢谢提供的信息,最终决定使用self.logger.info("{} - {}".format(type(error).__name__, str(error))),效果非常好。 - MadHatter
1
哇,真是一团糟啊...要花这么多功夫才能得到错误堆栈跟踪... - undefined

65

有些情况下您可以使用 e.message 或者 e.messages。但并非所有情况都适用。无论如何,更安全的做法是使用 str(e)

try:
  ...
except Exception as e:
  print(e.message)

65
问题在于,例如,如果你使用except Exception as e,并且e是一个IOError,则可以获得e.errnoe.filenamee.strerror,但很显然没有e.message(至少在Python 2.7.12中是如此)。如果你想捕获错误消息,请使用str(e),就像其他答案中所示。 - epalm
@epalm 如果在 Exception 之前捕获 IOError 呢? - Herii
2
@HeribertoJuárez 为什么要捕获特殊情况,而不直接将其转换为字符串呢? - HosseyNJF

54

为logger更新一些更简单的内容(适用于Python 2和3)。您不需要traceback模块。

import logging

logger = logging.Logger('catch_all')

def catchEverythingInLog():
    try:
        ... do something ...
    except Exception as e:
        logger.error(e, exc_info=True)
        ... exception handling ...

这是现在的老方法(虽然仍然可行):

import sys, traceback

def catchEverything():
    try:
        ... some operation(s) ...
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        ... exception handling ...

exc_value是错误信息。


3
这是我的首选方法。我猜打印字符串可能对于日志记录有用,但如果我需要对这些信息进行任何操作,我需要的不仅仅是一个字符串。 - sulimmesh
3
第二个例子中不需要导入traceback,对吗? - starikoff

24

您可以使用logger.exception("msg")记录带有追踪信息的异常:

try:
    #your code
except Exception as e:
    logger.exception('Failed: ' + str(e))

巧合的是,e.msgException 类的字符串表示形式。 - MarkHu
6
直接使用 logger.exception(e) - mbdevpl

24

使用 str(e)repr(e) 表示异常时,你不会得到实际的堆栈跟踪,因此对于查找异常出现的位置并不有帮助。

在阅读其他答案和日志记录包文档后,以下两种方法非常适合打印实际的堆栈跟踪以便更容易调试:

使用带有参数 exc_infologger.debug()

try:
    # my code
except SomeError as e:
    logger.debug(e, exc_info=True)

使用 logger.exception()

或者我们可以直接使用logger.exception()输出异常。

try:
    # my code
except SomeError as e:
    logger.exception(e)

21

13

您可以尝试明确指定BaseException类型。然而,这将只捕获派生自BaseException的异常。虽然这包括所有提供的实现异常,但也可能引发任意旧式类。

try:
  do_something()
except BaseException, e:
  logger.error('Failed to do something: ' + str(e))

13

如果您想查看原始的错误信息,(文件名行号)

import traceback
try:
    print(3/0)
except Exception as e:    
    traceback.print_exc() 

如果您不使用try-except,那么这将向您显示相同的错误消息。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接