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

使用dataclass字段初始化Logger遇OSError无效参数错误求助

问题解决:Logger类在数据类中实例化时用户参数异常

我的Logger类代码

import logging
import os
import datetime


class Logger:
    _logger = None

    def __new__(cls, user: str, *args, **kwargs):
        if cls._logger is None:
            cls._logger = super().__new__(cls, *args, **kwargs)
            cls._logger = logging.getLogger("crumbs")
            cls._logger.setLevel(logging.DEBUG)
            formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(filename)s] [%(funcName)s] [%(lineno)d]: %(message)s')
            now = datetime.datetime.now()
            directory_name = f'./log/{now.strftime("%Y-%m-%d")}'
            base_log_name = f'/{user}'
            log_file_extension = '.log'
            if not os.path.isdir(directory_name):
                os.mkdir(directory_name)
            file_handler = logging.FileHandler(f'{directory_name}{base_log_name}{now.strftime("%d-%m-%Y")}{log_file_extension}', 'w', 'utf-8')
            stream_handler = logging.StreamHandler()
            file_handler.setFormatter(formatter)
            stream_handler.setFormatter(formatter)
            cls._logger.addHandler(file_handler)
            cls._logger.addHandler(stream_handler)
        return cls._logger

RunningJobManager数据类代码

我创建了一个接收user参数的RunningJobManager数据类,希望日志文件名包含用户名:

from dataclasses import dataclass, field

@dataclass(kw_only=True)
class RunningJobManager:
    user: str = field(init=True)
    password: str = field(init=True)
    logging: Logger = field(init=False, default_factory=Logger(user=user))

出现的错误

目前Logger接收的user参数类型为dataclasses.Field而非字符串,尝试用default替代default_factory仍报错:

当前代码触发OSError: [Errno 22] Invalid argument:
'G:\my_project\log\2023-01-20\Field(name=None,type=None,default=<dataclasses._MISSING_TYPE
object at
0x0000017B1E78DB10>,default_factory=<dataclasses._MISSING_TYPE object
at
0x0000017B1E78DB10>,init=True,repr=True,hash=None,compare=True,metadata=mappingproxy({}),kw_only=<dataclasses._MISSING_TYPE
object at 0x0000017B1E78DB10>,_field_type=None)20-01-2023.log'

错误出现在这行代码:

file_handler = logging.FileHandler(f'{directory_name}{base_log_name}{now.strftime("%d-%m-%Y")}{log_file_extension}', 'w', 'utf-8')

编辑补充:

"stdout_handler": {
                    "formatter": "std_out",
                    "class": "logging.StreamHandler",
                    "level": "DEBUG"
                }

解决方案

问题根源

  1. 数据类default_factory使用错误:default_factory要求传入无参可调用对象,直接写Logger(user=user)会在数据类定义阶段执行,此时user还是dataclasses.Field对象,不是实例化时传入的字符串值。
  2. Logger单例逻辑不符合需求:当前全局单例会导致第一次实例化后,后续无法为不同用户生成独立日志文件。

修复步骤

1. 调整Logger类,支持多用户独立日志

去掉全局单例,改为每个用户实例对应独立的logger:

import logging
import os
import datetime
from typing import Optional

class Logger:
    def __init__(self, user: str):
        self.user = user
        # 每个用户使用唯一的logger名称,避免handler冲突
        self.logger = logging.getLogger(f"crumbs_{user}")
        self.logger.setLevel(logging.DEBUG)
        
        # 避免重复添加handler
        if self.logger.handlers:
            return
        
        formatter = logging.Formatter('[%(asctime)s] [%(levelname)s] [%(filename)s] [%(funcName)s] [%(lineno)d]: %(message)s')
        now = datetime.datetime.now()
        directory_name = f'./log/{now.strftime("%Y-%m-%d")}'
        base_log_name = f'/{user}'
        log_file_extension = '.log'
        
        # 用makedirs更安全,支持递归创建目录
        os.makedirs(directory_name, exist_ok=True)
        log_file_path = f'{directory_name}{base_log_name}_{now.strftime("%d-%m-%Y")}{log_file_extension}'
        file_handler = logging.FileHandler(log_file_path, 'w', 'utf-8')
        stream_handler = logging.StreamHandler()
        
        file_handler.setFormatter(formatter)
        stream_handler.setFormatter(formatter)
        
        self.logger.addHandler(file_handler)
        self.logger.addHandler(stream_handler)
    
    def __getattr__(self, name):
        # 代理原生logger的方法,保持调用习惯一致
        return getattr(self.logger, name)

2. 修复RunningJobManager的Logger初始化逻辑

使用__post_init__方法,在数据类实例化完成后再初始化Logger,此时能拿到正确的user字符串:

from dataclasses import dataclass, field

@dataclass(kw_only=True)
class RunningJobManager:
    user: str = field(init=True)
    password: str = field(init=True)
    logging: Logger = field(init=False)
    
    def __post_init__(self):
        self.logging = Logger(user=self.user)

补充说明

  • os.makedirs(directory_name, exist_ok=True)替代os.mkdir,避免目录已存在时抛出异常。
  • 每个用户使用唯一的logger名称,防止不同用户的日志处理逻辑互相干扰。
  • 通过__getattr__代理方法,使用时可以直接调用self.logging.debug("日志内容"),和原生logger用法完全一致。

内容的提问来源于stack exchange,提问作者falukky

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:10:28