Подтвердить что ты не робот

Как изменить дескриптор файла с протоколом Python "на лету" с разными классами и импортом

Я не могу выполнить изменение файла записи на лету.

Например, у меня есть 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() снова, это не сработает.

4b9b3361

Ответ 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

См. раздел Настройка ведения журнала в HOWTO журнала Python.

Ответ 2

Я нашел более простой способ, чем вышеупомянутый "принятый" ответ. Если у вас есть ссылка на обработчик, все, что вам нужно сделать, это вызвать метод 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

Ответ 3

Ответ, предоставленный @Martijn Pieters, хорош. Тем не менее, snipper кода удаляет все обработчики и помещает только обработчик файлов обратно. Это будет проблематичным, если в вашем приложении есть обработчики, добавленные другими модулями.

Следовательно, приведенный ниже фрагмент разработан таким образом, чтобы заменить только обработчик файла.

Строкой if isinstance(hdlr,log.FileHander) является клавиша.

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,log.FileHander):
        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)      

Ответ 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)