web-dev-qa-db-ja.com

複数のモジュールでPythonロギングを使用する

私は次のような構造を持つ小さなPythonプロジェクトを持っています -

Project 
 -- pkg01
   -- test01.py
 -- pkg02
   -- test02.py
 -- logging.conf

私はデフォルトのloggingモジュールを使って標準出力とログファイルにメッセージを出力することを計画しています。ロギングモジュールを使用するには、何らかの初期化が必要です -

import logging.config

logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')

logger.info('testing')

現在、メッセージのロギングを開始する前に、すべてのモジュールでこの初期化を実行しています。プロジェクト全体でログを記録して同じ設定を再利用するように、この初期化を一箇所で一度だけ実行することはできますか

190
Quest Monger

ベストプラクティスは、各モジュールで、次のようにロガーを定義することです。

import logging
logger = logging.getLogger(__name__)

モジュールの上部付近、そしてモジュール内の他のコードでは、.

logger.debug('My message with %s', 'variable data')

モジュール内でロギングアクティビティを細分化する必要がある場合は、例えば.

loggerA = logging.getLogger(__+ '.A')
loggerB = logging.getLogger(__+ '.B')

そして、必要に応じてloggerAおよびloggerBにログインします。

あなたのメインプログラムでは、例えば:

def main():
    "your program code"

if __== '__main__':
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    main()

または

def main():
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    # your program code

if __== '__main__':
    main()

複数のモジュールからのロギングについては here を、他のコードによってライブラリモジュールとして使用されるコードのロギング設定については here を参照してください。

更新:fileConfig()を呼ぶとき、Python 2.6以降を使っているならdisable_existing_loggers=Falseを指定したいかもしれません( the docs を見てください)詳細については)。下位互換性を保つため、デフォルト値はTrueです。これにより、既存のすべてのロガーがその設定で明示的に指定されていない限り、fileConfig()によって無効になります。値をFalseに設定すると、既存のロガーはそのまま残されます。 Python 2.7/Python 3.2以降を使用している場合は、dictConfig() APIよりも優れているfileConfig() APIを検討するとよいでしょう。

225
Vinay Sajip

実際には、すべてのロガーは親のパッケージロガーの子です(つまり、package.subpackage.modulepackage.subpackage)から設定を継承します。したがって、ルートロガーを設定するだけで済みます。これは logging.config.fileConfig (自分の設定で実現できます) (ロガーの場合)または logging.basicConfig (ルートロガーを設定します)エントリモジュールでのロギングの設定(__main__.pyまたは実行したいものは何でも。たとえばmain_script.py__init__.pyも同様に機能します)

basicConfigを使用して:

# package/__main__.py
import logging
import sys

logging.basicConfig(stream=sys.stdout, level=logging.INFO)

fileConfigを使う:

# package/__main__.py
import logging
import logging.config

logging.config.fileConfig('logging.conf')

そして、次のようにしてすべてのロガーを作成します。

# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)

log.info("Hello logging!")

詳細については、 Advanced Logging Tutorial を参照してください。

92
Stan Prokop

私はいつも以下のようにします。

単一のpythonファイルを使用して、ログを 'log_conf.py'という名前のシングルトンパターンとして設定します。

#-*-coding:utf-8-*-

import logging.config

def singleton(cls):
    instances = {}
    def get_instance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return get_instance()

@singleton
class Logger():
    def __init__(self):
        logging.config.fileConfig('logging.conf')
        self.logr = logging.getLogger('root')

別のモジュールでは、設定をインポートするだけです。

from log_conf import Logger

Logger.logr.info("Hello World")

これは、簡単かつ効率的にログに記録するためのシングルトンパターンです。

21
Yarkee

これらの答えのいくつかはあなたがモジュールの一番上であなたがすることを示唆しています

import logging
logger = logging.getLogger(__name__)

これが非常に悪い習慣と考えられていることは私の理解です。その理由は、ファイルconfigがデフォルトで既存のすべてのロガーを無効にするためです。例えば。

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logger.info('Hi, foo')

class Bar(object):
    def bar(self):
        logger.info('Hi, bar')

そしてあなたのメインモジュールで:

#main
import logging

# load my module - this now configures the logger
import my_module

# This will now disable the logger in my module by default, [see the docs][1] 
logging.config.fileConfig('logging.ini')

my_module.foo()
bar = my_module.Bar()
bar.bar()

File.ini呼び出しで既存のロガーが無効化されたため、logging.iniで指定されたログは空になります。

