質問

私のアプリケーションにはログレベル TRACE (5) を使用したいと考えています。 debug() 十分なものです。さらに log(5, msg) それは私が望むものではありません。カスタム ログレベルを Python ロガーに追加するにはどうすればよいですか?

私は持っています mylogger.py 次の内容で:

import logging

@property
def log(obj):
    myLogger = logging.getLogger(obj.__class__.__name__)
    return myLogger

私のコードでは、次の方法で使用します。

class ExampleClass(object):
    from mylogger import log

    def __init__(self):
        '''The constructor with the logger'''
        self.log.debug("Init runs")

今、電話したいのですが self.log.trace("foo bar")

よろしくお願いいたします。

編集 (2016 年 12 月 8 日):受け入れられた回答を次のように変更しました PFAの これは、私の意見では、Eric S. 氏の非常に優れた提案に基づいた優れたソリューションです。

役に立ちましたか?

解決

@Eric Sます。

エリックS.の答えは素晴らしいですが、私は、これは常にメッセージが印刷される新しいデバッグレベルでログに記録されます原因という実験によって学んだ - に関係なく、ログレベルに設定されているものの。あなたが9の新しいレベルの数を作るもしそうなら、あなたがsetLevel(50)を低いレベルを呼び出す場合、のメッセージが誤って印刷されます。

  あなたが問題になっているログレベルが実際に有効になっているかどうかを確認するために、「debugv」関数内で別の行を必要とし、起こってからそれを防ぐために。

:ログレベルが有効になっているかどうかをチェックすることを

固定例

import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    if self.isEnabledFor(DEBUG_LEVELV_NUM):
        # Yes, logger takes its '*args' as 'args'.
        self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv

あなたは、Python 2.7用class Loggerlogging.__init__.pyためのコードを見れば、これはすべての標準のログ機能が何をすべきかです(.critical、.DEBUG、など。)。

彼はこれを見れば、 I評判の不足のために他の人の回答に明らかにすることはできませんポストの返信...うまくいけば、エリックは彼のポストを更新します。 =)

他のヒント

私は「避ける見ラムダ」の答えを取り、log_at_my_log_levelが追加されていた場所を変更する必要がありました。私もパウロは「私はこの作品とは思わない。あなたはlog_at_my_log_levelの最初の引数としてロガー必要はないのですか?」ということが問題を見てこれは私のために働いた。

import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv

既存のすべての回答と多数の使用経験を組み合わせることで、新しいレベルを完全にシームレスに使用できるようにするために必要なすべてのことのリストを思いついたと思います。以下の手順は、新しいレベルを追加していることを前提としています。 TRACE 価値のあるもの logging.DEBUG - 5 == 5:

  1. logging.addLevelName(logging.DEBUG - 5, 'TRACE') 名前で参照できるように、内部的に登録された新しいレベルを取得するには、を呼び出す必要があります。
  2. 新しいレベルを属性として追加する必要があります。 logging 一貫性のためにそれ自体: logging.TRACE = logging.DEBUG - 5.
  3. と呼ばれるメソッド trace に追加する必要があります logging モジュール。次のように動作するはずです debug, info, 、など。
  4. と呼ばれるメソッド trace 現在構成されているロガー クラスに追加する必要があります。100%確実にそうなるわけではないので、 logging.Logger, 、 使用 logging.getLoggerClass() その代わり。

すべての手順を以下の方法で示します。

def addLoggingLevel(levelName, levelNum, methodName=None):
    """
    Comprehensively adds a new logging level to the `logging` module and the
    currently configured logging class.

    `levelName` becomes an attribute of the `logging` module with the value
    `levelNum`. `methodName` becomes a convenience method for both `logging`
    itself and the class returned by `logging.getLoggerClass()` (usually just
    `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is
    used.

    To avoid accidental clobberings of existing attributes, this method will
    raise an `AttributeError` if the level name is already an attribute of the
    `logging` module or if the method name is already present 

    Example
    -------
    >>> addLoggingLevel('TRACE', logging.DEBUG - 5)
    >>> logging.getLogger(__name__).setLevel("TRACE")
    >>> logging.getLogger(__name__).trace('that worked')
    >>> logging.trace('so did this')
    >>> logging.TRACE
    5

    """
    if not methodName:
        methodName = levelName.lower()

    if hasattr(logging, levelName):
       raise AttributeError('{} already defined in logging module'.format(levelName))
    if hasattr(logging, methodName):
       raise AttributeError('{} already defined in logging module'.format(methodName))
    if hasattr(logging.getLoggerClass(), methodName):
       raise AttributeError('{} already defined in logger class'.format(methodName))

    # This method was inspired by the answers to Stack Overflow post
    # http://stackoverflow.com/q/2183233/2988730, especially
    # http://stackoverflow.com/a/13638084/2988730
    def logForLevel(self, message, *args, **kwargs):
        if self.isEnabledFor(levelNum):
            self._log(levelNum, message, *args, **kwargs)
    def logToRoot(message, *args, **kwargs):
        logging.log(levelNum, message, *args, **kwargs)

    logging.addLevelName(levelNum, levelName)
    setattr(logging, levelName, levelNum)
    setattr(logging.getLoggerClass(), methodName, logForLevel)
    setattr(logging, methodName, logToRoot)

