web-dev-qa-db-ja.com

アプリケーションを再起動せずにログレベルを動的に変更する

pythonでfileConfigを使用してログレベルを変更することはできますか?

更新:これはサーバーで実行されているアプリケーション用で、システム管理者が実行時にアプリケーションによって選択される構成ファイルを変更し、ログレベルを動的に変更できるようにしたかったのです。当時私はgeventで作業していたので、inotifyを使用して構成ファイルへの変更を選択する回答の1つとしてコードを追加しました。

53
opensourcegeek

fileConfigは、ファイルに基づいてログレベルを設定するメカニズムです。プログラム内でいつでも動的に変更できます。

ログレベルを変更するロギングオブジェクトで .setLevel() を呼び出します。通常、ルートでそれを行います:

logging.getLogger().setLevel(logging.DEBUG)
95
Martijn Pieters

受け入れられた答えに加えて、ロガーを初期化した方法に応じて、ロガーのハンドラーも更新する必要があります。

import logging

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

fileConfig()を使用してその場でロギング構成を変更することは確かに可能ですが、単純な変更の場合は、Martijn Pietersの答えで提案されているプログラムによるアプローチが適切かもしれません。ロギングは、文書化された here のように、listen()/stopListening() AP​​Iを使用して設定変更をリッスンするソケットサーバーも提供します。特定のポートでリッスンするロギングを取得するには、使用します

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を使用して、dictConfigスキーマを使用してJSONファイルを送信できます。これにより、再構成をより適切に制御できます。これにはPython 2.7/3.2以上(dictConfig()が追加された場所)が必要です。または、stdlibコードを使用して、同じ方法で動作する独自のリスナーを実装できますが、特定のニーズに合わせて調整されます。

6
Vinay Sajip

これはあなたが探しているものかもしれません:

_import logging
logging.getLogger().setLevel(logging.INFO)
_

引数なしで呼び出されたgetLogger()はルートロガーを返すことに注意してください。

2
Rolando Max

最終的に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で使用しようとしている場合に役立つかもしれません。

2
opensourcegeek

sfinken's 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')
1
DrOffler

アプリに応じて、実行中に独自の構成ファイルに基づいてそのファイルをリロードする方法またはログレベルをリセットする方法を最初に見つける必要があります。

最も簡単な方法は、タイマーを使用することです。スレッド化を使用してそれを実行するか、非同期フレームワークを使用して実行します(使用する場合、通常は実装します)。

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によって提案されたソリューションを確認してください。

0
Mihai