python loggingモジュール登録プロセス(logging.config.dictConfigプロセスを例に)

20565 ワード

python loggingモジュール登録プロセス(logging.config.dictConfigプロセスを例に)
最近python logging関連のモジュールを見てdictConfigという関数を使って追跡してみましたが、dictConfigを呼び出した後、これらは何が起こったのか、fileConfigを使う流れも悪くないと思います.dictConfigのロードフローを先に記載し、ソースコードを追跡してから更新します.
logging.config.dictconfigソース:
dictConfigClass = DictConfigurator

def dictConfig(config):
    """Configure logging using a dictionary."""
    dictConfigClass(config).configure()

1.コードにはconfigを実行する実際にはDictConfiguratorという例のconfigureメソッドが見られる.では、DictConfiguratorインスタンスの初期化について説明します.
class DictConfigurator(BaseConfigurator):
    ...

1.1DictConfiguratorBaseConfiguratorから継承され、その__init__メソッドを表示します.
class BaseConfigurator(object):
    ....

    def __init__(self, config):
        self.config = ConvertingDict(config)
        self.config.configurator = self

コードには2つの点がある:ConvertingDictのインスタンスself.义齿config.configuratorは現在のBaseconfiguratorインスタンスを参照しています.では、コードのConvertingDict(config)は何ですか.トレースを続行:
1.2ConvertingDict解析
class ConvertingDict(dict, ConvertingMixin):
   """A converting dictionary wrapper."""

   def __getitem__(self, key):
       value = dict.__getitem__(self, key)
       return self.convert_with_key(key, value)

   def get(self, key, default=None):
       value = dict.get(self, key, default)
       return self.convert_with_key(key, value)

   def pop(self, key, default=None):
       value = dict.pop(self, key, default)
       return self.convert_with_key(key, value, replace=False)
Convertingdictdictから継承され、dictの__getitem__getpopの3つの方法が書き換えられ、dictの元の方法が先に呼び出され、取得された値はself.convert_with_keyで変換され、そのコードはConvertingMixinクラスにある.
1.3ConvertingMixinクラス
class ConvertingMixin(object):
   """For ConvertingXXX's, this mixin class provides common functions"""

   def convert_with_key(self, key, value, replace=True):
       result = self.configurator.convert(value)
       #If the converted value is different, save for next time
       if value is not result:
           if replace:
               self[key] = result
           if type(result) in (ConvertingDict, ConvertingList,
                              ConvertingTuple):
               result.parent = self
               result.key = key
       return result

   def convert(self, value):
       result = self.configurator.convert(value)
       if value is not result:
           if type(result) in (ConvertingDict, ConvertingList,
                              ConvertingTuple):
               result.parent = self
       return result
convert_with_keyメソッドでは、参照するself.configuratorメソッドのconvertメソッドが毎回呼び出され、このフローではself.configuratorが現在のBaseconfiguratorインスタンスである.
1.4Baseconfiguratorconvertメソッド:
 def convert(self, value):
 
        """
        Convert values to an appropriate type. dicts, lists and tuples are
        replaced by their converting alternatives. Strings are checked to
        see if they have a conversion format and are converted if they do.
        """
        if not isinstance(value, ConvertingDict) and isinstance(value, dict):
            value = ConvertingDict(value)
            value.configurator = self
        elif not isinstance(value, ConvertingList) and isinstance(value, list):
            value = ConvertingList(value)
            value.configurator = self
        elif not isinstance(value, ConvertingTuple) and\
                 isinstance(value, tuple):
            value = ConvertingTuple(value)
            value.configurator = self
        elif isinstance(value, str): # str for py3k
            m = self.CONVERT_PATTERN.match(value)
            if m:
                d = m.groupdict()
                prefix = d['prefix']
                converter = self.value_converters.get(prefix, None)
                if converter:
                    suffix = d['suffix']
                    converter = getattr(self, converter)
                    value = converter(suffix)
        return value

前は再帰的で、valueタイプがstrタイプの場合にのみ変換され、他は再帰変換タイプです.
1.4.1 strデータの変換
    CONVERT_PATTERN = re.compile(r'^(?P[a-z]+)://(?P.*)$')
