处理OSError之外的I/O异常:Python日志类错误处理问询
问题描述
我有一个需要写入每日日志文件的常驻程序,以下是我的Message类实现:程序启动时打开文件,每日关闭并重新打开新文件。我正在测试错误处理,确保即使无法写入日志(如文件系统满)程序也不会崩溃。多数常规I/O错误属于OSError,当前代码已处理这类错误。
但遇到两个问题:
- 启动时若无法打开初始文件,会抛出
AttributeError: 'NoneType' object has no attribute 'write'异常,因为log对象未创建成功,后续调用log.write时会操作None对象。正常打开日志会生成<class '_io.TextIOWrapper'>对象,请问能否将log初始化为一个虚拟TextIOWrapper?这样做有意义吗? - 写入已关闭的文件句柄时会抛出
ValueError: I/O operation on closed file.异常,我困惑为何这不属于OSError,因此至少需要将ValueError加入异常捕获列表。
附上代码:
import datetime class Message: log_date = f"{datetime.datetime.now():%Y%m%d}" log_file = log_date + '.log' try: log = open(log_file, 'a') log.write(f"{log_file} opened\n") log.flush() except OSError as err: errno, strerror = err.args print(f"I/O Error with {log_file}; {strerror}") def print(msg: str): today = f"{datetime.datetime.now():%Y%m%d}" try: if today != Message.log_date: Message.log.close() Message.log_date = today Message.log_file = today + '.log' Message.log = open(Message.log_file, 'a') Message.log.write(f"{Message.log_file} opened\n") Message.log.write(f"{msg}\n") Message.log.flush() except OSError as err: errno, strerror = err.args print(f"I/O Error with {Message.log_file}; {strerror}")
解决方案
虚拟日志对象的实现与意义
完全可以初始化一个虚拟的日志对象,这种做法非常有意义——能彻底避免后续操作None对象引发的AttributeError,保证程序在日志文件无法打开时仍能正常运行。
不需要严格继承TextIOWrapper,只需要实现一个包含write()、flush()、close()方法的空操作类即可,示例:
class DummyLog: def write(self, msg): # 可选:降级输出到控制台,也可以什么都不做 print(f"[Log Fail] {msg.strip()}") def flush(self): pass def close(self): pass
修改类初始化代码,捕获异常时将log赋值为DummyLog实例:
try: log = open(log_file, 'a') log.write(f"{log_file} opened\n") log.flush() except OSError as err: errno, strerror = err.args print(f"I/O Error with {log_file}; {strerror}") log = DummyLog() # 初始化虚拟日志对象
这样后续调用Message.log.write()时,只会执行你定义的降级逻辑,不会触发崩溃。
关于ValueError的问题
Python中ValueError用于表示参数合法但对象状态不允许操作的场景:写入已关闭的文件句柄时,文件路径本身是有效的,但文件对象已经处于关闭状态,不符合I/O操作的要求,因此抛出ValueError;而OSError更多对应操作系统层面的错误(如权限不足、磁盘满、文件不存在等)。
需要修改异常捕获逻辑,把ValueError加入捕获列表,示例:
try: # 原有日期切换与写入逻辑 if today != Message.log_date: Message.log.close() Message.log_date = today Message.log_file = today + '.log' Message.log = open(Message.log_file, 'a') Message.log.write(f"{Message.log_file} opened\n") Message.log.write(f"{msg}\n") Message.log.flush() except (OSError, ValueError) as err: if isinstance(err, OSError): errno, strerror = err.args print(f"I/O Error with {Message.log_file}; {strerror}") else: print(f"Log operation error: {str(err)}") # 可选:此处可以尝试重新初始化日志,或切换到虚拟日志
另外注意:类级别的datetime.now()只会在类加载时执行一次,若程序跨零点启动,初始log_date不会自动更新,但你在print方法中已经处理了日期切换,这个问题影响不大。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

