Skip to content

Commit

Permalink
feat(comet): Logger support print body from response.
Browse files Browse the repository at this point in the history
  • Loading branch information
yizzuide committed Jul 11, 2020
1 parent 1c65eab commit 618eb45
Show file tree
Hide file tree
Showing 16 changed files with 162 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.github.yizzuide.milkomeda.comet.core.CometData;
import com.github.yizzuide.milkomeda.comet.core.CometHolder;
import com.github.yizzuide.milkomeda.pillar.PillarExecutor;
import com.github.yizzuide.milkomeda.universe.polyfill.SpringMvcPolyfill;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -19,8 +18,6 @@
import org.springframework.lang.NonNull;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -31,7 +28,7 @@
*
* @author yizzuide
* @since 3.0.0
* @version 3.3.0
* @version 3.11.0
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(org.springframework.beans.factory.ListableBeanFactory, java.lang.Class)
* Create at 2020/03/28 18:54
*/
Expand All @@ -41,9 +38,6 @@
@ConditionalOnProperty(prefix = "milkomeda.comet.collector", name = "enable", havingValue = "true")
public class CometCollectorConfig implements ApplicationContextAware {

@Autowired
private CometCollectorProperties props;

@Autowired(required = false)
private List<Collector> collectors;

Expand All @@ -62,31 +56,13 @@ public CollectorRecorder collectorRecorder() {
return new CollectorRecorder(collectorFactory());
}

@SuppressWarnings("all")
@Autowired
public void config(CometAspect cometAspect, CometCollectorProperties cometCollectorProperties) {
// 设置日志采集器
cometAspect.setRecorder(collectorRecorder());
CometHolder.setCollectorProps(cometCollectorProperties);
}

@Bean
@ConditionalOnProperty(prefix = "milkomeda.comet.collector", name = "enable-tag", havingValue = "true")
public CometCollectorResponseBodyAdvice cometResponseBodyAdvice() {
return new CometCollectorResponseBodyAdvice();
}

@Autowired
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public void configResponseBodyAdvice(RequestMappingHandlerAdapter adapter, HandlerExceptionResolver handlerExceptionResolver) {
if (!props.isEnableTag()) {
return;
}
CometCollectorResponseBodyAdvice collectorResponseBodyAdvice = cometResponseBodyAdvice();
// 动态添加到响应处理
SpringMvcPolyfill.addDynamicResponseBodyAdvice(adapter.getReturnValueHandlers(), handlerExceptionResolver, collectorResponseBodyAdvice);
}

@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
// 如果为空,到BeanFactory里查找
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
import com.github.yizzuide.milkomeda.universe.polyfill.SpringMvcPolyfill;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

Expand All @@ -25,7 +25,7 @@
*
* @author yizzuide
* @since 2.0.0
* @version 3.5.0
* @version 3.11.0
* Create at 2019/12/12 18:10
*/
@Configuration
Expand All @@ -49,7 +49,6 @@ public CometAspect cometAspect() {
//@ConditionalOnMissingBean // 识别类型:FilterRegistrationBean,会导致永远无法加载
// 下面两方式在版本2.1.0推出,用于识别泛型类型:FilterRegistrationBean<CometRequestFilter>
// @ConditionalOnMissingBean(parameterizedContainer = FilterRegistrationBean.class)
@ConditionalOnProperty(prefix = "milkomeda.comet", name = "enable-read-request-body", havingValue = "true")
public FilterRegistrationBean<CometRequestFilter> cometRequestFilter() {
FilterRegistrationBean<CometRequestFilter> cometRequestFilter = new FilterRegistrationBean<>();
cometRequestFilter.setFilter(new CometRequestFilter());
Expand All @@ -61,7 +60,6 @@ public FilterRegistrationBean<CometRequestFilter> cometRequestFilter() {

// 配置RequestMappingHandlerAdapter实现动态注解SpringMVC处理器组件
@Autowired
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public void configParamResolve(RequestMappingHandlerAdapter adapter) {
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
// 动态添加针对注解 @CometParam 处理的解析器
Expand All @@ -71,7 +69,6 @@ public void configParamResolve(RequestMappingHandlerAdapter adapter) {
}

@Autowired
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public void configRequestMappingHandlerMapping(RequestMappingHandlerMapping requestMappingHandlerMapping) {
// 使用内置拦截器
SpringMvcPolyfill.addDynamicInterceptor(cometInterceptor(), Ordered.HIGHEST_PRECEDENCE, Collections.singletonList("/**"),
Expand All @@ -82,4 +79,16 @@ public void configRequestMappingHandlerMapping(RequestMappingHandlerMapping requ
public CometInterceptor cometInterceptor() {
return new CometInterceptor();
}

@Bean
public CometResponseBodyAdvice cometResponseBodyAdvice() {
return new CometResponseBodyAdvice();
}

@Autowired
public void configResponseBodyAdvice(RequestMappingHandlerAdapter adapter, HandlerExceptionResolver handlerExceptionResolver) {
CometResponseBodyAdvice collectorResponseBodyAdvice = cometResponseBodyAdvice();
// 动态添加到响应处理
SpringMvcPolyfill.addDynamicResponseBodyAdvice(adapter.getReturnValueHandlers(), handlerExceptionResolver, collectorResponseBodyAdvice);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
package com.github.yizzuide.milkomeda.comet.core;

import com.github.yizzuide.milkomeda.comet.collector.CometCollectorProperties;
import com.github.yizzuide.milkomeda.comet.logger.CometLoggerProperties;

/**
* CometHolder
*
* @author yizzuide
* @since 3.0.0
* @version 3.11.0
* Create at 2020/03/28 12:42
*/
public class CometHolder {
private static CometProperties props;

private static CometLoggerProperties logProps;

private static CometCollectorProperties collectorProps;

static void setProps(CometProperties props) {
Expand All @@ -29,4 +33,40 @@ public static void setCollectorProps(CometCollectorProperties collectorProps) {
static CometCollectorProperties getCollectorProps() {
return collectorProps;
}

public static void setLogProps(CometLoggerProperties logProps) {
CometHolder.logProps = logProps;
}

static CometLoggerProperties getLogProps() {
return logProps;
}

/**
* 是否需要包装请求
* @return 请求数据是否要被缓存起来采集
* @since 3.11.0
*/
public static boolean shouldWrapRequest() {
return getProps().isEnableReadRequestBody();
}

/**
* 是否需要包装响应
* @return 响应是否要被缓存起来采集
* @since 3.11.0
*/
public static boolean shouldWrapResponse() {
return getProps().isEnableReadResponseBody();
}

/**
* 响应可否可读
* @return 响应数据是否可以被探测
* @since 3.11.0
*/
public static boolean isResponseReadable() {
return (getCollectorProps() != null && getCollectorProps().isEnableTag()) ||
(getLogProps() != null && getLogProps().isEnableResponse());
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.yizzuide.milkomeda.comet.core;

import com.github.yizzuide.milkomeda.comet.collector.CometCollectorProperties;
import com.github.yizzuide.milkomeda.comet.collector.CometCollectorResponseBodyAdvice;
import com.github.yizzuide.milkomeda.comet.collector.TagCollector;
import com.github.yizzuide.milkomeda.comet.logger.CometLoggerProperties;
import com.github.yizzuide.milkomeda.comet.logger.CometLoggerType;
import com.github.yizzuide.milkomeda.universe.metadata.BeanIds;
import com.github.yizzuide.milkomeda.universe.parser.url.URLPathMatcher;
import com.github.yizzuide.milkomeda.universe.parser.url.URLPlaceholderParser;
Expand Down Expand Up @@ -131,7 +131,7 @@ public void setApplicationContext(@NonNull ApplicationContext applicationContext
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
if (this.cometLoggerProperties != null) {
printLog(request);
printRequestLog(request);
}

if (cometCollectorProperties != null && cometCollectorProperties.isEnableTag() && !CollectionUtils.isEmpty(this.tagCollectorMap)) {
Expand All @@ -142,13 +142,12 @@ public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServl

@Override
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, Exception ex) throws Exception {
if (cometCollectorProperties == null || !cometCollectorProperties.isEnableTag() ||
CollectionUtils.isEmpty(this.tagCollectorMap) || threadLocal.get() == null) {
if (!CometHolder.isResponseReadable()) {
return;
}
// 获取ResponseEntity返回值
Object body = request.getAttribute(CometCollectorResponseBodyAdvice.REQUEST_ATTRIBUTE_BODY);
if (body == null) {
Object body = request.getAttribute(CometResponseBodyAdvice.REQUEST_ATTRIBUTE_BODY);
if (body == null && CometHolder.shouldWrapResponse()) {
// 从ResponseWrapper获取
CometResponseWrapper responseWrapper =
WebUtils.getNativeResponse(response, CometResponseWrapper.class);
Expand All @@ -167,7 +166,16 @@ public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpSe
}
}
}
this.collectPostLog(response.getStatus(), body, ex);

if (cometLoggerProperties != null && cometLoggerProperties.isEnableResponse()) {
this.printResponseLog(request, body);
}

if (cometCollectorProperties != null && cometCollectorProperties.isEnableTag() &&
!CollectionUtils.isEmpty(this.tagCollectorMap) && threadLocal.get() != null) {
this.collectPostLog(response.getStatus(), body, ex);
}

}

@SuppressWarnings("all")
Expand Down Expand Up @@ -305,26 +313,47 @@ private void collectPreLog(HttpServletRequest request) {
threadLocal.set(cometData);
}

private void printLog(HttpServletRequest request) {
private void printRequestLog(HttpServletRequest request) {
printLog(request, null, CometLoggerType.REQUEST);
}

private void printResponseLog(HttpServletRequest request, Object body) {
printLog(request, body, CometLoggerType.RESPONSE);
}

private void printLog(HttpServletRequest request, Object body, CometLoggerType type) {
// 排除不需要打印的URL
CometLoggerProperties logger = this.cometLoggerProperties;
List<String> exclude = logger.getExclude();
String requestURI = request.getRequestURI();
if (!CollectionUtils.isEmpty(exclude)) {
if (exclude.contains(requestURI)) {
if (URLPathMatcher.match(exclude, requestURI)) {
return;
}
}
List<CometLoggerProperties.Strategy> strategyList = this.strategyList;

// 过滤打印策略类型
List<CometLoggerProperties.Strategy> strategyList = this.strategyList.stream()
.filter(s -> s.getType() == type).collect(Collectors.toList());
if (CollectionUtils.isEmpty(strategyList)) {
return;
}

String requestParams = CometAspect.resolveThreadLocal.get();
for (CometLoggerProperties.Strategy strategy : strategyList) {
if (CollectionUtils.isEmpty(strategy.getPaths()) ||
!URLPathMatcher.match(strategy.getPaths(), requestURI)) {
continue;
}
log.info(urlPlaceholderParser.parse(strategy.getTpl(), request, requestParams, strategy.getCacheKeys()));
String resp;
if (body == null) {
resp = null;
} else if (body instanceof Map) {
resp = JSONUtil.serialize(body);
} else {
resp = body.toString();
}
log.info(urlPlaceholderParser.parse(strategy.getTpl(), request, requestParams, resp, strategy.getCacheKeys()));
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public class CometProperties {
private boolean enableReadRequestBody = false;

/**
* 允许开启响应包装类读取响应消息体,依赖开启 {@link CometProperties#enableReadRequestBody}
* (使用 Collector 时,如果通过注入HttpServletResponse直接写出流则必须开启)
* 允许开启响应包装类读取响应消息体(获取通过注入HttpServletResponse直接写出响应数据则必须开启)
* @see CometProperties#enableReadRequestBody
*/
private boolean enableReadResponseBody = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
// 设置编码,防止Spring MVC注册Filter顺序问题导致乱码问题
// servletRequest.setCharacterEncoding(Charset.defaultCharset().toString());
ServletRequest requestWrapper = servletRequest;
// 如果有Form表单数据则不读取body,交给SpringMVC框架处理(但@CometParam功能仍然有效)
if (CollectionUtils.isEmpty(servletRequest.getParameterMap())) {
requestWrapper = new CometRequestWrapper((HttpServletRequest) servletRequest);
if (CometHolder.shouldWrapRequest()) {
// 如果有Form表单数据则不读取body,交给SpringMVC框架处理(但@CometParam功能仍然有效)
if (CollectionUtils.isEmpty(servletRequest.getParameterMap())) {
requestWrapper = new CometRequestWrapper((HttpServletRequest) servletRequest);
}
}
// 只有Tag Collector开启,并且指定开启`comet.enable-read-response-body`,才会装饰包装类读Response Body
boolean enableAddResponseWrapper = CometHolder.getProps().isEnableReadResponseBody() &&
CometHolder.getCollectorProps() != null && CometHolder.getCollectorProps().isEnableTag();
boolean enableAddResponseWrapper = CometHolder.shouldWrapResponse();
if (enableAddResponseWrapper) {
servletResponse = new CometResponseWrapper((HttpServletResponse) servletResponse);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.yizzuide.milkomeda.comet.collector;
package com.github.yizzuide.milkomeda.comet.core;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
Expand Down Expand Up @@ -27,9 +27,9 @@
* Create at 2020/03/29 11:30
*/
//@ControllerAdvice // 这种方式默认就会扫描并加载到Ioc,不好动态控制是否加载,但好处是外部API对未来版本的兼容性强
public class CometCollectorResponseBodyAdvice implements ResponseBodyAdvice<Object> {
public class CometResponseBodyAdvice implements ResponseBodyAdvice<Object> {

public static final String REQUEST_ATTRIBUTE_BODY = "comet.collect.body";
public static final String REQUEST_ATTRIBUTE_BODY = "comet.response.body";

@Override
public boolean supports(@NonNull MethodParameter returnType, @NonNull Class<? extends HttpMessageConverter<?>> converterType) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.github.yizzuide.milkomeda.comet.logger;

import com.github.yizzuide.milkomeda.comet.core.CometConfig;
import com.github.yizzuide.milkomeda.comet.core.CometHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -11,11 +13,17 @@
*
* @author yizzuide
* @since 3.0.0
* @version 3.11.0
* Create at 2020/04/05 18:50
*/
@Configuration
@AutoConfigureAfter(CometConfig.class)
@EnableConfigurationProperties(CometLoggerProperties.class)
@ConditionalOnProperty(prefix = "milkomeda.comet.logger", name = "enable", havingValue = "true")
public class CometLoggerConfig {

@Autowired
public void config(CometLoggerProperties props) {
CometHolder.setLogProps(props);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ public class CometLoggerProperties {

@Data
public static class Strategy {
/**
* 打印日志类型
* @since 3.11.0
*/
private CometLoggerType type = CometLoggerType.REQUEST;
/**
* 策略包含路径
*/
Expand Down
Loading

0 comments on commit 618eb45

Please sign in to comment.