如何从json文件中指定要使用的日志记录配置

u3r8eeie  于 2023-04-22  发布在  其他
关注(0)|答案(2)|浏览(152)

我正在尝试为python日志库创建一个 Package 器类,其想法是用户可以向构造器提供一个日志记录器的名称,并根据json文件的内容配置示例,其中提供的名称是配置文件相应部分的键。
这是我的代码

class LogLib:
    def __init__(self, logger_name=""):
        conf_dict = Config.get("LogLib")
        logging_config.dictConfig(conf_dict)
        if not logger_name:
            self.logger = logging.getLogger()
        else:
            self.logger = logging.getLogger(logger_name)

    def debug(self, message, db=False):
        caller_info = self.get_callerInfo()
        msg = message + caller_info
        self.logger.debug(msg)
        if db:
            self.log_db(message, "DEBUG")

    def info(self, message, db=False):
        caller_info = self.get_callerInfo()
        msg = message + caller_info
        self.logger.info(msg)
        if db:
            self.log_db(message, "INFO")

    def warning(self, message, db=False):
        caller_info = self.get_callerInfo()
        msg = message + caller_info
        self.logger.warning(msg)
        if db:
            self.log_db(message, "WARNING")

    def error(self, message, db=False, stacktrace=False):
        caller_info = self.get_callerInfo()
        msg = message + caller_info
        self.logger.error(msg, exc_info=stacktrace)
        if db:
            self.log_db(message, "ERROR")

    def critical(self, message, db=False, stacktrace=False):
        caller_info = self.get_callerInfo()
        msg = message + caller_info
        self.logger.critical(msg, exc_info=stacktrace)
        if db:
            self.log_db(message, "CRITICAL")

    def log_db(self, message, level):
        raise NotImplemented()
        # psql = PostgresqlConnector()
        # with psql.create_session() as session:
        #    psql.insert(session, Log(message=message, level=level))

    def get_callerInfo(self):
        raw = self.logger.findCaller(stack_info=False)
        caller_fileName = raw[0].rsplit("/", 1)[1].split(".")[0]
        return f"\nSOURCE > {caller_fileName}.{raw[2]}, Line: {raw[1]}\n"

为了做一些测试,我在LogLib文件的底部添加了一个小的main(),在类的外部。它看起来像这样:

def main():
        logger = LogLib(logger_name="db_logger")
        logger.debug("Debug test - This should not show up in the file.")
        logger.info("Info test")
        logger.warning("Warning test")
        logger.error("Error test")

    if __name__ == "__main__":
        main()

为了配置这个 Package 器,我创建了一个JSON格式的配置部分,然后在_ _ init _ _中获取并使用它。配置看起来像这样:

"LogLib": {
    "version": 1,
    "root": {
        "handlers": ["console", "file"],
        "level": "DEBUG"
    },
    "db_logger": {
        "handlers": ["db_file"],
        "level": "INFO"
    },
    "handlers": {
        "console": {
            "formatter": "console_formatter",
            "class": "logging.StreamHandler",
            "level": "WARNING"
          },
        "file": {
          "formatter": "file_formatter",
          "class": "logging.FileHandler",
          "level": "DEBUG",
          "filename": "C:\\Users\\name\\Documents\\GitHub\\proj\\logs\\app_err.log"
        },
        "db_file": {
            "formatter": "file_formatter",
            "class": "logging.FileHandler",
            "level": "INFO",
            "filename": "C:\\Users\\name\\Documents\\GitHub\\proj\\logs\\db.log"
        }
      },
    "formatters": {
        "console_formatter": {
          "format": "%(asctime)s [%(levelname)s] > %(message)s",
          "datefmt": "%d/%m/%Y-%I:%M:%S"
        },
        "file_formatter": {
          "format": "%(asctime)s [%(levelname)s] > %(message)s",
          "datefmt": "%d/%m/%Y-%I:%M:%S"
        }
      }
}

根日志记录器在它的配置中工作得很好(写入app_err.log并打印到给定级别的控制台),但是当我尝试为它提供名称“db_logger”时,它不起作用,并且默认为root。
我想要的是,当用户通过参数“logger_name”向构造函数提供一个名称时,它应该检查具有该名称的日志记录器的配置,并将为该名称指定的配置应用于LogLib示例。在这种情况下,我希望所有INFO或更高级别的日志记录消息都被发送到一个名为db.log的文件,而没有任何控制台输出。

