web-dev-qa-db-ja.com

Python:すべてのモジュールにわたるカスタムロギング

タスク

スクリプトのコレクションがあり、実際のメッセージのロギングを行うモジュールへの変更を最小限に抑えて、統合されたロギングメッセージを生成するようにしたいと思います。

メインアプリケーションから一度呼び出す予定の小さなモジュール「custom_logger」を作成し、ロガーを返してもらい、それを引き続き使用します。

私がアプリにインポートするサブモジュールは、(またはむしろ私が望むように)のみすべきです

  • 「ログをログとしてインポートする」だけでよいので、他の誰かがそれらを有用だと思った場合にそれらを実行するために、私のサイトに固有のものは何も必要ありません。
  • サイト固有のものを追加せずに、log.info/error( 'message')を使用してメッセージをログに記録する必要があります
  • すでに構成されている「ルート」ロガーとそのすべてのフォーマットおよびハンドラーを使用し、ルートロガーの構成に影響を与えないようにする必要があります

* custom_logger.py *

_import logging
import logging.handlers
import os
import sys


def getLogger(name='root', loglevel='INFO'):
  logger = logging.getLogger(name)

  # if logger 'name' already exists, return it to avoid logging duplicate
  # messages by attaching multiple handlers of the same type
  if logger.handlers:
    return logger
  # if logger 'name' does not already exist, create it and attach handlers
  else:
    # set logLevel to loglevel or to INFO if requested level is incorrect
    loglevel = getattr(logging, loglevel.upper(), logging.INFO)
    logger.setLevel(loglevel)
    fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
    fmt_date = '%Y-%m-%dT%T%Z'
    formatter = logging.Formatter(fmt, fmt_date)
    handler = logging.StreamHandler()
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    if logger.name == 'root':
      logger.warning('Running: %s %s',
                     os.path.basename(sys.argv[0]),
                     ' '.join(sys.argv[1:]))
    return logger
_

次に、機能するものと機能しないものの例を含むいくつかのテストメッセージを含むサブモジュールがあります。

submodule.py

_import sys
import custom_logger
import logging


class SubClass(object):

  def __init__(self):
    # NOK (no idea why since by default (no name parameter), it should return the root logger)
    #log = logging.getLogger()
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    #log = logging.getLogger('root')
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / __init__')


  def SomeMethod(self):
    # OK but I'd have to define `log` for every method, which is unacceptable
    # Please see question below all code snippets
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / SomeMethod')
_

そしてメインアプリ:app.pyここでは特別なことは何もありません:

_#!/usr/bin/python

import custom_logger
import submodule

log = custom_logger.getLogger('root', loglevel='DEBUG')

log.debug('debug message')
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this
_

非常に醜い方法で、私が求めているものと私が得ているものを出力します。

_% ./app.py 
2013-04-08T03:07:46BST custom_logger.py   WARNING : Running: app.py 
2013-04-08T03:07:46BST app.py             DEBUG   : debug message
2013-04-08T03:07:46BST app.py             INFO    : info message
2013-04-08T03:07:46BST app.py             WARNING : warning message
2013-04-08T03:07:46BST app.py             ERROR   : error message
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / SomeMethod
_

App.pyでロガーを定義できるようにしたいのですが、サブモジュールでは、標準のPythonロギングライブラリのみを使用して、app.pyで既に構成されているロガーを利用します。

また、醜い回避策:submodule.pyのインポート後に以下のコードを配置した場合:

_log = custom_logger.getLogger('root')
_

これは、私のロガーがapp.pyで構成される前に実行され、アプリの構成ログではなく、サブモジュールを効果的に作成します。

私が検討した別の回避策:SubClassクラスのコンストラクター内で、次のように定義できます。

self.log = custom_logger.getLogger('root')

次に、self.log.error( 'some error')を使用します。もっと良い方法があるはずです-何か役に立つものを提案したり、私がドキュメントを誤解したところを指摘したりできれば、私はとても感謝しています!

PS。 Pythonロギングハウツー(基本および上級)とクックブック)をかなり読んだことがあるので、そこで役立つものを見逃した場合は、それを指摘してください。

ありがとうございました!

19
Marcin Kaminski

ルートロガーを変更したい場合は、引数なしでどこでもgetLogger()を使用できます。

メインモジュールでのみインスタンスをセットアップする場合は、ロガーをインスタンス化し、独自のハンドラーを追加して、他のすべてのサブモジュールで使用できます(以下で説明します)。

custom_logger.pyでStreamHandlerを継承するクラスを作成しました。

class MyHandler(logging.StreamHandler):

    def __init__(self):
        logging.StreamHandler.__init__(self)
        fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
        fmt_date = '%Y-%m-%dT%T%Z'
        formatter = logging.Formatter(fmt, fmt_date)
        self.setFormatter(formatter)

次に、submodule.pyで、インポートの後にgetLoggerを配置し、メソッドでコメントしました。

import sys
import logging

log = logging.getLogger('root')

class SubClass(object):

    def __init__(self):
         log.info('message from SubClass / __init__')

    def SomeMethod(self):
        log.info('message from SubClass / SomeMethod')

次に、app.pyでLoggerインスタンス(すべてのモジュールで同じ)を作成し、出力をフォーマットするハンドラーを追加しました。

#!/usr/bin/python

import logging
import custom_logger
import submodule

log = logging.getLogger('root')
log.setLevel('DEBUG')
log.addHandler(custom_logger.MyHandler())

log.debug('debug message') 
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this

出力:

./app.py 
2013-04-08T15:20:05EEST app.py             DEBUG   : debug message
2013-04-08T15:20:05EEST app.py             INFO    : info message
2013-04-08T15:20:05EEST app.py             WARNING : warning message
2013-04-08T15:20:05EEST app.py             ERROR   : error message
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / SomeMethod
8
Mihai