Я пытаюсь добиться того, что сделано в этих сообщениях
Разделение журнала Python между stdout и stderr
Разделение журнала Python между stdout и stderr
но с dictConfig, пока безуспешно. Вот мой код и конфиг:
Это конфигурация, которую я использую для создания журнала stdout
# logging.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
}
},
"handlers": {
"console": {
"level": "INFO",
"formatter": "console",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"file": {
"level": "DEBUG",
"formatter": "file",
"class": "logging.FileHandler",
"encoding": "utf-8",
"filename": "app.log"
}
},
"loggers": {
"": {
"handlers": ["console", "file"],
"level": "INFO",
"propagate": false
},
"default": {
"handlers": ["console", "file"],
"level": "DEBUG",
"propagate": false
}
}
}
А это для логов stderr
# logging_stderr.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
}
},
"handlers": {
"console": {
"level": "WARN",
"formatter": "console",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr"
},
"file": {
"level": "DEBUG",
"formatter": "file",
"class": "logging.FileHandler",
"encoding": "utf-8",
"filename": "wusync.log"
}
},
"loggers": {
"": {
"handlers": ["console", "file"],
"level": "INFO",
"propagate": false
},
"default": {
"handlers": ["console", "file"],
"level": "DEBUG",
"propagate": false
}
}
}
Тогда в моем коде есть вспомогательная функция.
import logging
import logging.config
import os
from os.path import abspath, basename, dirname, exists, isfile, isdir, join, split, splitext
import sys
_script_dir = abspath(dirname(__file__))
def build_default_logger(logdir, name=None, cfgfile=None):
"""
Create per-file logger and output to shared log file.
- If found config file under script folder, use it;
- Otherwise use default config: save to /project_root/project_name.log.
- 'filename' in config is a filename; must prepend folder path to it.
:logdir: directory the log file is saved into.
:name: basename of the log file,
:cfgfile: config file in the format of dictConfig.
:return: logger object.
"""
try:
os.makedirs(logdir)
except:
pass
cfg_file = cfgfile or join(_script_dir, 'logging.json')
logging_config = None
try:
if sys.version_info.major > 2:
with open(cfg_file, 'r', encoding=TXT_CODEC, errors='backslashreplace', newline=None) as f:
text = f.read()
else:
with open(cfg_file, 'rU') as f:
text = f.read()
# Add object_pairs_hook=collections.OrderedDict hook for py3.5 and lower.
logging_config = json.loads(text, object_pairs_hook=collections.OrderedDict)
logging_config['handlers']['file']['filename'] = join(logdir, logging_config['handlers']['file']['filename'])
except Exception:
filename = name or basename(basename(logdir.strip('\\/')))
log_path = join(logdir, '{}.log'.format(filename))
logging_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: \n%(message)s\n"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: \n%(message)s\n"
}
},
"handlers": {
"console": {
"level": "INFO",
"formatter": "console",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
},
"file": {
"level": "DEBUG",
"formatter": "file",
"class": "logging.FileHandler",
"encoding": "utf-8",
"filename": log_path
}
},
"loggers": {
"": {
"handlers": ["console", "file"],
"level": "INFO",
"propagate": True
},
"default": {
"handlers": ["console", "file"],
"level": "WARN",
"propagate": True
}
}
}
if name:
logging_config['loggers'][name] = logging_config['loggers']['default']
logging.config.dictConfig(logging_config)
return logging.getLogger(name or 'default')
Наконец-то в моей основной рутине
# main.py
_script_dir = abspath(dirname(__file__))
_logger = util.build_default_logger(logdir='temp', cfgfile=abspath(join(_script_dir, 'logging_stderr.json')))
_stderr_logger = util.build_default_logger(logdir='temp', name='myerrorlog', cfgfile=abspath(join(_script_dir, 'logging_stderr.json')))
...
_logger.info('my info')
_stderr_logger.warning('my warning')
Я ожидаю, что информация будет отображаться через stdout, а предупреждение - через stderr.
Но в результате появляются только предупреждения, и информация полностью исчезла.
Если используется только _logger
, то все проходит через стандартный вывод.
Где я был не прав?
Это значит, что dictconfig поддерживает только один обработчик потока?