从多个模块使用python日志记录,并写入文件和旋转FileHandler
我正在使用下面的模块记录模块中的事件。我称之为: 单元1从多个模块使用python日志记录,并写入文件和旋转FileHandler,python,logging,Python,Logging,我正在使用下面的模块记录模块中的事件。我称之为: 单元1 from tools.debug_logger import debug_logger self.logger = debug_logger().start_logger('module1') self.logger.debug("Top left corner found") 模块2: from tools.debug_logger import debug_logger self.logger = debug_logger().st
from tools.debug_logger import debug_logger
self.logger = debug_logger().start_logger('module1')
self.logger.debug("Top left corner found")
模块2:
from tools.debug_logger import debug_logger
self.logger = debug_logger().start_logger('module2')
self.logger.debug("Top left corner found")
这里是文件/tools/debug_logger.py
import logging, logging.handlers
import sys
class debug_logger(object):
def start_logger(self,name):
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
if not len(logger.handlers):
fh = logging.handlers.RotatingFileHandler('log/pokerprogram.log', maxBytes=1000000, backupCount=10)
fh.setLevel(logging.DEBUG)
fh2 = logging.handlers.RotatingFileHandler('log/pokerprogram_info_only.log', maxBytes=1000000, backupCount=5)
fh2.setLevel(logging.INFO)
er = logging.handlers.RotatingFileHandler('log/errors.log', maxBytes=2000000, backupCount=2)
er.setLevel(logging.WARNING)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(1)
fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
fh2.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
er.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
ch.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
logger.addHandler(fh)
logger.addHandler(fh2)
logger.addHandler(ch)
logger.addHandler(er)
return logger
一切正常,我得到了相应级别的日志文件,但当调用RotatingFileHandler时,有时会出错。这就好像不同的实例希望同时进行旋转,尽管我很确定这不应该发生,因为我确保只有一个处理程序具有if not len(logger.handlers)
,如这里所建议的那样:
任何关于在轮换期间可能导致文件访问违规的建议都将不胜感激
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'C:\\Users\\Nicolas\\Dropbox\\PythonProjects\\Poker\\log\\pokerprogram.log' -> 'C:\\Users\\Nicolas\\Dropbox\\PythonProjects\\Poker\\log\\pokerprogram.log.1'
我认为出现权限错误是因为当旋转发生时,其他模块仍在写入文件
当我写入文件并使用此RotatingFileHandler时,从多个模块进行日志记录的最佳方式是什么?有什么最佳做法吗?我认为您的日志设置有误。建议的设置日志的方法是不要定义任何处理程序,也不要将日志级别记录到模块中,而是在主文件中定义所有配置 例如在
module1.py
中:
import logging
logger = logging.getLogger(__name__)
# use logger.info/logger.debug etc.
在module2.py
中,您输入了完全相同的代码:
import logging
logger = logging.getLogger(__name__)
# use logger.info/logger.debug etc.
请注意,\uu name\uu
是模块名称,因此它类似于package.module1
或package.module2
。使用虚线名称会自动创建记录器的层次结构,因此习惯上只使用模块的\uuu name\uuu
来获取记录器
module1
和module2
不需要包含任何与日志记录相关的内容。他们不应该决定日志输出的位置或级别,因为这是启动应用程序的人应该控制的事情。因此,最好在主可执行文件中处理它
现在,在主可执行文件中定义处理程序:
import logging, logging.handlers
fh = logging.handlers.RotatingFileHandler('log/pokerprogram.log', maxBytes=1000000, backupCount=10)
fh.setLevel(logging.DEBUG)
fh2 = logging.handlers.RotatingFileHandler('log/pokerprogram_info_only.log', maxBytes=1000000, backupCount=5)
fh2.setLevel(logging.INFO)
er = logging.handlers.RotatingFileHandler('log/errors.log', maxBytes=2000000, backupCount=2)
er.setLevel(logging.WARNING)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(1)
fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
fh2.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
er.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
ch.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
root = logging.getLogger()
root.setLevel(logging.DEBUG)
# alternatively:
# root.setLevel(min([fh.level, fh2.level, ch.level, er.level])
root.addHandler(fh)
root.addHandler(fh2)
root.addHandler(ch)
root.addHandler(er)
最后,您只需将处理程序添加到根记录器,并将根记录器的级别设置为处理程序中的最低级别:
import logging, logging.handlers
fh = logging.handlers.RotatingFileHandler('log/pokerprogram.log', maxBytes=1000000, backupCount=10)
fh.setLevel(logging.DEBUG)
fh2 = logging.handlers.RotatingFileHandler('log/pokerprogram_info_only.log', maxBytes=1000000, backupCount=5)
fh2.setLevel(logging.INFO)
er = logging.handlers.RotatingFileHandler('log/errors.log', maxBytes=2000000, backupCount=2)
er.setLevel(logging.WARNING)
ch = logging.StreamHandler(sys.stdout)
ch.setLevel(1)
fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
fh2.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
er.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
ch.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s'))
root = logging.getLogger()
root.setLevel(logging.DEBUG)
# alternatively:
# root.setLevel(min([fh.level, fh2.level, ch.level, er.level])
root.addHandler(fh)
root.addHandler(fh2)
root.addHandler(ch)
root.addHandler(er)
这是因为伐木者的等级性质。调用module1.logger.debug
时,如果记录器没有处理程序,它将把日志记录传递给其父记录器,父记录器将继续这样做,直到根记录器最终使用其处理程序来处理日志记录
还需要设置根记录器级别,因为它默认为警告
,而其他记录器默认为未设置
(这导致前面提到的委派)
或者,向两个模块记录器显式添加相同的处理程序:
from <package> import module1, module2
module1.logger.setLevel(logging.DEBUG)
module2.logger.setLevel(logging.DEBUG)
module1.logger.addHandler(fh)
module2.logger.addHandler(fh)
module1.logger.addHandler(fh2)
module2.logger.addHandler(fh2)
module1.logger.addHandler(ch)
module2.logger.addHandler(ch)
module1.logger.addHandler(er)
module2.logger.addHandler(er)
来自导入模块1、模块2
module1.logger.setLevel(logging.DEBUG)
module2.logger.setLevel(logging.DEBUG)
module1.logger.addHandler(fh)
module2.logger.addHandler(fh)
module1.logger.addHandler(fh2)
module2.logger.addHandler(fh2)
module1.logger.addHandler(ch)
module2.logger.addHandler(ch)
module1.logger.addHandler(er)
module2.logger.addHandler(er)
将同一个处理程序对象添加到多个记录器中并没有什么害处。这样可以确保处理程序不会同时旋转文件。您做得不对。您的模块不应定义任何处理程序,也不应设置日志记录级别。只有主可执行文件才能设置要使用的处理程序和级别。。。这简化了一切,因为执行此操作后,该文件始终只有一个处理程序,并且不可能发生该错误。with
if not len(logger.handlers):
我确保只有一个处理程序。有什么更好的方法吗?不,你没有。因为module1和module2有不同的名称,这意味着由getLogger
返回的记录器是不同的,因此您要为module1和module2添加一个处理程序。因此,请确保单个记录器没有多个处理程序,但您确实有两个处理程序使用同一个文件,这会导致您看到的错误。我明白了,这是有道理的。那么,解决这个问题的最佳方法是什么,这样我就可以轻松地调用例程并将其导入到不同的模块中?不需要导入它。我已经写了一个答案,描述了在大多数情况下工作的典型日志设置。module1
和module2
应仅导入日志记录
模块,并通过logging.getLogger(\uuu name\uuuu)
获取其记录器。有关日志输出配置的所有其他内容都是从可执行文件处理的。这是有意义的,因为启动应用程序的人应该能够决定在何处以及在哪个级别记录内容,而不是模块本身!您能否确认每个模块中都需要重新设置日志级别,否则将设置为默认的日志记录。警告?@nickpick Ah,对不起,我忘了添加:您可以只设置根日志记录程序的级别。我将在答案中编辑此内容。@nickpick在文档中略读一下。它表示默认情况下根记录器设置为WARNING
,而其他记录器默认设置为NOTSET
。当记录器处于级别NOTSET
时,将委托给父记录器,这就是为什么如果您决定将处理程序直接添加到两个模块记录器,则必须修改它们的级别,如果将它们添加到根记录器,则不应设置它们的级别。我已将处理程序及其各自的级别添加到根记录器,但仍需要在模块中设置级别。@尼克如果这些模块遵循相同的结构,则只需为它们设置所需的级别即可。例如:如果我的应用程序使用pika
包,我可以将logging.getLogger('pika').setLevel(logging.WARNING)
。现在,发送到pika记录器的所有消息都将根据该级别进行过滤。