如何使用不同的类和导入动态更改Python日志记录的filehandle

如何使用不同的类和导入动态更改Python日志记录的filehandle,python,logging,configuration,Python,Logging,Configuration,我无法执行动态日志文件句柄更改 例如,我有3门课 one.py import logging class One(): def __init__(self,txt="?"): logging.debug("Hey, I'm the class One and I say: %s" % txt) import logging class Two(): def __init__(self,txt="?"): logging.debug("Hey, I

我无法执行动态日志文件句柄更改

例如,我有3门课

one.py

import logging
class One():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class One and I say: %s" % txt)
import logging
class Two():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class Two and I say: %s" % txt)
import logging
class Config():
    def __init__(self,logfile=None):
        logging.debug("Reading config")
        self.logfile(logfile)
two.py

import logging
class One():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class One and I say: %s" % txt)
import logging
class Two():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class Two and I say: %s" % txt)
import logging
class Config():
    def __init__(self,logfile=None):
        logging.debug("Reading config")
        self.logfile(logfile)
config.py

import logging
class One():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class One and I say: %s" % txt)
import logging
class Two():
    def __init__(self,txt="?"):
        logging.debug("Hey, I'm the class Two and I say: %s" % txt)
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()
,它将不起作用。

事实上,
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日志指南中的。

我找到了一种比上述“已接受”答案更简单的方法。如果有对处理程序的引用,则只需调用close()方法,然后设置baseFilename属性。分配baseFilename时,请确保使用os.path.abspath()。库源代码中有一条注释,表明需要它。我将配置内容保存在全局dict()中,因此很容易保存FileHandler引用对象。正如您在下面看到的,动态更改处理程序的日志文件名只需要两行代码

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

@Martijn Pieters提供的答案很好。但是,代码狙击手会删除所有处理程序,并只放回文件处理程序。如果您的应用程序有其他模块添加的处理程序,这将很麻烦

因此,下面的代码段设计为仅替换文件处理程序

如果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)

我试图实现@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)      

即使不保存对处理程序的引用,如果记录器中只有一个处理程序,也可以直接引用它并使用相同的技术:
mylogger=logging.getLogger('mylogger')#get logger
,然后
mylogger.handlers[0].close()mylogger.handlers[0].baseFilename=os.path.abspath('newfilename.log'))
@user2179204您说“更简单”,写了四倍多的代码行-这不是python的方式。@阿尔珀:下一个选项:创建每线程处理程序,为每个处理程序提供一个过滤器,该过滤器在logrecord
thread
属性上进行过滤。为设置了
thread
的日志记录返回False的任何其他处理程序附加一个筛选器。♦ 谢谢,我也在这里问过()我会把它链接到你的评论。