基于4.1.7.RELEASE
Filter处理request log的基类,提供了在filterChain.doFilter调用前后的回调函数,其实现类有CommonsRequestLoggingFilter,Log4jNestedDiagnosticContextFilter,ServletContextRequestLoggingFilter。
其核心代码为doFilterInternal方法:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { boolean isFirstRequest = !isAsyncDispatch(request); HttpServletRequest requestToUse = request; if (isIncludePayload() && isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) { requestToUse = new ContentCachingRequestWrapper(request); } boolean shouldLog = shouldLog(requestToUse); if (shouldLog && isFirstRequest) { beforeRequest(requestToUse, getBeforeMessage(requestToUse)); } try { filterChain.doFilter(requestToUse, response); } finally { if (shouldLog && !isAsyncStarted(requestToUse)) { afterRequest(requestToUse, getAfterMessage(requestToUse)); } } }
在调用filterChain.doFilter方法前后分别调用beforeRequest和afterRequest,而这两个方法具体实现由子类决定,根据功能不同来决定记录日志的方法,
在beforeRequest中和afterRequest中分别调用了 getBeforeMessage,getAfterMessage,这两个方法代码如下
private String getBeforeMessage(HttpServletRequest request) { return createMessage(request, this.beforeMessagePrefix, this.beforeMessageSuffix); } private String getAfterMessage(HttpServletRequest request) { return createMessage(request, this.afterMessagePrefix, this.afterMessageSuffix); }
最终都会进入到createMessage中去,只是前缀后缀不同,
before的Msg格式是: Before request [ Msg ]
after的Msg格式是 : After request [ Msg ]
这里的Msg具体内容则由createMessage决定
protected String createMessage(HttpServletRequest request, String prefix, String suffix) { StringBuilder msg = new StringBuilder(); msg.append(prefix); msg.append("uri=").append(request.getRequestURI()); if (isIncludeQueryString()) { msg.append('?').append(request.getQueryString()); } if (isIncludeClientInfo()) { String client = request.getRemoteAddr(); if (StringUtils.hasLength(client)) { msg.append(";client=").append(client); } HttpSession session = request.getSession(false); if (session != null) { msg.append(";session=").append(session.getId()); } String user = request.getRemoteUser(); if (user != null) { msg.append(";user=").append(user); } } if (isIncludePayload() && request instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request; byte[] buf = wrapper.getContentAsByteArray(); if (buf.length > 0) { int length = Math.min(buf.length, getMaxPayloadLength()); String payload; try { payload = new String(buf, 0, length, wrapper.getCharacterEncoding()); } catch (UnsupportedEncodingException e) { payload = "[unknown]"; } msg.append(";payload=").append(payload); } } msg.append(suffix); return msg.toString(); }
根据具体的参数设置的不同,其表现出不同的形式,msg的最基本格式为
Before/After request [ uri=xxx ]
设置includeQueryString=true:
Before/After request [ uri=xxx?a=xx&b=xxx ]
设置includeClientInfo=true:
Before/After request [ uri=xxx?a=xxx&b=xxx;client=xxx;session=sessionId;user=xxx ]
设置includePayload=true:
先判断request的content的长度,如果超过设置maxPayload的长度,则按照maxPayload进行截取,如果出现异常,则payload=[unknown]
Before/After request [ uri=xxx?a=xxx&b=xxx;client=xxx;session=sessionId;user=xxx;payload=xxx/[unknown] ]
下面来看其子类对应的不同的实现
CommonsRequestLoggingFilter: @Override protected boolean shouldLog(HttpServletRequest request) { return logger.isDebugEnabled(); } @Override protected void beforeRequest(HttpServletRequest request, String message) { logger.debug(message); } @Override protected void afterRequest(HttpServletRequest request, String message) { logger.debug(message); }
其主要是调用初始化时候设置的GenericFilterBean中的logger进行记录。
Log4jNestedDiagnosticContextFilter:
其采用了Log4j来进行日志记录。自定义变量
protected final Logger log4jLogger = Logger.getLogger(getClass());
ServletContextRequestLoggingFilter:
使用ServletContext来记录日志