方案:
.接口列表保存在数据库表中, 指定是否记录日志以及日志模板.默认提取body,path variables,query properties的值(去除敏感属性)
.通过拦截器在请求处理后记录请求处理日志(HandlerInterceptor#postHandle)
。接口列表保存在sys_func表中:
示例记录:
insert into sys_func(id,name,method,path,grant_flag,log_flag) values(1,'测试-1',1,'/trade/order/test/{name}',1,1);
method: HTTP方法编码. 1-POST
path: 接口path
log_flag:是否记录日志.
sys_func表的log_template定义输出日志的模板.
。如何确认HttpServletRequest对应的sys_func记录呢?
方法1:
逻辑:HttpServletRequest执行的是什么方法,在RequestMappingHandlerMapping查找该方法对应的@RequestMapping信息。确定method,path。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
/// 获取RequestMapping与handler映射
RequestMappingHandlerMapping mapping = ApplicationContextUtil.getBean(RequestMappingHandlerMapping.class);
/// 当前请求的HandlerExecutionChain
HandlerExecutionChain handlerExecutionChain = mapping.getHandler(request);
HandlerMethod handlerMethod = (HandlerMethod) handlerExecutionChain.getHandler();
///< 从request获取到了执行的方法
Method method = handlerMethod.getMethod();
/// 找到方法对应的map设置信息: 得到原始的method,path信息
Map<RequestMappingInfo, HandlerMethod> handlerMethodMapping = mapping.getHandlerMethods();
RequestMappingInfo requestMappingInfo = handlerMethodMapping.entrySet().stream().filter(e->
e.getValue().getMethod().equals(method)
).map(Map.Entry::getKey).findFirst().orElse(null);
if (requestMappingInfo==null)
return;
/// 确定method,path
Set<RequestMethod> methods = requestMappingInfo.getMethodsCondition().getMethods();
if (methods.isEmpty())
return;
String methodName = ((RequestMethod)methods.toArray()[0]).name();
Set<String> patterns = requestMappingInfo.getPatternsCondition().getPatterns();
if (patterns.isEmpty())
return;
String path = (String) patterns.toArray()[0];
/// 跟数据库信息比较,确定sys_func记录.
FuncDto func = ParameterExtractor.findFunc(methodName,path);
if (func==null)
return;
///写日志
}
方法2:
为sys_func的每个接口记录,创建MvcRequestMatcher,建立映射。
拦截时查找匹配HttpServletRequest的MvcRequestMatcher,确定sys_func记录。
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object o, ModelAndView modelAndView) throws Exception {
FuncDto func = matchers.entrySet().stream().filter(e->e.getKey().matches(request)).map(Map.Entry::getValue).findFirst().orElse(null);
if (func==null)
return;
///写日志
}
/// 支持代码
private Map<MvcRequestMatcher,FuncDto> matchers = new HashMap<>();
@PostConstruct
private void init() {
boolean isServlet30 = ClassUtils.isPresent("javax.servlet.ServletRegistration", getClass().getClassLoader());
ObjectPostProcessor<Object> opp = ApplicationContextUtil.getBean(ObjectPostProcessor.class);
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
ParameterExtractor.funcs.forEach(e->{
MvcRequestMatcher matcher = new MvcRequestMatcher(introspector, e.getPath());
if (isServlet30) {
opp.postProcess(matcher);
}
HttpMethod httpMethod;
switch(e.getMethod()) {
case POST :httpMethod = HttpMethod.POST; break;
case GET: httpMethod = HttpMethod.GET; break;
case PATCH: httpMethod = HttpMethod.PATCH; break;
case DELETE: httpMethod = HttpMethod.DELETE; break;
default:
return;
}
matcher.setMethod(httpMethod);
matchers.put(matcher,e);
});
}