Как изменить дескриптор файла с протоколом 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()
снова, это не сработает.
Ответы
Ответ 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)