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

Динамическое изменение уровня журнала без перезапуска приложения

Можно ли изменить уровень журнала с помощью fileConfig в python без перезапуска приложения. Если он не может быть достигнут с помощью fileConfig, есть ли другой способ получить тот же результат?

Обновление: это было для приложения, работающего на сервере, я хотел, чтобы администраторы sys могли изменять файл конфигурации, который будет выбран во время выполнения приложения, и динамически изменять уровень журнала. В то время я работал с gevent, поэтому я добавил свой код в качестве одного из ответов, который использует inotify для выбора изменений в файле конфигурации.

4b9b3361

Ответ 1

fileConfig - это механизм настройки уровня журнала для вас на основе файла; вы можете динамически изменять его в любое время в своей программе.

Вызовите .setLevel() для объекта ведения журнала, для которого вы хотите изменить уровень журнала. Обычно вы делаете это в корне:

logging.getLogger().setLevel(logging.DEBUG)

Ответ 2

В дополнение к принятому ответу: в зависимости от того, как вы инициализировали регистратор, вам также может потребоваться обновить обработчики журналов:

import logging

level = logging.DEBUG
logger = logging.getLogger()
logger.setLevel(level)
for handler in logger.handlers:
    handler.setLevel(level)

Ответ 3

Конечно, можно использовать fileConfig() для изменения конфигурации журналирования на лету, хотя для простых изменений может подойти программный подход, предложенный в ответе Мартина Питерса. Ведение журнала даже предоставляет сокет-сервер для прослушивания изменений конфигурации с помощью API listen()/stopListening(), как stopListening() здесь. Чтобы получить запись для прослушивания определенного порта, вы используете

t = logging.config.listen(PORT_NUMBER)
t.start()

и чтобы прекратить слушать, позвоните

logging.config.stopListening()

Для отправки данных на сервер вы можете использовать, например,

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', PORT_NUMBER))
with open(CONFIG_FILE) as f:
    data_to_send = f.read()
s.send(struct.pack('>L', len(data_to_send)))
s.send(data_to_send)
s.close()

Обновление: из-за ограничений обратной совместимости внутренняя реализация fileConfig() означает, что вы не можете указать disable_existing_loggers=False в вызове, что делает эту функцию менее полезной в определенных сценариях. Вы можете использовать тот же API для отправки файла JSON, используя схему dictConfig, что позволит лучше контролировать реконфигурацию. Для этого требуется Python 2.7/3.2 или выше (где был добавлен dictConfig()). Или вы можете использовать код stdlib для реализации вашего собственного слушателя, который работает таким же образом, но с учетом ваших конкретных потребностей.

Ответ 4

Это может быть то, что вы ищете:

import logging
logging.getLogger().setLevel(logging.INFO)

Обратите внимание, что getLogger(), вызываемый без каких-либо аргументов, возвращает корневой журнал.

Ответ 5

Я, наконец, решил использовать inotify и gevent для проверки операции записи файла, и как только я знаю, что файл был изменен, я перехожу и устанавливаю уровень для каждого регистратора, на котором я основываюсь на конфигурации.

import gevent
import gevent_inotifyx as inotify
from gevent.queue import Queue

class FileChangeEventProducer(gevent.Greenlet):
    def __init__(self, fd, queue):
        gevent.Greenlet.__init__(self)
        self.fd = fd
        self.queue = queue

    def _run(self):
        while True:
            events = inotify.get_events(self.fd)
            for event in events:
                self.queue.put(event)
                gevent.sleep(0)


class FileChangeEventConsumer(gevent.Greenlet):
    def __init__(self, queue, callBack):
        gevent.Greenlet.__init__(self)
        self.queue = queue
        self.callback = callBack

    def _run(self):
        while True:
            _ = self.queue.get()
            self.callback()
            gevent.sleep(0)


class GeventManagedFileChangeNotifier:
    def __init__(self, fileLocation, callBack):
        self.fileLocation = fileLocation
        self.callBack = callBack
        self.queue = Queue()
        self.fd = inotify.init()
        self.wd = inotify.add_watch(self.fd, self.fileLocation, inotify.IN_CLOSE_WRITE)


    def start(self):
        producer = FileChangeEventProducer(self.fd, self.queue)
        producer.start()
        consumer = FileChangeEventConsumer(self.queue, self.callBack)
        consumer.start()
        return (producer, consumer)

Приведенный выше код используется, как показано ниже,

    def _setUpLoggingConfigFileChangeNotifier(self):
        loggingFileNameWithFullPath = self._getFullPathForLoggingConfig()
        self.gFsNotifier = GeventManagedFileChangeNotifier(loggingFileNameWithFullPath, self._onLogConfigChanged)
        self.fsEventProducer, self.fsEventConsumer = self.gFsNotifier.start()


    def _onLogConfigChanged(self):
        self.rootLogger.info('Log file config has changed - examining the changes')
        newLoggingConfig = Config(self.resourcesDirectory, [self.loggingConfigFileName]).config.get('LOG')
        self.logHandler.onLoggingConfigChanged(newLoggingConfig)

Как только у меня будет новая конфигурация файла журнала, я могу подключиться к правильному уровню ведения журнала для каждого регистратора из конфигурации. Я просто хотел поделиться этим ответом, и это может помочь кому-то, если они пытаются использовать его с gevent.

Ответ 6

Развернув sfinken answer и последующий комментарий Starman, вы также можете проверить тип обработчика, нацеленного на конкретный выходной файл, например:

import logging
logger = logging.getLogger()
for handler in logger.handlers:
    if isinstance(handler, type(logging.StreamHandler())):
        handler.setLevel(logging.DEBUG)
        logger.debug('Debug logging enabled')

Ответ 7

В зависимости от вашего приложения сначала необходимо найти способ перезагрузки этого файла или сброса уровня журнала в зависимости от вашего собственного файла конфигурации во время выполнения.

Самый простой способ - использовать таймер. Либо используйте потоки, чтобы сделать это, либо сделайте свою инфраструктуру async, чтобы сделать это (если вы используете какой-либо, они обычно реализуют его).

Использование threading.Timer:

import threading
import time


def reset_level():
    # you can reload your own config file or use logging.config.fileConfig here
    print 'Something else'
    pass


t = threading.Timer(10, reset_level)
t.start()

while True:
    # your app code
    print 'Test'
    time.sleep(2)

Вывод:

Test
Test
Test
Test
Test
Something else
Test
Test

Обновление: Пожалуйста, проверьте решение, предложенное Martijn Pieters.