spring拦截器与过滤器简单实现日志功能
HandlerInterceptor拦截器+Filter+HttpServletRequestWrapper的包装类
开发思路
日志记录包含的是程序所有接收到的请求与响应.单个设置并不符合需求.应全局对请求
进行拦截获取,拿到请求对象
与响应对象
,从请求对象
与响应对象
中取得请求接口 请求参数 请求ip浏览器 请求身份等关键信息,存入到数据库中,新加一个页面筛选查看日志系统捕获的所有请求.
如何拿到请求的数据,可以使用HandlerInterceptor拦截器
.
使用HandlerInterceptor拦截器发现存在问题:无法获取到json格式传输的数据.因为json格式传输的数据并不是存储在RequestMap对象中.
通过使用过滤器拿到json格式传递的数据,因为过滤器优
先于拦截器
执行.在过滤器中拿到json格式传输的数据,将数据通过设置request中参数的方法存储到request对象中.流程到拦截器中时从request中取出参数.
实现这一步发现新的问题,由于在json格式存储数据是io流,在过滤器中已经读取了,导致json格式数据丢失,无法在controller中拿到json参数,
通过自己重写一个包装类处理request请求对象,避免原对象中流的丢失. 在包装类中获取json请求的参数,将数据保存到reques对象中,在SysEventService
拦截器中获取request的内容,拿到json请求参数
日志功能最核心的部分完成
代码展示
注册拦截器
//springmvc中配置拦截器
<mvc:interceptor>
//拦截的路径
<mvc:mapping path="/**"/>
//不拦截的路径
<mvc:exclude-mapping path="/resources/**"/>
<mvc:exclude-mapping path="/inventory/goods/supplier_importExcel_excel.do"/>
<bean class="com.mysoft.lesite.modules.exceptionHandling.EventInterceptor" />
</mvc:interceptor>
注册过滤器
//web.xml中配置过滤器
<filter>
<filter-name>requestFilter</filter-name>
<filter-class>HttpServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
HandlerInterceptor拦截器
public class EventInterceptor extends HandlerInterceptorAdapter {
/**
*
* preHandle:在方法被调用前执行。在该方法中可以做类似校验的功能。如果返回true,则继续调用下一个拦截器。如果返回false,则中断执行,也就是说我们想调用的方法 不会被执行,但是你可以修改response为你想要的响应。
postHandle:在方法执行后调用。
afterCompletion:在整个请求处理完毕后进行回调,也就是说视图渲染完毕或者调用方已经拿到响应。
**
*/
@Autowired
private SysEventService sysEventService;
private final ThreadLocal<Long> startTimeThreadLocal = new NamedThreadLocal<Long>("ThreadLocal StartTime");
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 开始时间(该数据只有当前请求的线程可见)
startTimeThreadLocal.set(System.currentTimeMillis());
return super.preHandle(request, response, handler);
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
// 保存日志
sysEventService.saveEvent(request, response, ex, startTimeThreadLocal.get(), System.currentTimeMillis());
super.afterCompletion(request, response, handler, ex);
}
}
@Service
public class SysEventService {
@Autowired
SysEventMapper service;
@Autowired
LeStoreService leStoreService;
private ExecutorService executorService = Executors.newCachedThreadPool();
public void saveEvent(HttpServletRequest request, HttpServletResponse response, Exception ex, long startTime,
long endTime) throws IOException {
// 时间处理类
TimeManager timeManager = new TimeManager();
// 处理一下返回--@ReponseBody返回无法获取
// ResponseWrapper resonseWrapper = new ResponseWrapper((HttpServletResponse) response);
// 处理请求
// BodyRequestWrapper requestWrapper = new BodyRequestWrapper(request);
Logger logger = LoggerFactory.getLogger(FileUtils.class);
final SysEventEntity record = new SysEventEntity();
record.setId(Tools.getUUID());
record.setMethod(request.getMethod());
record.setRequestUri(request.getServletPath());
// record.setClientHost(WebUtil.getHost(request));
// RequestMapper myRequestWrapper = new RequestMapper((HttpServletRequest) request);
// String body = myRequestWrapper.getBody();
String body = JSON.toJSONString(request.getAttribute("jsonBody"));
record.setUserAgent(request.getHeader("user-agent"));
//如果是图片或文件上传就跳过
// 判断是否是json传值
if (body == null || body.replace("\"", "").length() <= 0) {
record.setParammeters(JSON.toJSONString(request.getParameterMap()));
// record.setParammeters(JSON.toJSONString(request.getParameterMap().size()));
} else {
// record.setParammeters(JSON.toJSONString(body));
record.setParammeters(JSON.toJSONString(body));
}
String returneValue = String.valueOf(request.getAttribute("response"));
// record.setParammeters(requestWrapper.getJsonData());
// 获取网页登录信息
if (request.getSession().getAttribute("sessionMember") != null) {
SessionMember sessionMember = (SessionMember) request.getSession().getAttribute("sessionMember");
record.setCreateBy("1");
record.setAh1(sessionMember.getAh1());
record.setAa3(sessionMember.getAa3());
record.setAb4(sessionMember.getAb4());
}
record.setResponseData(returneValue);
// record.setCreateBy(sessionMember != null?:"非关系系统用户");
// record.setStatus(response.getStatus());
//设置时间
// record.setCreateTime(timeManager.getTimeString(startTime));
record.setCreateTime(DateUtil.getTime());
final String msg = (String) request.getAttribute("msg");
executorService.submit(new Runnable() {
public void run() {
try { // 保存操作
if (StringUtils.isNotBlank(msg)) {
record.setRemark(msg);
} else {
// record.setRemark(ExceptionUtil.getStackTraceAsString(ex));
record.setRemark("ExceptionUtil.getStackTraceAsString(ex)");
}
// 插入一条记录
try {
service.insert(record);
} catch (Exception e) {
// TODO: handle exception
record.setResponseData("记录日志数据过长插入存在问题"+e);
record.setParammeters(record.getParammeters().substring(0,100));
service.insert(record);
}
// 内存信息
if (logger.isDebugEnabled()) {
String message = "开始时间: {}; 结束时间: {}; 耗时: {}s; URI: {}; ";
// 最大内存: {}M; 已分配内存: {}M; 已分配内存中的剩余空间: {}M; 最大可用内存: {}M.
// long total = Runtime.getRuntime().totalMemory() / 1024 / 1024;
// long max = Runtime.getRuntime().maxMemory() / 1024 / 1024;
// long free = Runtime.getRuntime().freeMemory() / 1024 / 1024;
// , max, total, free, max - total + free
logger.debug(message, timeManager.getTimeString(startTime), timeManager.getTimeString(endTime),
(endTime - startTime) / 1000.00, record.getRequestUri());
}
} catch (Exception e) {
logger.error("Save event log cause error :", e);
}
}
});
}
}
过滤器
public class HttpServletFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
RequestMapper requestWrapper = null;
Boolean isStata = true;
String hs = JSON.toJSONString(request.getParameterMap());
String path = ((HttpServletRequest) request).getRequestURI();
ArrayList<String> listData = new ArrayList<String>();
//需要略过的请求
listData.add("/inventory/goods/supplier_importExcel_excel.do");
listData.add("/resources/");
listData.add("/login/");
listData.add("/statistics/stockppdStatistics");
listData.add("/portal/purchase/purchasecheckMain/le_purchase_attachment/upload.do");
listData.add("/inventory/goods/supplier_importExcel_excel.do");
for(String item : listData ) {
//判断是否是不需过滤的地址
if(path.indexOf(item) != -1) {
isStata = false;
}
}
if(isStata) {
//用自己写的包装类处理request请求对象
requestWrapper = new RequestMapper((HttpServletRequest) request);
request.setAttribute("jsonBody", requestWrapper.getBody());
requestWrapper.setAttribute("jsonBody", requestWrapper.getBody());
chain.doFilter(requestWrapper, response);
}else {
chain.doFilter(request, response);
}
// if(requestWrapper == null) {
// chain.doFilter(request, response);
// } else {
// chain.doFilter(requestWrapper, response);
// }
// chain.doFilter(request, response);
}
}
存在问题
这种方式的拦截报错只能获取到请求中的数据,如果是程序本身捕获错误,未返回给请求中,无法拿到报错信息.