これを回避することは確かに可能ですが(disable_existing_Loggers = False)、実際にはあなたのライブラリの多くのクライアントはこの振る舞いを知らず、あなたのログを受け取らないでしょう。常にローカルでlogging.getLoggerを呼び出すことで、クライアントにとって簡単にすることができます。 Hat Tip: Victor Linのウェブサイト からこの振る舞いについて学びました。

そのため、代わりに常にlogging.getLoggerをローカルに呼び出すことをお勧めします。例えば。

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logging.getLogger(__name__).info('Hi, foo')

class Bar(object):
    def bar(self):
        logging.getLogger(__name__).info('Hi, bar')    

また、メインでfileconfigを使用する場合は、ライブラリ設計者がモジュールレベルのロガーインスタンスを使用する場合に備えて、disable_existing_loggers = Falseを設定してください。

8
phil_20686

@ Yarkeeの解決策は良さそうだった。もう少し加えたいと思います -

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances.keys():
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class LoggerManager(object):
    __metaclass__ = Singleton

    _loggers = {}

    def __init__(self, *args, **kwargs):
        pass

    @staticmethod
    def getLogger(name=None):
        if not name:
            logging.basicConfig()
            return logging.getLogger()
        Elif name not in LoggerManager._loggers.keys():
            logging.basicConfig()
            LoggerManager._loggers[name] = logging.getLogger(str(name))
        return LoggerManager._loggers[name]    


log=LoggerManager().getLogger("Hello")
log.setLevel(level=logging.DEBUG)

そのため、LoggerManagerはアプリケーション全体にプラグイン可能です。それが理にかなった、そして価値があることを願っています。

5
deeshank

別の解決策で投げます。

私のモジュールのメインではinit私は以下のようなものを持っています:

import logging

def get_module_logger(mod_name):
  logger = logging.getLogger(mod_name)
  handler = logging.StreamHandler()
  formatter = logging.Formatter(
        '%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
  handler.setFormatter(formatter)
  logger.addHandler(handler)
  logger.setLevel(logging.DEBUG)
  return logger

それから各クラスで私はロガーを必要とします、私はします:

from [modname] import get_module_logger
logger = get_module_logger(__name__)

ログが見逃されたとき、あなたはそれらが由来するモジュールによってそれらのソースを区別することができます。

5
Tommy

このようなことを思いつくこともできます。

def get_logger(name=None):
    default = "__app__"
    formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s',
                              datefmt='%Y-%m-%d %H:%M:%S')
    log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"}
    if name:
        logger = logging.getLogger(name)
    else:
        logger = logging.getLogger(default)
    fh = logging.FileHandler(log_map[name])
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.setLevel(logging.DEBUG)
    return logger

上記が別のモジュールで定義されていて、他のモジュールにインポートされている場合は、同じモジュール内でプロジェクト全体で複数のロガーを使用することができます。

a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
4
deeshank

いくつかの答えがあります。私は私にとって理にかなっているが、多分それはあなたにも理にかなっているかもしれませんが似たようなまだ別のソリューションになった。私の主な目的はそれらのレベル(コンソールへのデバッグレベルログ、ファイルへの警告以上)でログをハンドラに渡すことができるようにすることでした:

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# make default logger output everything to the console
logging.basicConfig(level=logging.DEBUG)

rotating_file_handler = RotatingFileHandler(filename="logs.log")
rotating_file_handler.setLevel(logging.INFO)

app.logger.addHandler(rotating_file_handler)

logger.pyという名前のNice utilファイルを作成しました。

import logging

def get_logger(name):
    return logging.getLogger("flask.app." + name)

flask.appはflaskにハードコードされた値です。アプリケーションロガーはいつもflask.appをモジュール名として始めています。

今、各モジュールでは、私は次のモードでそれを使用することができます:

from logger import get_logger
logger = get_logger(__name__)

logger.info("new log")

これにより、最小限の労力で "app.flask.MODULE_NAME"の新しいログが作成されます。

2
Ben Yitzhaki

ベストプラクティスは、呼び出し側のメソッドにロガーハンドラを渡すというタスクを持つメソッドを1つだけ持つモジュールを個別に作成することです。このファイルをm_logger.pyとして保存します

import logger, logging

def getlogger():
    # logger
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    # create console handler and set level to debug
    #ch = logging.StreamHandler()
    ch = logging.FileHandler(r'log.txt')
    ch.setLevel(logging.DEBUG)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    # add formatter to ch
    ch.setFormatter(formatter)
    # add ch to logger
    logger.addHandler(ch)
    return logger

ロガーハンドラが必要なときはいつでもgetlogger()メソッドを呼び出します。

from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')
1
Mousam Singh