python loggingモジュール登録プロセス(logging.config.dictConfigプロセスを例に)
20565 ワード
python loggingモジュール登録プロセス(logging.config.dictConfigプロセスを例に)
最近python logging関連のモジュールを見てdictConfigという関数を使って追跡してみましたが、dictConfigを呼び出した後、これらは何が起こったのか、fileConfigを使う流れも悪くないと思います.dictConfigのロードフローを先に記載し、ソースコードを追跡してから更新します.
logging.config.dictconfigソース:
1.コードにはconfigを実行する実際にはDictConfiguratorという例のconfigureメソッドが見られる.では、
1.1
コードには2つの点がある:ConvertingDictのインスタンスself.义齿config.configuratorは現在の
1.2
1.3
1.4
前は再帰的で、valueタイプがstrタイプの場合にのみ変換され、他は再帰変換タイプです.
1.4.1 strデータの変換
次に
これは、
実際には
1.4.1
公式文書によると、
1.5まとめ:
2.
ソースコードは次のとおりです.
2.1
コードには、
このパラメータの役割は何ですか?
2.1.1仮定
この例では、
以上のコードから分かるように、
2.2
2.2.1 loggingの中の
2.2.2 formmatersを初期化し、ソースコードは以下の通りである.
まずすべてのformatterを取得し、configure_を通過します.formatterメソッドで初期化する.
パラメータはformatter構成で2つのケースがあります.有名 configure_が呼び出されます.Customメソッド:
コードから、
これは呼び出しが必要なモジュールを取得し、最後に対応するモジュールを呼び出し、formatterを生成することである.
この配置に加えた.号属性の設定は、初期化後、対応するStreamhandlerに属性正常なkeyは対応するclassモジュールを直接呼び出し、formatter を生成する.
2.2.3 filter、Handlerを生成し、formatterと同様に、Handlerは最後に対応するformmatersとfiltersを加える
2.2.4 loggerの生成
これが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.1
DictConfigurator
はBaseConfigurator
から継承され、その__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.2
ConvertingDict
解析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)
Convertingdict
はdict
から継承され、dictの__getitem__
、get
、pop
の3つの方法が書き換えられ、dictの元の方法が先に呼び出され、取得された値はself.convert_with_key
で変換され、そのコードはConvertingMixin
クラスにある.1.3
ConvertingMixin
クラス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.4
Baseconfigurator
のconvert
メソッド: 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.1
Baseconfigurator
のresolve
メソッド: 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
インスタンスを初期化し、そのインスタンスのconfigurator
をBaseconfigurator
自身に向け、ConvertingDict
の役割:ext
を例に、対応するkey:value値を取得すると、プロトコル対応のモジュールに変換して使用します.2.
DictConfigurator
のconfigure
メソッド:ソースコードは次のとおりです.
"""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_logger
はDictConfigurator
にあり、コードは以下の通りである. 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
以上のコードから分かるように、
incremental
がTrue
の場合、ログのレベルlevel
のみが変更する.2.2
incremental
パラメータが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 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)
があります.2.2.3 filter、Handlerを生成し、formatterと同様に、Handlerは最後に対応するformmatersとfiltersを加える
2.2.4 loggerの生成
これがloggingです.config.dictConfigの初期化プロセスは、一歩一歩解析し、プロトコル:
.
とhei
プロトコルを持参することで外部モジュールや既存の構成を引用し、自分のニーズのloggerを初期化し、関連モジュールをカスタマイズし、特殊化のニーズを完了することができ、粗い質問、少し乱れています.間違いがあって、面倒なところがあって、訂正しやすいです.