web-dev-qa-db-ja.com

Pythonさまざまなクラスとインポートでオンザフライでロギングするファイルハンドルを変更する方法

オンザフライのロギングfileHandleの変更を実行できません。

たとえば、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()を再試行すると、機能しません。

40
xkill

確かに、 logging.basicConfigは、ハンドラーが既にセットアップされている場合、nothingを行います。

ルートロガーにすでにハンドラーが設定されている場合、この関数は何もしません。

ルートロガーの現在のハンドラーをreplaceする必要があります。

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

Python Logging HOWTO。の ロギングの設定の章 を参照してください。

54
Martijn Pieters

上記の「受け入れられた」答えよりも簡単な方法を見つけました。ハンドラーへの参照がある場合は、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
7
user2179204

@Martijn Pietersが提供する答えはうまくいきます。ただし、コードスニッパーはすべてのハンドラーを削除し、ファイルハンドラーのみを戻します。アプリケーションに他のモジュールによって追加されたハンドラーがある場合、これは面倒です。

したがって、以下のスニペットは、ファイルハンドラーのみを置き換えるように設計されています。

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)      

このページの提案を、@ Arun Thundyill Saseendranと組み合わせた@Martijn Pietersから実装しようとしました。私はあまりにも新しいのでコメントできませんので、調整済みの回答を投稿する必要があります。 isinstance呼び出しでは、型にアクセスするために「ログ」ではなく「ログ」を使用する必要があり(ログはインスタンスでした)、「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)      
0
T. Haggis