人类经常把一个生涯发生的事,撰写成历史,在从那里看人生;其实,那不过是衣服,人生是内在的——罗曼。罗兰
日记记录过滤器
package com.kuang.config.log;
import com.alibaba.fastjson.JSON;
import com.kuang.common.util.Opt;
import com.kuang.common.util.ResponseWrapper;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 日志记录过滤器
*
* @author <achao1441470436@gmail.com>
* @since 2021/7/29 9:58
*/
@Slf4j
//@Component
//@WebFilter(filterName = "LogFilter", urlPatterns = "/**")
public class LogFilter implements Filter {
/**
* The <code>doFilter</code> method of the Filter is called by the container
* each time a request/response pair is passed through the chain due to a
* client request for a resource at the end of the chain. The FilterChain
* passed in to this method allows the Filter to pass on the request and
* response to the next entity in the chain.
* <p>
* A typical implementation of this method would follow the following
* pattern:- <br>
* 1. Examine the request<br>
* 2. Optionally wrap the request object with a custom implementation to
* filter content or headers for input filtering <br>
* 3. Optionally wrap the response object with a custom implementation to
* filter content or headers for output filtering <br>
* 4. a) <strong>Either</strong> invoke the next entity in the chain using
* the FilterChain object (<code>chain.doFilter()</code>), <br>
* 4. b) <strong>or</strong> not pass on the request/response pair to the
* next entity in the filter chain to block the request processing<br>
* 5. Directly set headers on the response after invocation of the next
* entity in the filter chain.
*
* @param request The request to process
* @param response The response associated with the request
* @param chain Provides access to the next filter in the chain for this
* filter to pass the request and response to for further
* processing
* @throws IOException if an I/O error occurs during this filter's
* processing of the request
* @throws ServletException if the processing fails for any other reason
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ResponseWrapper wrapper = new ResponseWrapper((HttpServletResponse) response);
HttpServletRequest req = (HttpServletRequest) request;
String uri = req.getRequestURI();
long startTime = System.nanoTime();
chain.doFilter(request, wrapper);
// 获取response返回的内容并重新写入response
String result = wrapper.getResponseData(response.getCharacterEncoding());
response.getOutputStream().write(result.getBytes());
Opt.of(log).filter(Logger::isInfoEnabled).ifPresent(l -> {
log.info("method:{}", req.getMethod());
log.info("uri:{}", uri);
log.info("parameterMap:{}", JSON.toJSONString(req.getParameterMap()));
log.info("responseCode:{}", wrapper.getStatus());
log.info("result:{}", result);
log.info("timeCost:{}", (System.nanoTime() - startTime) / (1000.0 * 1000.0) + "ms");
});
}
}
因为默认的response
获取了响应结果就没了,因此我们需要封装一个ResponseWrapper
package com.kuang.common.util;
import lombok.SneakyThrows;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
/**
* 过滤器获取响应结果
*
* @author <achao1441470436@gmail.com>
* @since 2021/7/29 10:13
*/
public class ResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer = null;
private ServletOutputStream outputStream = null;
private PrintWriter writer = null;
/**
* Constructs a response adaptor wrapping the given response.
*
* @param response The response to be wrapped
* @throws IllegalArgumentException if the response is null
*/
@SneakyThrows
public ResponseWrapper(HttpServletResponse response) {
super(response);
buffer = new ByteArrayOutputStream();
outputStream = new WrapperOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer, StandardCharsets.UTF_8));
}
/**
* The default behavior of this method is to return getOutputStream() on the
* wrapped response object.
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
return outputStream;
}
/**
* The default behavior of this method is to return getWriter() on the
* wrapped response object.
*/
@Override
public PrintWriter getWriter() throws IOException {
return writer;
}
/**
* The default behavior of this method is to call flushBuffer() on the
* wrapped response object.
*/
@Override
@SneakyThrows
public void flushBuffer() {
if (outputStream != null) {
outputStream.flush();
}
if (writer != null) {
writer.flush();
}
}
/**
* The default behavior of this method is to call reset() on the wrapped
* response object.
*/
@Override
public void reset() {
buffer.reset();
}
/**
* Get response content
*
* @param charset HttpServletResponse#getCharacterEncoding()
* @return response content
*/
@SneakyThrows
public String getResponseData(String charset) {
// 将out、writer中的数据强制输出到WrapperResponse的buffer里面,否则取不到数据
flushBuffer();
return buffer.toString(StandardCharsets.UTF_8.displayName());
}
/**
* 内部类,对ServletOutputStream进行包装,指定输出流的输出端
*/
private static class WrapperOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WrapperOutputStream(ByteArrayOutputStream stream) throws IOException {
bos = stream;
}
/**
* Writes the specified byte to this output stream. The general
* contract for <code>write</code> is that one byte is written
* to the output stream. The byte to be written is the eight
* low-order bits of the argument <code>b</code>. The 24
* high-order bits of <code>b</code> are ignored.
* <p>
* Subclasses of <code>OutputStream</code> must provide an
* implementation for this method.
*
* @param b the <code>byte</code>.
* @throws IOException if an I/O error occurs. In particular,
* an <code>IOException</code> may be thrown if the
* output stream has been closed.
*/
@Override
public void write(int b) throws IOException {
bos.write(b);
}
/**
* Checks if a non-blocking write will succeed. If this returns
* <code>false</code>, it will cause a callback to
* {@link WriteListener#onWritePossible()} when the buffer has emptied. If
* this method returns <code>false</code> no further data must be written
* until the contain calls {@link WriteListener#onWritePossible()}.
*
* @return <code>true</code> if data can be written, else <code>false</code>
* @since Servlet 3.1
*/
@Override
public boolean isReady() {
return false;
}
/**
* Sets the {@link WriteListener} for this {@link ServletOutputStream} and
* thereby switches to non-blocking IO. It is only valid to switch to
* non-blocking IO within async processing or HTTP upgrade processing.
*
* @param listener The non-blocking IO write listener
* @throws IllegalStateException If this method is called if neither
* async nor HTTP upgrade is in progress or
* if the {@link WriteListener} has already
* been set
* @throws NullPointerException If listener is null
* @since Servlet 3.1
*/
@Override
public void setWriteListener(WriteListener listener) {
}
}
}