-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Interceptor to record payload info
- Loading branch information
1 parent
ffe0224
commit 5de9b87
Showing
9 changed files
with
214 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
...age-web-api/src/main/java/com/arextest/storage/beans/CachingInterceptorConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package com.arextest.storage.beans; | ||
|
||
import com.arextest.storage.filter.ContentCachingFilter; | ||
import com.arextest.storage.interceptor.MetricInterceptor; | ||
import com.arextest.storage.metric.MetricListener; | ||
import java.util.List; | ||
import org.apache.commons.collections4.CollectionUtils; | ||
import org.springframework.boot.web.servlet.FilterRegistrationBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | ||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | ||
|
||
/** | ||
* Configuration class that registers and sets up the ContentCachingFilter interceptor. | ||
* created by xinyuan_wang on 2023/12/25 | ||
*/ | ||
@Configuration | ||
public class CachingInterceptorConfiguration implements WebMvcConfigurer { | ||
|
||
private final MetricInterceptor metricInterceptor; | ||
public final List<MetricListener> metricListeners; | ||
|
||
public CachingInterceptorConfiguration(MetricInterceptor metricInterceptor, List<MetricListener> metricListeners) { | ||
this.metricInterceptor = metricInterceptor; | ||
this.metricListeners = metricListeners; | ||
} | ||
|
||
@Override | ||
public void addInterceptors(InterceptorRegistry registry) { | ||
if (CollectionUtils.isEmpty(metricListeners)) { | ||
return; | ||
} | ||
registry.addInterceptor(metricInterceptor); | ||
} | ||
|
||
/** | ||
* Register the ContentCachingFilter filter and add it to the Filter chain | ||
*/ | ||
@Bean | ||
public FilterRegistrationBean<ContentCachingFilter> contentCachingFilter() { | ||
FilterRegistrationBean<ContentCachingFilter> registrationBean = new FilterRegistrationBean<>(); | ||
registrationBean.setFilter(new ContentCachingFilter(metricListeners)); | ||
registrationBean.addUrlPatterns("/*"); | ||
return registrationBean; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 58 additions & 0 deletions
58
arex-storage-web-api/src/main/java/com/arextest/storage/filter/ContentCachingFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.arextest.storage.filter; | ||
|
||
import com.arextest.storage.metric.MetricListener; | ||
import java.io.IOException; | ||
import java.util.List; | ||
import javax.servlet.Filter; | ||
import javax.servlet.FilterChain; | ||
import javax.servlet.FilterConfig; | ||
import javax.servlet.ServletException; | ||
import javax.servlet.ServletRequest; | ||
import javax.servlet.ServletResponse; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.apache.commons.collections4.CollectionUtils; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.springframework.web.util.ContentCachingRequestWrapper; | ||
import org.springframework.web.util.ContentCachingResponseWrapper; | ||
|
||
/** | ||
* Cache the response and record the size of the request and response | ||
* created by xinyuan_wang on 2023/12/25 | ||
*/ | ||
public class ContentCachingFilter implements Filter { | ||
private static final String GET_METHOD = "GET"; | ||
private final List<MetricListener> metricListeners; | ||
public ContentCachingFilter(List<MetricListener> metricListeners) { | ||
this.metricListeners = metricListeners; | ||
} | ||
|
||
@Override | ||
public void init(FilterConfig filterConfig) { | ||
// do nothing | ||
} | ||
|
||
@Override | ||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) | ||
throws IOException, ServletException { | ||
if (CollectionUtils.isEmpty(metricListeners)) { | ||
chain.doFilter(request, response); | ||
return; | ||
} | ||
if (skipMetric(((HttpServletRequest) request).getMethod())) { | ||
chain.doFilter(request, response); | ||
return; | ||
} | ||
chain.doFilter(new ContentCachingRequestWrapper((HttpServletRequest) request), | ||
new ContentCachingResponseWrapper((HttpServletResponse) response)); | ||
} | ||
|
||
@Override | ||
public void destroy() { | ||
// do nothing | ||
} | ||
|
||
private boolean skipMetric(String method) { | ||
return StringUtils.isEmpty(method) || GET_METHOD.equalsIgnoreCase(method); | ||
} | ||
} |
98 changes: 98 additions & 0 deletions
98
arex-storage-web-api/src/main/java/com/arextest/storage/interceptor/MetricInterceptor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package com.arextest.storage.interceptor; | ||
|
||
import com.arextest.storage.metric.MetricListener; | ||
import com.google.common.collect.Maps; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.servlet.http.HttpServletRequest; | ||
import javax.servlet.http.HttpServletResponse; | ||
import org.apache.commons.collections4.CollectionUtils; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.springframework.lang.Nullable; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.web.servlet.HandlerInterceptor; | ||
import org.springframework.web.servlet.ModelAndView; | ||
import org.springframework.web.util.ContentCachingRequestWrapper; | ||
import org.springframework.web.util.ContentCachingResponseWrapper; | ||
|
||
/** | ||
* record request and response interceptor | ||
* created by xinyuan_wang on 2023/12/25 | ||
*/ | ||
@Component | ||
public class MetricInterceptor implements HandlerInterceptor { | ||
private static final String CLIENT_APP_HEADER = "client-app"; | ||
private static final String CATEGORY_TYPE_HEADER = "category-type"; | ||
private static final String START_TIME = "startTime"; | ||
private static final String ENTRY_PAYLOAD_NAME = "service.entry.payload"; | ||
private final static String TYPE = "type"; | ||
private final static String REQUEST_TAG = "request"; | ||
private final static String RESPONSE_TAG = "response"; | ||
private final static String CLIENT_APP_ID = "clientAppId"; | ||
private final static String PATH = "path"; | ||
private final static String CATEGORY = "category"; | ||
public final List<MetricListener> metricListeners; | ||
|
||
public MetricInterceptor(List<MetricListener> metricListeners) { | ||
this.metricListeners = metricListeners; | ||
} | ||
|
||
@Override | ||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { | ||
request.setAttribute(START_TIME, System.currentTimeMillis()); | ||
return true; | ||
} | ||
|
||
@Override | ||
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, | ||
@Nullable ModelAndView modelAndView) throws Exception { | ||
if (!(request instanceof ContentCachingRequestWrapper && response instanceof ContentCachingResponseWrapper)) { | ||
return; | ||
} | ||
|
||
long endTime = System.currentTimeMillis(); | ||
|
||
byte[] requestBody = ((ContentCachingRequestWrapper) request).getContentAsByteArray(); | ||
int requestLength = requestBody.length; | ||
|
||
ContentCachingResponseWrapper responseWrapper = (ContentCachingResponseWrapper) response; | ||
int responseLength = responseWrapper.getContentSize(); | ||
responseWrapper.copyBodyToResponse(); | ||
|
||
long startTime = (Long) request.getAttribute(START_TIME); | ||
|
||
// todo: Put key information into the request header | ||
String clientApp = request.getHeader(CLIENT_APP_HEADER); | ||
String category = request.getHeader(CATEGORY_TYPE_HEADER); | ||
recordPayloadInfo(clientApp, category, request.getRequestURI(), requestLength, responseLength, endTime - startTime); | ||
} | ||
|
||
public void recordPayloadInfo(String clientApp, String category, | ||
String path, int requestLength, int responseLength, long executeMillis) { | ||
if (CollectionUtils.isEmpty(metricListeners)) { | ||
return; | ||
} | ||
|
||
Map<String, String> tags = Maps.newHashMapWithExpectedSize(5); | ||
putIfValueNotEmpty(clientApp, CLIENT_APP_ID, tags); | ||
putIfValueNotEmpty(category, CATEGORY, tags); | ||
putIfValueNotEmpty(path, PATH, tags); | ||
for (MetricListener metricListener : metricListeners) { | ||
if (executeMillis > 0) { | ||
metricListener.recordTime(ENTRY_PAYLOAD_NAME, tags, executeMillis); | ||
} | ||
|
||
tags.put(TYPE, REQUEST_TAG); | ||
metricListener.recordSize(ENTRY_PAYLOAD_NAME, tags, requestLength); | ||
tags.put(TYPE, RESPONSE_TAG); | ||
metricListener.recordSize(ENTRY_PAYLOAD_NAME, tags, responseLength); | ||
} | ||
} | ||
|
||
private void putIfValueNotEmpty(String value, String tagName, Map<String, String> tags) { | ||
if (StringUtils.isEmpty(value)) { | ||
return; | ||
} | ||
tags.put(tagName, value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters