如何使用不同的类和导入动态地使用 Python 日志更改文件句柄
Posted
技术标签:
【中文标题】如何使用不同的类和导入动态地使用 Python 日志更改文件句柄【英文标题】:How to change filehandle with Python logging on the fly with different classes and imports 【发布时间】:2012-11-30 03:28:53 【问题描述】:我无法执行动态记录文件句柄更改。
例如,我有 3 个班级
one.py
import logging
class One():
def __init__(self,txt="?"):
logging.debug("Hey, I'm the class One and I say: %s" % txt)
two.py
import logging
class Two():
def __init__(self,txt="?"):
logging.debug("Hey, I'm the class Two and I say: %s" % txt)
config.py
import logging
class Config():
def __init__(self,logfile=None):
logging.debug("Reading config")
self.logfile(logfile)
myapp
from one import One
from two import Two
from config import Config
import logging
#Set default logging
logging.basicConfig(
level=logging.getLevelName(DEBUG),
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename=None
)
logging.info("Starting with stdout")
o=One(txt="STDOUT")
c=Config(logfile="/tmp/logfile")
# Here must be the code that change the logging configuration and set the filehandler
t=One(txt="This must be on the file, not STDOUT")
如果我再次尝试loggin.basicConfig()
,它不起作用。
【问题讨论】:
【参考方案1】:确实,如果已经设置了处理程序,logging.basicConfig
什么都不做:
如果根记录器已经为其配置了处理程序,则此函数不执行任何操作。
您需要替换根记录器上的当前处理程序:
import logging
fileh = logging.FileHandler('/tmp/logfile', 'a')
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fileh.setFormatter(formatter)
log = logging.getLogger() # root logger
for hdlr in log.handlers[:]: # remove all old handlers
log.removeHandler(hdlr)
log.addHandler(fileh) # set the new handler
请参阅 Python Logging HOWTO 中的 Configuring Logging chapter。
【讨论】:
@alper:下一个选项:创建每个线程的处理程序,为每个处理程序提供一个过滤器,用于过滤日志记录thread
属性。将过滤器附加到任何其他处理程序,该处理程序为设置了thread
的日志记录返回 False。
♦ 谢谢,我也在这里问过 (***.com/q/61818674/2402577) 我会将其链接到您的评论。【参考方案2】:
我找到了比上述“接受”答案更简单的方法。如果您有对处理程序的引用,您需要做的就是调用 close() 方法,然后设置 baseFilename 属性。分配 baseFilename 时,请务必使用 os.path.abspath()。库源中有一条注释表明它是必需的。我将配置内容保存在全局 dict() 中,因此很容易保存 FileHandler 引用对象。正如您在下面看到的,只需要 2 行代码即可动态更改处理程序的日志文件名。
import logging
def setup_logging():
global config
if config['LOGGING_SET']:
config['LOG_FILE_HDL'].close()
config['LOG_FILE_HDL'].baseFilename = os.path.abspath(config['LOG_FILE'])
config['DEBUG_LOG_HDL'].close()
config['DEBUG_LOG_HDL'].baseFilename = os.path.abspath(config['DEBUG_LOG'])
else:
format_str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
formatter = logging.Formatter(format_str)
log = logging.getLogger()
log.setLevel(logging.DEBUG)
# add file mode="w" to overwrite
config['LOG_FILE_HDL'] = logging.FileHandler(config['LOG_FILE'], mode='a')
config['LOG_FILE_HDL'].setLevel(logging.INFO)
config['LOG_FILE_HDL'].setFormatter(formatter)
log.addHandler(config['LOG_FILE_HDL'])
# the delay=1 should prevent the file from being opened until used.
config['DEBUG_LOG_HDL'] = logging.FileHandler(config['DEBUG_LOG'], mode='a', delay=1)
config['DEBUG_LOG_HDL'].setLevel(logging.DEBUG)
config['DEBUG_LOG_HDL'].setFormatter(formatter)
log.addHandler(config['DEBUG_LOG_HDL'])
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
ch.setFormatter(formatter)
log.addHandler(ch)
config['LOGGING_SET'] = True
【讨论】:
即使您不保存对处理程序的引用,如果您的记录器中只有一个处理程序,您也可以直接引用它并使用相同的技术:mylogger = logging.getLogger('mylogger') # get logger
然后mylogger.handlers[0].close() mylogger.handlers[0].baseFilename = os.path.abspath('newfilename.log')
@user2179204 你伤心“更容易”并且写了四倍多的代码行 - 这不是 python 方式。【参考方案3】:
@Martijn Pieters 提供的答案效果很好。但是,代码剪辑器删除了所有处理程序并仅将文件处理程序放回原处。如果您的应用程序有其他模块添加的处理程序,这将很麻烦。
因此,下面的 sn-p 被设计为仅替换文件处理程序。
if isinstance(hdlr,logging.FileHandler)
这行是关键。
import logging
filehandler = logging.FileHandler('/tmp/logfile', 'a')
formatter = logging.Formatter('%(asctime)-15s::%(levelname)s::%(filename)s::%(funcName)s::%(lineno)d::%(message)s')
filehandler.setFormatter(formatter)
log = logging.getLogger() # root logger - Good to get it only once.
for hdlr in log.handlers[:]: # remove the existing file handlers
if isinstance(hdlr,logging.FileHandler):
log.removeHandler(hdlr)
log.addHandler(filehandler) # set the new handler
# set the log level to INFO, DEBUG as the default is ERROR
log.setLevel(logging.DEBUG)
【讨论】:
【参考方案4】:我尝试将@Martijn Pieters 和@Arun Thundyill Saseendran 在此页面上的建议结合起来。我太新了,不能发表评论,所以我必须发布一个调整后的答案。在 isinstance 调用中,我必须使用“logging”而不是“log”来访问类型(log 是一个实例),然后“FileHander”应该是“FileHandler”。我正在使用 Python 3.6。
import logging
filehandler = logging.FileHandler('/tmp/logfile', 'a')
formatter = logging.Formatter('%(asctime)-15s::%(levelname)s::%(filename)s::%(funcName)s::%(lineno)d::%(message)s')
filehandler.setFormatter(formatter)
log = logging.getLogger() # root logger - Good to get it only once.
for hdlr in log.handlers[:]: # remove the existing file handlers
if isinstance(hdlr,logging.FileHandler): #fixed two typos here
log.removeHandler(hdlr)
log.addHandler(filehandler) # set the new handler
# set the log level to INFO, DEBUG as the default is ERROR
logging.setLevel(log.DEBUG)
【讨论】:
很好的解决方案!我正在使用 Python 烧瓶,它让我可以将消息保存在烧瓶日志中。谢谢。以上是关于如何使用不同的类和导入动态地使用 Python 日志更改文件句柄的主要内容,如果未能解决你的问题,请参考以下文章
python中函数和方法区别,以及如何给python类动态绑定方法和属性(涉及types.MethodType()和__slots__)