是不是 possible/advisable 把一个 logger 放到一个包中,然后在一个项目的所有包中使用它?

Is it possible/advisable to put a logger into a single package and use it across all packages in a project?

我想做的事情:
要创建一个 Python 包,并在该包中创建一个实例化记录器的 class。我会在项目的 main 函数中创建此 class 的一个实例,每当我实例化任何包或 class 时,我都会导入记录器 class我创建或传递了我在 main 函数中创建的实例的引用。

我为什么要这样做:

  1. 记录器文件名的引用完整性,无需写入 到处都是相同的代码行来实例化记录器。
  2. 不要将记录器作为全局记录器。
  3. 能够在实例化之前执行文件操作 记录器文件。比如创建一个log文件夹来存放日志 启动记录器之前的文件。
  4. 能够从配置文件(我的配置 文件管理器将是一个单独的 class 在一个单独的包中)并且 能够从配置文件加载记录器参数。

我现在实现的代码(我想更改):
主文件:

import logging
from logging.handlers import RotatingFileHandler
from operatingSystemFunctions import operatingSystemFunc
from diskOperations import fileAndFolderOperations

logFileName = os.path.join('logs', 'logs.log') 
logging.basicConfig(level=logging.INFO, format='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
log = logging.getLogger(__name__)
handler = RotatingFileHandler(logFileName, maxBytes=2000, backupCount=5)#TODO: Shift to config file
handler.formatter = logging.Formatter(fmt='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S') 
log.addHandler(handler)
log.setLevel(logging.INFO)

if __name__ == '__main__':
    while True:
        log.info("Running")
        #do something

同样,我导入的自定义包,每个都有自己的记录器全局实例:
operatingSystemFunc例如是:

import subprocess
from sys import platform #to check which OS the program is running on
from abc import ABC, abstractmethod
import logging
from logging.handlers import RotatingFileHandler

logFileName = os.path.join('logs', 'logs.log')
logging.basicConfig(level=logging.INFO, format='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
log = logging.getLogger(__name__)
handler = RotatingFileHandler(logFileName , maxBytes = 2000 , backupCount = 5)#TODO: Shift to config file
handler.formatter = logging.Formatter(fmt='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S') #setting this was necessary to get it to write the format to file. Without this, only the message part of the log would get written to the file
log.addHandler(handler)
log.setLevel(logging.INFO)

class AudioNotifier(ABC):#Abstract parent class
    @abstractmethod
    def __init__(self):
        #self.id = "someUniqueNotifierName" #has to be implemented in the child class
        pass

现在代码抛出错误 FileNotFoundError: [Errno 2] No such file or directory: '/home/nav/prog/logs/logs.log' 因为我还没有创建 logs 文件夹。

我担心的一个问题是,如果我在单独的包中实现记录器并通过将该包导入所有其他包或通过将对 class 的实例的引用传递给所有其他 class是的,那么这会是一种不好的做法吗?

更新:
从 Pablo 的回答中学习,我更改了代码以确保 RotatingFileHandler 能够同时写入文件。所以这是结果代码:

import logging
from logging.handlers import RotatingFileHandler
from operatingSystemFunctions import operatingSystemFunc
from diskOperations import fileAndFolderOperations

logFileName = 'logs.log'
logging.basicConfig(level=logging.INFO, format='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S')
#log = logging.getLogger(__name__)
handler = RotatingFileHandler(logFileName, maxBytes=2000, backupCount=2)#TODO: Shift to config file
handler.formatter = logging.Formatter(fmt='%(levelname)s %(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S') #setting this was necessary to get it to write the format to file. Without this, only the message part of the log would get written to the file
logging.getLogger().addHandler(handler)
logging.getLogger().setLevel(logging.INFO)

if __name__ == '__main__':
    logging.info("\n\n---------------------------------")
    logging.info("program started")
    operatingSystemCheck = operatingSystemFunc.OperatingSystemFunc()
    logging.info("Monitoring ...")

    while True:
        pass

它在这样的包中使用:

import logging

class OperatingSystemFunc:    
    def __init__(self):
        logging.info("Some message")

很少考虑,python 记录器已经是在他自己的线程中运行的单例,因此只创建了一个记录器实例。当您调用它或尝试在不同的地方创建它时,您所做的只是第一次创建它并一次又一次地调用同一个单例。因此,首先定义的处理程序在任何地方都可以访问,而无需传递或导入它们。

如果您在主函数中使用名称创建记录器实例一次,以后您只需使用 logging.getLogger("your chosen name"),因此在大多数情况下没有必要创建 class 在包内等等。您可以使用您在别处定义的处理程序,而无需直接导入它们。

所以我想说你提议的不是一个好的做法,因为没有必要,也许有一些例外。如果您真的想保存写入“log = logging.getLogger('...')”,这是您唯一要保存的内容,那么您绝对可以将该行写在一个包中并导入“log”而不是日志记录模块,这样就可以了。但是我建议你不要将记录器放在 class 中,或者如果在 class 中,请在同一个文件中创建实例并导入实例,而不是 class.

这是我在项目中使用的记录器文件:

import logging

class CustomFormatter(logging.Formatter):

    grey = "\x1b[38;21m"
    green = "\x1b[0;30;42m"
    yellow = "\x1b[1;30;43m"
    red = "\x1b[0:30;41m"
    bold_red = "\x1b[1;30;1m"
    reset = "\x1b[0m"

    FORMATS = {
        logging.DEBUG: f"%(asctime)s -  {grey}%(levelname)s{reset} - %(message)s",
        logging.INFO: f"%(asctime)s -  {green}%(levelname)s{reset} - %(message)s",
        logging.WARNING: f"%(asctime)s -  {yellow}%(levelname)s{reset} - %(message)s",
        logging.ERROR: f"%(asctime)s -  {red}%(levelname)s{reset} - %(message)s",
        logging.CRITICAL: f"%(asctime)s -  {bold_red}%(levelname)s{reset} - %(message)s"
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)


logger = logging.getLogger("core")
logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(CustomFormatter())
logger.addHandler(ch)

并放入

_ init _.py

from .logger import logger as LOGGER

并制作了一个类似

的 ini 文件

log.ini

[loggers]
keys=root

[handlers]
keys=logfile,logconsole

[formatters]
keys=logformatter

[logger_root]
level=INFO
handlers=logfile, logconsole

[formatter_logformatter]
format=[%(asctime)s.%(msecs)03d] %(levelname)s [%(thread)d] - %(message)s

[handler_logfile]
class=handlers.RotatingFileHandler
level=INFO
args=('logs.log','a')
formatter=logformatter

[handler_logconsole]
class=handlers.logging.StreamHandler
level=INFO
args=()
formatter=logformatter

这些对我帮助太大了,希望对你有所帮助