この質問はかなり古いですが、私はちょうど同じトピックを扱って、私に少しクリーナーを表示され、既に述べたものへの道の似を発見しました。私は確認の方法は、古いバージョンに存在するかどうかを使用していないよので、これは、3.4でテストされています:

from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET

VERBOSE = 5

class MyLogger(getLoggerClass()):
    def __init__(self, name, level=NOTSET):
        super().__init__(name, level)

        addLevelName(VERBOSE, "VERBOSE")

    def verbose(self, msg, *args, **kwargs):
        if self.isEnabledFor(VERBOSE):
            self._log(VERBOSE, msg, args, **kwargs)

setLoggerClass(MyLogger)

内部メソッド(self._log)を使用しての悪い習慣を開始し、理由に基づいて、それぞれの答えは?

:神託のソリューションを使用すると、任意の内部のものを台無しにする必要はありませんので、代わりにself.logを使用することです
import logging

SUBDEBUG = 5
logging.addLevelName(SUBDEBUG, 'SUBDEBUG')

def subdebug(self, message, *args, **kws):
    self.log(SUBDEBUG, message, *args, **kws) 
logging.Logger.subdebug = subdebug

logging.basicConfig()
l = logging.getLogger()
l.setLevel(SUBDEBUG)
l.subdebug('test')
l.setLevel(logging.DEBUG)
l.subdebug('test')

私はそれが簡単にログ()関数を渡しロガーオブジェクトの新しい属性を作成するために見つけます。私はロガーモジュールがaddLevelName()とまさにこの理由のためにログを()を提供だと思います。したがって、何のサブクラスまたは新しい方法が必要ありません。

import logging

@property
def log(obj):
    logging.addLevelName(5, 'TRACE')
    myLogger = logging.getLogger(obj.__class__.__name__)
    setattr(myLogger, 'trace', lambda *args: myLogger.log(5, *args))
    return myLogger

今すぐ

mylogger.trace('This is a trace message')

期待通りに動作するはずです。

私はあなたがLoggerクラスをサブクラス化し、基本的にtraceよりも低いレベルでLogger.logを呼び出すDEBUGというメソッドを追加する必要がありますと思います。私はこれを試していないが、これはドキュメントはを示すものです。

カスタム ロガーを作成するためのヒント:

  1. 使ってはいけません _log, 、 使用 log (チェックする必要はありません isEnabledFor)
  2. ロギング モジュールはカスタム ロガーのインスタンスを作成するモジュールである必要があります。これは、ロギング モジュールがいくつかの魔法を実行するためです。 getLogger, 、したがって、次のようにクラスを設定する必要があります setLoggerClass
  3. 定義する必要はありません __init__ ロガーの場合、何も保存していない場合はクラス
# Lower than debug which is 10
TRACE = 5
class MyLogger(logging.Logger):
    def trace(self, msg, *args, **kwargs):
        self.log(TRACE, msg, *args, **kwargs)

このロガーを呼び出すときは、次のように使用します setLoggerClass(MyLogger) これをデフォルトのロガーにするには getLogger

logging.setLoggerClass(MyLogger)
log = logging.getLogger(__name__)
# ...
log.trace("something specific")

必要となるのは、 setFormatter, setHandler, 、 そして setLevel(TRACE)handler そして上に log それ自体がこの低レベルのトレースを実際に設定する

これは私のために働いた。

import logging
logging.basicConfig(
    format='  %(levelname)-8.8s %(funcName)s: %(message)s',
)
logging.NOTE = 32  # positive yet important
logging.addLevelName(logging.NOTE, 'NOTE')      # new level
logging.addLevelName(logging.CRITICAL, 'FATAL') # rename existing

log = logging.getLogger(__name__)
log.note = lambda msg, *args: log._log(logging.NOTE, msg, args)
log.note('school\'s out for summer! %s', 'dude')
log.fatal('file not found.')
@marqueedが指摘したように、

ラムダ/ FUNCNAMEの問題がlogger._logで固定されています。私は、ラムダは少しクリーナーを使用して見えると思いますが、欠点は、それがキーワード引数を取ることができないということです。私は自分自身、そういない大物ことを使用したことがありません。

  NOTE     setup: school's out for summer! dude
  FATAL    setup: file not found.

私の経験では、これはOPの問題の完全なソリューションです...メッセージが放出されている機能として「ラムダ」を見て避けるために、より深く行くます:

