如何使用不同的类和导入即时更改 Python 日志记录的文件句柄

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13839554/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 09:43:19  来源:igfitidea点击:

How to change filehandle with Python logging on the fly with different classes and imports

pythonloggingconfiguration

提问by xkill

I cannot perform an on-the-fly logging fileHandle change.

我无法执行即时记录文件句柄更改。

For example, I have 3 classes

例如,我有 3 个班级

one.py

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

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

config.py

import logging
class Config():
    def __init__(self,logfile=None):
        logging.debug("Reading config")
        self.logfile(logfile)

myapp

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")

If I try loggin.basicConfig()again, it doesn't work.

如果我再试loggin.basicConfig()一次,它不起作用。

采纳答案by Martijn Pieters

Indeed, logging.basicConfigdoes nothingif a handler has been set up already:

实际上,如果已经设置了处理程序,logging.basicConfig什么都不做:

This function does nothing if the root logger already has handlers configured for it.

如果根记录器已经为其配置了处理程序,则此函数不执行任何操作。

You'll need to replacethe current handler on the root logger:

您需要替换根记录器上的当前处理程序:

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

See the Configuring Logging chapterin the Python Logging HOWTO.

请参阅Python 日志记录 HOWTO 中的配置日志记录一章

回答by user2179204

I found an easier way than the above 'accepted' answer. If you have a reference to the handler, all you need to do is call the close() method and then set the baseFilename property. When you assign baseFilename, be sure to use os.path.abspath(). There's a comment in the library source that indicates it's needed. I keep my configuration stuff in a global dict() so it's easy to keep the FileHandler reference objects. As you can see below, it only takes 2 lines of code to change a log filename for a handler on the fly.

我找到了比上述“已接受”答案更简单的方法。如果您有对处理程序的引用,您需要做的就是调用 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

回答by Arun Thundyill Saseendran

The answer provided by @Martijn Pieters works good. However, the code snipper removes all handlers and placed only the file handler back. This will be troublesome if your application has handlers added by other modules.

@Martijn Pieters 提供的答案效果很好。但是,代码截取器会删除所有处理程序并仅将文件处理程序放回原处。如果您的应用程序具有由其他模块添加的处理程序,这将很麻烦。

Hence, the below snippet is designed in such a way to replace only the file handler.

因此,以下代码段的设计方式是仅替换文件处理程序。

The line if isinstance(hdlr,logging.FileHandler)is the key.

线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)

回答by T. Haggis

I tried to implemented the suggestions on this page from @Martijn Pieters combined with @Arun Thundyill Saseendran. I'm too new to be allowed to comment so I have to post an adjusted answer. In the isinstance call, I had to use 'logging' instead of 'log' to get access to the types (log was an instance) and then the 'FileHander' should be 'FileHandler'. I'm using Python 3.6.

我尝试将@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)