Spring Boot 接口访问日志
2019-12-20 本文已影响0人
霸哥终结者
需求
框架需要记录每一个HTTP请求的信息,包括请求路径、请求参数、响应状态、返回参数、请求耗时等信息
Spring Boot Filter方式
1. 添加 HttpTraceFilter
import com.alibaba.fastjson.JSON;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.util.Objects;
/**
* 功能描述:Http日志打印
*
* @author lq
* @date 2019-09-06 09:20
*/
@Slf4j
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {
// 配置要记录请求的路径前缀
private static final String NEED_TRACE_PATH_PREFIX = "/";
// 忽略为multipart/form-data的ContentType的请求
private static final String IGNORE_CONTENT_TYPE = "multipart/form-data";
private final MeterRegistry registry;
public HttpTraceLogFilter(MeterRegistry registry) {
this.registry = registry;
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
if (!isRequestValid(request)) {
filterChain.doFilter(request, response);
return;
}
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
long startTime = System.currentTimeMillis();
try {
filterChain.doFilter(request, response);
status = response.getStatus();
} finally {
String path = request.getRequestURI();
if (path.startsWith(NEED_TRACE_PATH_PREFIX) && !Objects.equals(IGNORE_CONTENT_TYPE, request.getContentType())) {
// 1. 记录日志
log.info("---------- 请求日志开始 ----------");
log.info("请求路径:{}", path);
log.info("请求方法:{}", request.getMethod());
log.info("请求IP:{}", request.getRemoteAddr());
log.info("请求时间:{}", LocalDateTime.now().toString());
long latency = System.currentTimeMillis() - startTime;
log.info("请求耗时:{} ms", latency);
log.info("请求参数:{}", JSON.toJSONString(request.getParameterMap()));
log.info("请求Content-Type:{}", request.getContentType());
log.info("请求Body:{}", getRequestBody(request));
log.info("响应状态:{}", status);
log.info("响应Body:{}", getResponseBody(response));
log.info("---------- 请求日志结束 ----------");
}
updateResponse(response);
}
}
private boolean isRequestValid(HttpServletRequest request) {
try {
new URI(request.getRequestURL().toString());
return true;
} catch (URISyntaxException ex) {
return false;
}
}
private String getRequestBody(HttpServletRequest request) {
String requestBody = "";
ContentCachingRequestWrapper wrapper = WebUtils.getNativeRequest(request, ContentCachingRequestWrapper.class);
if (wrapper != null) {
try {
requestBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return requestBody;
}
private String getResponseBody(HttpServletResponse response) {
String responseBody = "";
ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
try {
responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), wrapper.getCharacterEncoding());
} catch (IOException e) {
// NOOP
}
}
return responseBody;
}
private void updateResponse(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
Objects.requireNonNull(responseWrapper).copyBodyToResponse();
}
}
2. 添加 HttpTraceConfiguration
import com.swstsoft.camera.server.config.filter.HttpTraceLogFilter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 功能描述:Http日志跟踪配置
*
* @author lq
* @date 2019-09-06 09:23
*/
@Configuration
@ConditionalOnWebApplication
public class HttpTraceConfiguration {
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
static class ServletTraceFilterConfiguration {
@Bean
public HttpTraceLogFilter httpTraceLogFilter(MeterRegistry registry) {
return new HttpTraceLogFilter(registry);
}
}
}