例如我的log日志中可能会出现用户的password密码,这在日志中如果暴露出来的话,也许会有安全隐患,所以需要对password的值进行处理,我这边是对值全部设置为******
我这边用到的是monkey_patch
重写logging模块中logging.Logger._log这个_log方法
def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False):
msg_list = re.findall("(?:\"password[^\\\\,]*)", msg)
for msg_res in msg_list:
msg = msg.replace(msg_res, "'password': '******'")
"""
Low-level logging routine which creates a LogRecord and then calls
all the handlers of this logger to handle the record.
"""
sinfo = None
if _srcfile:
# IronPython doesn't track Python frames, so findCaller raises an
# exception on some versions of IronPython. We trap it here so that
# IronPython can use logging.
try:
fn, lno, func, sinfo = self.findCaller(stack_info)
except ValueError: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
else: # pragma: no cover
fn, lno, func = "(unknown file)", 0, "(unknown function)"
if exc_info:
if isinstance(exc_info, BaseException):
exc_info = (type(exc_info), exc_info, exc_info.__traceback__)
elif not isinstance(exc_info, tuple):
exc_info = sys.exc_info()
record = self.makeRecord(self.name, level, fn, lno, msg, args,
exc_info, func, extra, sinfo)
self.handle(record)
def patch_args_from_params():
logging.Logger._log = _log
实际上也就是这几行
msg_list = re.findall("(?:\"password[^\\\\,]*)", msg)
for msg_res in msg_list:
msg = msg.replace(msg_res, "'password': '******'")
最后在app.py入口文件处调用一下就可以了
最终实现的效果如下:
可以看到请求完成后,打印出的日志已经对password字段的值进行了处理
上面的方法有点刚,还是推介用这种方法,创建文件log.py内容如下
"""Log helper functions."""
import logging
import re
from logging import LogRecord
from oslo_config import cfg
from oslo_log import log
from oslo_utils import strutils
CONF = cfg.CONF
LOG = log.getLogger(__name__)
LOGGER_MAP = {
'info': LOG.info,
'debug': LOG.debug,
'warning': LOG.warning,
'error': LOG.error,
'exception': LOG.exception
}
def log(level='debug'):
if level not in LOGGER_MAP:
raise Exception('error log level: %s' % (level, ))
def wrapper(method):
"""Decorator helping to log method calls."""
def callfunction(*args, **kwargs):
result = None
exception = None
try:
result = method(*args, **kwargs)
return result
except Exception as e:
exception = e.__class__.__name__
raise e
finally:
instance = args[0]
data = {"class_name": (instance.__class__.__module__ + '.'
+ instance.__class__.__name__),
"method_name": method.__name__,
"args": strutils.mask_password(args[1:]),
"kwargs": strutils.mask_password(kwargs),
"exception": exception,
"result": result}
logger = LOGGER_MAP[level]
logger('%(class_name)s method %(method_name)s'
' called with arguments %(args)s %(kwargs)s'
' exception %(exception)s result %(result)s', data)
return callfunction
return wrapper
def add_meao_log_record():
logging._logRecordFactory = MEAOLogRecord
class MEAOLogRecord(LogRecord):
def __init__(self, name=None, level=None, pathname=None, lineno=None,
msg=None, args=None, exc_info=None, func=None, sinfo=None,
**kwargs):
# Handle password before __init__.
msg_list = re.findall("(?:\"password[^\\\\,]*)", msg)
for msg_res in msg_list:
msg = msg.replace(msg_res, "'password': '******'")
LogRecord.__init__(self, name, level, pathname, lineno,
msg, args, exc_info, func, sinfo, **kwargs)
可以看到我重写了LogRecord类的__init__方法,在init之前对msg进行了处理
然后在合适的位置进行调用就行了
除了log之外其实还有个地方是raise exception的时候也会有这个问题,这个自己去研究一下吧,我这边就不放代码了,有需要的可以私信