我正在努力实现这些帖子中所做的工作Python 日志记录分为 stdout 和 stderr。
Python 日志记录分为 stdout 和 stderr。
但是使用dictConfig,到目前为止还没有成功。这是我的代码和配置:
这是我用于生成标准输出日志的配置
# logging.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: n%(message)sn"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: n%(message)sn"
}
},
"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
}
}
}
这个是用于标准日志的
# logging_stderr.json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: n%(message)sn"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: n%(message)sn"
}
},
"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)sn"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: n%(message)sn"
}
},
"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')
我希望信息将通过标准输出显示,并通过标准输出发出警告。但是在结果中,只有警告,信息完全消失了。
如果只使用 _logger
,那么一切都通过标准输出。
我错在哪里?是不是 dictconfig 只支持一个流处理程序?
我通过使用过滤器解决了自己的问题。
这篇文章帮助了我:
使用 dictConfig 在 python 的日志记录级别上安装过滤器
我基本上想将 INFO 发送到 stdout,并将 WARNING-to-CRITICAL 发送到 stderr。这意味着有一个范围,其中为处理程序定义了两端。处理程序的 level
属性仅定义低端。
现在过滤到救援。我最终使用了这个配置:
{
"version": 1,
"disable_existing_loggers": false,
"filters": {
"infofilter": {
"()": "util.LowPassFilter",
"level": 20
},
"warnfilter": {
"()": "util.HighPassFilter",
"level": 30
}
},
"formatters": {
"console": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: n%(message)sn"
},
"file": {
"format": "%(asctime)s: %(levelname)s: %(pathname)s: %(lineno)d: n%(message)sn"
}
},
"handlers": {
"console": {
"level": "INFO",
"formatter": "console",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
"filters": ["infofilter"]
},
"console_err": {
"level": "WARN",
"formatter": "console",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
"filters": ["warnfilter"]
},
"file": {
"level": "DEBUG",
"formatter": "file",
"class": "logging.FileHandler",
"encoding": "utf-8",
"filename": "app.log"
}
},
"loggers": {
"": {
"handlers": ["console", "console_err", "file"],
"level": "INFO",
"propagate": false
},
"default": {
"handlers": ["console", "console_err", "file"],
"level": "DEBUG",
"propagate": true
}
}
}
和过滤器
class LowPassFilter(object):
"""
Logging filter: Show log messages below input level.
- CRITICAL = 50
- FATAL = CRITICAL
- ERROR = 40
- WARNING = 30
- WARN = WARNING
- INFO = 20
- DEBUG = 10
- NOTSET = 0
"""
def __init__(self, level):
self.__level = level
def filter(self, log):
return log.levelno <= self.__level
class HighPassFilter(object):
"""Logging filter: Show log messages above input level."""
def __init__(self, level):
self.__level = level
def filter(self, log):
return log.levelno >= self.__level