You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

处理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:01:10