str(exc)默认不换行是因为它仅拼接args且忽略\n,而traceback.format_exception()等才能保留原始换行结构。

为什么 str 异常信息默认不换行
Python 中抛出异常时,str(exc) 返回的是异常的 args 拼接结果(通常是空格分隔的单字符串),不保留原始换行符。即使你在 raise ValueError("line1\nline2") 里写了 \n,str(exc) 也可能把它压成一行——尤其在某些日志框架或 IDE 的异常展示逻辑中会进一步 strip 或重格式化。
用 traceback.format_exception() 获取带换行的完整文本
真正保留结构化换行的,是标准库的 traceback 模块。它把异常对象转为多行字符串,含类型、消息、堆栈,且原样保留 \n。
实操建议:
- 不要直接
str(exc),改用traceback.format_exception(type(exc), exc, exc.__traceback__) - 返回的是字符串列表,需
''.join(...)合并 - 如果只想要异常消息部分(不含堆栈),可用
traceback.format_exception_only(type(exc), exc),它也保留\n - 注意:
exc.__traceback__在异常未被激活(比如刚构造未抛出)时为None,此时传None给format_exception会报错,应改用format_exception_only
自定义 __str__ 时显式处理换行
如果你在自定义异常类里重写了 __str__,必须自己确保返回值含换行符且不被意外替换。
常见错误写法:return ' '.join(self.args) —— 这会抹掉所有 \n。
正确做法示例:
class MultiLineError(Exception):
def __init__(self, message):
super().__init__(message)
self.message = message
def __str__(self):
return self.message # 直接返回原始字符串,不加工
或者更稳妥地:
- 用
os.linesep替换\n(适配 Windows/Linux 换行差异) - 避免在
__str__中做.strip()或.replace(' ', '')类操作
日志输出时仍显示为单行?检查 handler 的 formatter
即使你拿到了带 \n 的字符串,用 logging.error(str(exc)) 输出后仍可能被压成一行——这是因为默认的 Formatter 会把换行符转义或截断。
解决方式:
- 用
logging.error('', exc_info=True),让 logger 自动调用traceback.format_exception - 或自定义 formatter,在
format()方法中对record.msg调用str.replace('\n', '\\n')并保留,但更推荐前者 - 某些终端(如 PyCharm Console)对日志中连续换行支持不佳,可临时改用
print()验证原始字符串是否真含换行
最易被忽略的一点:异常消息里的 \n 是真实字符,不是字面量两个字符 \ 和 n;调试时用 repr(str(exc)) 看一眼,比凭眼力判断可靠得多。