MY_LEVEL_NUM = 25
logging.addLevelName(MY_LEVEL_NUM, "MY_LEVEL_NAME")
def log_at_my_log_level(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    self._log(MY_LEVEL_NUM, message, args, **kws)
logger.log_at_my_log_level = log_at_my_log_level

私は、スタンドアロンのロガークラスで作業しようとしたことがありませんが、私は基本的な考え方は同じ(使用_log)だと思います。

マッド物理学者の例に加えて、正しいファイル名と行番号を取得する:

def logToRoot(message, *args, **kwargs):
    if logging.root.isEnabledFor(levelNum):
        logging.root._log(levelNum, message, args, **kwargs)
私たちはすでにたくさん正解の持っているが、

は、以下が私の意見では、よりニシキヘビです。

import logging

from functools import partial, partialmethod

logging.TRACE = 5
logging.addLevelName(logging.TRACE, 'TRACE')
logging.Logger.trace = partialmethod(logging.Logger.log, logging.TRACE)
logging.trace = partial(logging.log, logging.TRACE)

あなたのコードにmypyを使用したい場合は、それは属性を追加することから抑制の警告に# type: ignoreを追加することが推奨されます。

私はLogger.log(level, msg)メソッドを使用することをお勧めしますLoggerクラスに特別なメソッドを追加することの代替としてます。

import logging

TRACE = 5
logging.addLevelName(TRACE, 'TRACE')
FORMAT = '%(levelname)s:%(name)s:%(lineno)d:%(message)s'


logging.basicConfig(format=FORMAT)
l = logging.getLogger()
l.setLevel(TRACE)
l.log(TRACE, 'trace message')
l.setLevel(logging.DEBUG)
l.log(TRACE, 'disabled trace message')

私は混乱しています。 python 3.5で、少なくとも、それだけで動作します:

import logging


TRACE = 5
"""more detail than debug"""

logging.basicConfig()
logging.addLevelName(TRACE,"TRACE")
logger = logging.getLogger('')
logger.debug("n")
logger.setLevel(logging.DEBUG)
logger.debug("y1")
logger.log(TRACE,"n")
logger.setLevel(TRACE)
logger.log(TRACE,"y2")

出力:

  

DEBUG:ルート:Y1

     

TRACE:ルート:Y2

固定回答に基づいて

、 私はautomaticaly新しいロギングレベルを作成少し方法を書きました。

def set_custom_logging_levels(config={}):
    """
        Assign custom levels for logging
            config: is a dict, like
            {
                'EVENT_NAME': EVENT_LEVEL_NUM,
            }
        EVENT_LEVEL_NUM can't be like already has logging module
        logging.DEBUG       = 10
        logging.INFO        = 20
        logging.WARNING     = 30
        logging.ERROR       = 40
        logging.CRITICAL    = 50
    """
    assert isinstance(config, dict), "Configuration must be a dict"

    def get_level_func(level_name, level_num):
        def _blank(self, message, *args, **kws):
            if self.isEnabledFor(level_num):
                # Yes, logger takes its '*args' as 'args'.
                self._log(level_num, message, args, **kws) 
        _blank.__name__ = level_name.lower()
        return _blank

    for level_name, level_num in config.items():
        logging.addLevelName(level_num, level_name.upper())
        setattr(logging.Logger, level_name.lower(), get_level_func(level_name, level_num))

コンフィグそのようになめらかあります:

new_log_levels = {
    # level_num is in logging.INFO section, that's why it 21, 22, etc..
    "FOO":      21,
    "BAR":      22,
}

の場合には、誰もがロギングモジュール(またはそのコピー)に新しいログレベルを追加するための自動化された方法を望んで動的に、私は、PFAの答え@拡大し、この関数を作成しています:

def add_level(log_name,custom_log_module=None,log_num=None,
                log_call=None,
                   lower_than=None, higher_than=None, same_as=None,
              verbose=True):
    '''
    Function to dynamically add a new log level to a given custom logging module.
    <custom_log_module>: the logging module. If not provided, then a copy of
        <logging> module is used
    <log_name>: the logging level name
    <log_num>: the logging level num. If not provided, then function checks
        <lower_than>,<higher_than> and <same_as>, at the order mentioned.
        One of those three parameters must hold a string of an already existent
        logging level name.
    In case a level is overwritten and <verbose> is True, then a message in WARNING
        level of the custom logging module is established.
    '''
    if custom_log_module is None:
        import imp
        custom_log_module = imp.load_module('custom_log_module',
                                            *imp.find_module('logging'))
    log_name = log_name.upper()
    def cust_log(par, message, *args, **kws):
        # Yes, logger takes its '*args' as 'args'.
        if par.isEnabledFor(log_num):
            par._log(log_num, message, args, **kws)
    available_level_nums = [key for key in custom_log_module._levelNames
                            if isinstance(key,int)]

    available_levels = {key:custom_log_module._levelNames[key]
                             for key in custom_log_module._levelNames
                            if isinstance(key,str)}
    if log_num is None:
        try:
            if lower_than is not None:
                log_num = available_levels[lower_than]-1
            elif higher_than is not None:
                log_num = available_levels[higher_than]+1
            elif same_as is not None:
                log_num = available_levels[higher_than]
            else:
                raise Exception('Infomation about the '+
                                'log_num should be provided')
        except KeyError:
            raise Exception('Non existent logging level name')
    if log_num in available_level_nums and verbose:
        custom_log_module.warn('Changing ' +
                                  custom_log_module._levelNames[log_num] +
                                  ' to '+log_name)
    custom_log_module.addLevelName(log_num, log_name)

    if log_call is None:
        log_call = log_name.lower()

    setattr(custom_log_module.Logger, log_call, cust_log)
    return custom_log_module
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top