CONVERT_PATTERNは正則であり、マッチングはprefix://suffixのようなプロトコルに類似する.
次にvalue_converters:
    value_converters = {
        'ext' : 'ext_convert',
        'cfg' : 'cfg_convert',
    }

これは、extおよびcfgの2つのプロトコルに対応することが分かる.参照ext://プロトコル対応ext_convertメソッド:
    def ext_convert(self, value):
        """Default converter for the ext:// protocol."""
        return self.resolve(value)

実際にはself.resolve(value),selfが現在のBaseconfiguratorであり、resolveメソッドを表示します.
1.4.1Baseconfiguratorresolveメソッド:
    def resolve(self, s):
        """
        Resolve strings to objects using standard import and attribute
        syntax.
        """
        name = s.split('.')
        used = name.pop(0)
        try:
            found = self.importer(used)
            for frag in name:
                used += '.' + frag
                try:
                    found = getattr(found, frag)
                except AttributeError:
                    self.importer(used)
                    found = getattr(found, frag)
            return found
        except ImportError:
            e, tb = sys.exc_info()[1:]
            v = ValueError('Cannot resolve %r: %s' % (s, e))
            v.__cause__, v.__traceback__ = e, tb
            raise v

公式文書によると、extプロトコルは主に他のモジュールを参照するために使用され、例えばカスタムモジュールext://a.b.cであり、ソースコードを分析する:まず.号を通じて各セグメントを分割し、self.importerは実際には__import__関数であり、resolveメソッドはモジュールをループし、最後にモジュールを返す.
1.5まとめ:
  • Baseconfigurator初期化プロセスは、ConvertingDictインスタンスを初期化し、そのインスタンスのconfiguratorBaseconfigurator自身に向け、
  • ConvertingDictの役割:extを例に、対応するkey:value値を取得すると、プロトコル対応のモジュールに変換して使用します.

  • 2.DictConfiguratorconfigureメソッド:
    ソースコードは次のとおりです.
    """Do the configuration."""
    
            config = self.config
            if 'version' not in config:
                raise ValueError("dictionary doesn't specify a version")
            if config['version'] != 1:
                raise ValueError("Unsupported version: %s" % config['version'])
            incremental = config.pop('incremental', False)
            EMPTY_DICT = {}
            logging._acquireLock()
            try:
                if incremental:
                    handlers = config.get('handlers', EMPTY_DICT)
                    for name in handlers:
                        if name not in logging._handlers:
                            raise ValueError('No handler found with '
                                             'name %r'  % name)
                        else:
                            try:
                                handler = logging._handlers[name]
                                handler_config = handlers[name]
                                level = handler_config.get('level', None)
                                if level:
                                    handler.setLevel(logging._checkLevel(level))
                            except Exception as e:
                                raise ValueError('Unable to configure handler '
                                                 '%r: %s' % (name, e))
                    loggers = config.get('loggers', EMPTY_DICT)
                    for name in loggers:
                        try:
                            self.configure_logger(name, loggers[name], True)
                        except Exception as e:
                            raise ValueError('Unable to configure logger '
                                             '%r: %s' % (name, e))
                    root = config.get('root', None)
                    if root:
                        try:
                            self.configure_root(root, True)
                        except Exception as e:
                            raise ValueError('Unable to configure root '
                                             'logger: %s' % e)
                else:
                    disable_existing = config.pop('disable_existing_loggers', True)
    
                    logging._handlers.clear()
                    del logging._handlerList[:]
    
                    # Do formatters first - they don't refer to anything else
                    formatters = config.get('formatters', EMPTY_DICT)
                    for name in formatters:
                        try:
                            formatters[name] = self.configure_formatter(
                                                                formatters[name])
                        except Exception as e:
                            raise ValueError('Unable to configure '
                                             'formatter %r: %s' % (name, e))
                    # Next, do filters - they don't refer to anything else, either
                    filters = config.get('filters', EMPTY_DICT)
                    for name in filters:
                        try:
                            filters[name] = self.configure_filter(filters[name])
                        except Exception as e:
                            raise ValueError('Unable to configure '
                                             'filter %r: %s' % (name, e))
    
                    # Next, do handlers - they refer to formatters and filters
                    # As handlers can refer to other handlers, sort the keys
                    # to allow a deterministic order of configuration
                    handlers = config.get('handlers', EMPTY_DICT)
                    deferred = []
                    for name in sorted(handlers):
                        try:
                            handler = self.configure_handler(handlers[name])
                            handler.name = name
                            handlers[name] = handler
                        except Exception as e:
                            if 'target not configured yet' in str(e):
                                deferred.append(name)
                            else:
                                raise ValueError('Unable to configure handler '
                                                 '%r: %s' % (name, e))
    
                    # Now do any that were deferred
                    for name in deferred:
                        try:
                            handler = self.configure_handler(handlers[name])
                            handler.name = name
                            handlers[name] = handler
                        except Exception as e:
                            raise ValueError('Unable to configure handler '
                                             '%r: %s' % (name, e))
    
                    # Next, do loggers - they refer to handlers and filters
    
                    #we don't want to lose the existing loggers,
                    #since other threads may have pointers to them.
                    #existing is set to contain all existing loggers,
                    #and as we go through the new configuration we
                    #remove any which are configured. At the end,
                    #what's left in existing is the set of loggers
                    #which were in the previous configuration but
                    #which are not in the new configuration.
                    root = logging.root
                    existing = list(root.manager.loggerDict.keys())
                    #The list needs to be sorted so that we can
                    #avoid disabling child loggers of explicitly
                    #named loggers. With a sorted list it is easier
                    #to find the child loggers.
                    existing.sort()
                    #We'll keep the list of existing loggers
                    #which are children of named loggers here...
                    child_loggers = []
                    #now set up the new ones...
                    loggers = config.get('loggers', EMPTY_DICT)
                    for name in loggers:
                        if name in existing:
                            i = existing.index(name) + 1 # look after name
                            prefixed = name + "."
                            pflen = len(prefixed)
                            num_existing = len(existing)
                            while i < num_existing:
                                if existing[i][:pflen] == prefixed:
                                    child_loggers.append(existing[i])
                                i += 1
                            existing.remove(name)
                        try:
                            self.configure_logger(name, loggers[name])
                        except Exception as e:
                            raise ValueError('Unable to configure logger '
                                             '%r: %s' % (name, e))
    
                    #Disable any old loggers. There's no point deleting
                    #them as other threads may continue to hold references
                    #and by disabling them, you stop them doing any logging.
                    #However, don't disable children of named loggers, as that's
                    #probably not what was intended by the user.
                    #for log in existing:
                    #    logger = root.manager.loggerDict[log]
                    #    if log in child_loggers:
                    #        logger.level = logging.NOTSET
                    #        logger.handlers = []
                    #        logger.propagate = True
                    #    elif disable_existing:
                    #        logger.disabled = True
                    _handle_existing_loggers(existing, child_loggers,
                                             disable_existing)
    
                    # And finally, do the root logger
                    root = config.get('root', None)
                    if root:
                        try:
                            self.configure_root(root)
                        except Exception as e:
                            raise ValueError('Unable to configure root '
                                             'logger: %s' % e)
            finally:
                logging._releaseLock()
    

    2.1 incremental
    コードには、incrementalパラメータが1つ表示されます.
        incremental = config.pop('incremental', False)
    

    このパラメータの役割は何ですか?
    2.1.1仮定incrementalパラメータはTrueそのコアはここにある.
        for name in loggers:
            try:
                self.configure_logger(name, loggers[name], True)
            except Exception as e:
                raise ValueError('Unable to configure logger '
    

    この例では、configure_loggerDictConfiguratorにあり、コードは以下の通りである.
        def common_logger_config(self, logger, config, incremental=False):
            """
            Perform configuration which is common to root and non-root loggers.
            """
            level = config.get('level', None)
            if level is not None:
                logger.setLevel(logging._checkLevel(level))
            if not incremental:
                #Remove any existing handlers
                for h in logger.handlers[:]:
                    logger.removeHandler(h)
                handlers = config.get('handlers', None)
                if handlers:
                    self.add_handlers(logger, handlers)
                filters = config.get('filters', None)
                if filters:
                    self.add_filters(logger, filters)
    
        def configure_logger(self, name, config, incremental=False):
            """Configure a non-root logger from a dictionary."""
            logger = logging.getLogger(name)
            self.common_logger_config(logger, config, incremental)
            propagate = config.get('propagate', None)
            if propagate is not None:
                logger.propagate = propagate
    

    以上のコードから分かるように、incrementalTrueの場合、ログのレベルlevelのみが変更する.
    2.2incrementalパラメータがFalseの場合:
    2.2.1 loggingの中の_handler_handlerList情報をクリアする
        logging._handlers.clear()
        del logging._handlerList[:]
    

    2.2.2 formmatersを初期化し、ソースコードは以下の通りである.
                    formatters = config.get('formatters', EMPTY_DICT)
                    for name in formatters:
                        try:
                            formatters[name] = self.configure_formatter(
                                                                formatters[name])
                        except Exception as e:
                            raise ValueError('Unable to configure '
                                             'formatter %r: %s' % (name, e))
    
    

    まずすべてのformatterを取得し、configure_を通過します.formatterメソッドで初期化する.
        def configure_formatter(self, config):
            """Configure a formatter from a dictionary."""
            if '()' in config:
                factory = config['()'] # for use in exception handler
                try:
                    result = self.configure_custom(config)
                except TypeError as te:
                    if "'format'" not in str(te):
                        raise
                    #Name of parameter changed from fmt to format.
                    #Retry with old name.
                    #This is so that code can be used with older Python versions
                    #(e.g. by Django)
                    config['fmt'] = config.pop('format')
                    config['()'] = factory
                    result = self.configure_custom(config)
            else:
                fmt = config.get('format', None)
                dfmt = config.get('datefmt', None)
                style = config.get('style', '%')
                cname = config.get('class', None)
                if not cname:
                    c = logging.Formatter
                else:
                    c = _resolve(cname)
                result = c(fmt, dfmt, style)
            return result
    

    パラメータはformatter構成で2つのケースがあります.
  • 有名()のkey
  • configure_が呼び出されます.Customメソッド:
        def configure_custom(self, config):
            """Configure an object with a user-supplied factory."""
            c = config.pop('()')
            if not callable(c):
                c = self.resolve(c)
            props = config.pop('.', None)
            # Check for valid identifiers
            kwargs = dict([(k, config[k]) for k in config if valid_ident(k)])
            result = c(**kwargs)
            if props:
                for name, value in props.items():
                    setattr(result, name, value)
            return result
    

    コードから、config.pop('()')の呼び出しが表示されます.
        def pop(self, key, default=None):
            value = dict.pop(self, key, default)
            return self.convert_with_key(key, value, replace=False)
    

    これは呼び出しが必要なモジュールを取得し、最後に対応するモジュールを呼び出し、formatterを生成することである.
            "console": {
                "class": "logging.StreamHandler",
                "level": "DEBUG",
                "formatter": "simple",
                ".": {
                    "hei": 1
                }
            }
    
    

    この配置に加えた.号属性の設定は、初期化後、対応するStreamhandlerに属性props = config.pop('.', None)があります.
  • 正常なkeyは対応するclassモジュールを直接呼び出し、formatter
  • を生成する.
    2.2.3 filter、Handlerを生成し、formatterと同様に、Handlerは最後に対応するformmatersとfiltersを加える
    2.2.4 loggerの生成
    これがloggingです.config.dictConfigの初期化プロセスは、一歩一歩解析し、プロトコル:.heiプロトコルを持参することで外部モジュールや既存の構成を引用し、自分のニーズのloggerを初期化し、関連モジュールをカスタマイズし、特殊化のニーズを完了することができ、粗い質問、少し乱れています.間違いがあって、面倒なところがあって、訂正しやすいです.