rdlzhqv9

rdlzhqv91#

我认为logging Package 器通常是一个糟糕的设计,我在商业和OSS代码库中看到过很多这样的设计。我会称之为Python中的反模式,因为logging包实际上是非常可扩展的,很少会超出其(扩展)机制。
日志库采用模块化方法,并提供了几类组件:记录器、处理程序、筛选器和格式化程序。

  • 记录器公开应用程序代码直接使用的接口。
  • 处理程序将日志记录(由记录器创建)发送到适当的目的地。
  • 过滤器提供了一种更细粒度的工具,用于确定要输出哪些日志记录。
  • 格式设置指定最终输出中日志记录的布局。

还有一些关于这个片段的要点。
1.除非你想在运行时修改JSON配置文件,否则没有必要在每个logger上调用logging.config.dictConfig
1.如果你确实想在运行时更改日志记录配置,请注意,你可能需要设置disable_existing_loggers=False。在文档中有一个关于它的警告:
fileConfig()函数接受一个默认参数disable_existing_loggers,出于向后兼容的原因,该参数默认为True。这可能是您想要的,也可能不是,因为它将导致在fileConfig()调用之前存在的任何非根记录器被禁用,除非它们(或祖先)在配置中显式命名。请参考参考文档以获取更多信息,如果您愿意,请为此参数指定False
传递给dictConfig()的字典也可以用键disable_existing_loggers指定一个布尔值,如果没有在字典中显式指定,默认也会被解释为True。这会导致上面描述的日志记录器禁用行为,这可能不是你想要的-在这种情况下,显式提供键False的值。
增量配置也有此警告:
[...]一旦设置了配置,在运行时任意改变记录器、处理程序、过滤器、格式化程序的对象图没有令人信服的情况;记录器和处理程序的详细程度可以通过设置级别来控制(在记录器的情况下,还可以设置传播标志)。虽然不是不可能的,但是其益处不值得它增加实现的复杂性。
如果你仍然想继续logging.config.listen可以是一个灵感。
1.您不需要get_callerInfoLogRecord具有以下开箱即用的功能:filenamemodulelinenofuncName。要在日志中公开它们,请在格式字符串或子类logging.Formatter中引用它们,因为您希望有一些格式逻辑。
1.想要将日志记录写到一个新的介质,比如Postgres(stdlib logging.handlers或第三方包还不支持)?写一个logging.Handler的子类。还要注意,记录到生产数据库可能很棘手。下面是几个例子:logbeam(AWS CloudWatch Logs的日志处理程序)和Chronologer(我用MySQL和SQLite编写的Python客户端/服务器日志系统)。

h22fl7wq

h22fl7wq2#

感谢@saaj的精彩回复
1.我不需要在运行时更改配置。但是,这将在高度分布式的环境中运行我不太熟悉引导的详细工作原理,但我认为为了执行一些小任务而创建的Celery工作线程不是应用程序主执行线程的一部分(如果我在这里遗漏了什么,请纠正我)。我想说的是,在生成Celery worker时没有引导过程,因此配置对Worker不可见。我没有看到任何我们希望在运行时更改配置的情况,这样的更改将以计划的方式进行,然后在整个应用程序重新启动后应用。我真正想要的是能够为特定上下文创建日志记录器(例如:几个密切相关的模块应该以相同的方式配置日志记录/将日志记录到同一个特定的文件)。我不知道如何配置日志记录,以便具有广泛不同的__name__值的模块都将日志记录到同一个文件。
1.我得到这样的配置的原因是因为它与我们配置应用程序其他部分的方式一致。我的团队希望所有配置都存储在一个高度保护的单个文件中。它有点旧,在我被分配到它之前就被设计成这样了。我不认为我可以改变它。
1.我很确定我尝试在配置中公开这些值(logging.formatter),但它没有找到正确的值。它只显示了有关日志库的信息,而不是与调用日志 Package 器的模块相关的信息。
1.当我们想要记录到postgresqldb时,我将看看如何创建logging.Handler的子类。
感谢您的反馈和建议。

相关问题