<!-- 私人工具包 -->
<dependency>
<groupId>cn.changeforyou</groupId>
<artifactId>location</artifactId>
<version>1.13-SNAPSHOT</version>
</dependency>
<!-- hutool工具依赖 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
LogFilter.java
package com.abliner.test.common.log;
import cn.changeforyou.web.utils.http.ServletUtils;
import cn.changeforyou.web.utils.http.warpper.BufferedHttpResponseWrapper;
import cn.hutool.json.JSONUtil;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import com.abliner.test.common.log.LogRecordConfig.InterfaceLogConfig;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class LogFilter extends OncePerRequestFilter {
Logger log = LoggerFactory.getLogger("reqResp");
@Autowired
private LogRecordConfig logRecordConfig;
private final Set<String> urls;
private final AntPathMatcher antPathMatcher;
private final Map<String, InterfaceLogConfig> url2Config = new ConcurrentHashMap<>();
public LogRecordConfig getLogRecordConfig() {
return logRecordConfig;
}
public LogRecordConfig addInterfaceLogConfig(InterfaceLogConfig config) {
logRecordConfig.getInterfaceLogConfigs().add(config);
initMatcher();
return logRecordConfig;
}
public LogRecordConfig removeInterfaceLogConfig(String url) {
if (url2Config.containsKey(url)) {
InterfaceLogConfig config = url2Config.remove(url);
logRecordConfig.getInterfaceLogConfigs().remove(config);
initMatcher();
}
return logRecordConfig;
}
public LogRecordConfig updateDefaultInterfaceLogLevel(InterfaceLogConfig config) {
logRecordConfig.setDefaultInterfaceLogConfig(config);
return logRecordConfig;
}
public LogFilter() {
urls = Collections.synchronizedSet(new HashSet<>());
antPathMatcher = new AntPathMatcher();
}
private InterfaceLogConfig matches(String url) {
if (urls.isEmpty()) {
return null;
}
for (String s : urls) {
if (antPathMatcher.match(s, url)) {
return url2Config.get(s);
}
}
return null;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
long requestTime = System.currentTimeMillis();
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String url = uri.substring(contextPath.length());
InterfaceLogConfig thisConfig = matches(url);
if (null == thisConfig) {
thisConfig = logRecordConfig.getDefaultInterfaceLogConfig();
}
if (!thisConfig.printLog()) {
filterChain.doFilter(request, response);
return;
}
String requestBody = "";
String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
if (requestContentType != null) {
// xml json
if ((requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE) || requestContentType.startsWith(MediaType.APPLICATION_XML_VALUE)) && request.getMethod()
.equalsIgnoreCase("POST")) {
StringBuilder sb = new StringBuilder();
request = ServletUtils.getRequestBody(request, sb);
requestBody = sb.toString();
// 普通表单提交
} else if (requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
requestBody = toJson(request.getParameterMap());
// 文件表单提交
} else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
requestBody = getFormParam(request);
} else {
requestBody = toJson(request.getParameterMap());
}
} else if (request.getMethod().equals(HttpMethod.GET.name())) {
requestBody = toJson(request.getParameterMap());
}
BufferedHttpResponseWrapper responseWrapper = new BufferedHttpResponseWrapper(response);
if (thisConfig.printReq()) {
if (thisConfig.isDebugEnabled()) {
log.debug("URL: {}, requestBody: {}", url, requestBody);
} else {
log.info("URL: {}, requestBody: {}", url, requestBody);
}
}
filterChain.doFilter(request, responseWrapper);
long costTime = System.currentTimeMillis() - requestTime;
String responseBody = "";
// 暂定只有json 输出响应体
String contentType = responseWrapper.getContentType();
if (contentType != null && contentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
responseBody = new String(responseWrapper.getBuffer(), StandardCharsets.UTF_8);
}
StringBuilder sb = new StringBuilder();
sb.append("URL:").append(url).append(", total time:").append(costTime).append(" ms, ");
if (thisConfig.printRes()) {
sb.append(", responseBody:").append(responseBody);
}
if (responseWrapper.getStatus() >= 200 && responseWrapper.getStatus() < 1000) {
if (thisConfig.isDebugEnabled()) {
log.debug(sb.toString());
} else {
log.info(sb.toString());
}
} else {
log.error(sb.toString());
}
response.getOutputStream().write(responseWrapper.getBuffer());
}
private String getFormParam(HttpServletRequest request) {
MultipartResolver resolver = new StandardServletMultipartResolver();
MultipartHttpServletRequest mRequest = resolver.resolveMultipart(request);
Map<String, Object> param = new HashMap<>();
Map<String, String[]> parameterMap = mRequest.getParameterMap();
if (!parameterMap.isEmpty()) {
param.putAll(parameterMap);
}
Map<String, MultipartFile> fileMap = mRequest.getFileMap();
if (!fileMap.isEmpty()) {
for (Map.Entry<String, MultipartFile> fileEntry : fileMap.entrySet()) {
MultipartFile file = fileEntry.getValue();
param.put(fileEntry.getKey(), file.getOriginalFilename() + "(" + file.getSize() + " byte)");
}
}
return toJson(param);
}
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet();
initMatcher();
}
private void initMatcher() {
List<InterfaceLogConfig> configs = logRecordConfig.getInterfaceLogConfigs();
this.urls.clear();
if (CollectionUtils.isNotEmpty(configs)) {
for (InterfaceLogConfig config : configs) {
this.urls.add(config.getUrl());
url2Config.put(config.getUrl(), config);
}
}
}
private static String toJson(Object object) {
return JSONUtil.toJsonStr(object);
}
}
LogRecordConfig.java
package com.abliner.test.common.log;
import com.abliner.test.common.validator.InStrings;
import com.abliner.test.common.validator.ValidatorConstant;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@ConfigurationProperties(prefix = "log.record")
@Data
@Component
public class LogRecordConfig {
private InterfaceLogConfig defaultInterfaceLogConfig;
@NestedConfigurationProperty
private List<InterfaceLogConfig> interfaceLogConfigs;
@Data
public static class InterfaceLogConfig {
@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)
@NotEmpty(groups = ValidatorConstant.Delete.class)
private String url;
/***
* 1: info
* 2: debug
*/
@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)
@InStrings(in= {"info", "debug"}, groups = ValidatorConstant.InsertAndUpdate.class)
@InStrings(in= {"info", "debug"}, groups = ValidatorConstant.UpdateDefault.class)
private String logLevel;
/***
* res
* req
* all
* none
*/
@NotEmpty(groups = ValidatorConstant.InsertAndUpdate.class)
@InStrings(in= {"res", "req", "all", "none"},groups = ValidatorConstant.InsertAndUpdate.class)
@InStrings(in= {"res", "req", "all", "none"},groups = ValidatorConstant.UpdateDefault.class)
private String print;
public boolean isDebugEnabled() {
return "debug".equalsIgnoreCase(logLevel);
}
public boolean printLog() {
return !"none".equalsIgnoreCase(print);
}
public boolean printRes() {
return "res".equalsIgnoreCase(print) || "all".equalsIgnoreCase(print);
}
public boolean printReq() {
return "req".equalsIgnoreCase(print) || "all".equalsIgnoreCase(print);
}
}
}
InStrings.java
package com.abliner.test.common.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {InStringsValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(InStrings.List.class)
public @interface InStrings {
String message() default "字符串不在设定范围内";
String[] in();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Defines several {@code @NotEmpty} constraints on the same element.
*
* @see InStrings
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
InStrings[] value();
}
}
InStringsValidator.java
package com.abliner.test.common.validator;
import cn.changeforyou.utils.string.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class InStringsValidator implements ConstraintValidator<InStrings, String> {
private String[] mustIn;
@Override
public void initialize(InStrings constraintAnnotation) {
mustIn = constraintAnnotation.in();
}
@Override
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if (StringUtils.isEmpty(s)) {
return false;
}
return StringUtils.in(s, mustIn);
}
}
ValidatorConstant.java
package com.abliner.test.common.validator;
public interface ValidatorConstant {
interface Insert {
}
interface Update {
}
interface Delete {
}
interface Select {
}
interface InsertAndUpdate {
}
interface SelectAndDelete {
}
interface UpdateDefault {
}
}
application.yml
添加代码log:
record:
defaultInterfaceLogConfig:
logLevel: info
print: req
因篇幅问题不能全部显示,请点此查看更多更全内容