Skip to content

Commit

Permalink
feat: add Interceptor to record payload info
Browse files Browse the repository at this point in the history
  • Loading branch information
pangdayuan1 authored and mr3 committed Jan 4, 2024
1 parent ffe0224 commit 5de9b87
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 5 deletions.
2 changes: 1 addition & 1 deletion arex-storage-config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<parent>
<artifactId>arex-storage-service</artifactId>
<groupId>com.arextest</groupId>
<version>1.0.57</version>
<version>1.0.58</version>
</parent>

<properties>
Expand Down
2 changes: 1 addition & 1 deletion arex-storage-model/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<artifactId>arex-storage-service</artifactId>
<groupId>com.arextest</groupId>
<version>1.0.57</version>
<version>1.0.58</version>
</parent>

<profiles>
Expand Down
2 changes: 1 addition & 1 deletion arex-storage-web-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
<parent>
<artifactId>arex-storage-service</artifactId>
<groupId>com.arextest</groupId>
<version>1.0.57</version>
<version>1.0.58</version>
</parent>

<profiles>
Expand Down
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.arextest.model.mock.AREXMocker;
import com.arextest.model.mock.MockCategoryType;
import com.arextest.storage.converter.ZstdJacksonMessageConverter;
import com.arextest.storage.interceptor.RequestResponseMetricInterceptor;
import com.arextest.storage.metric.AgentWorkingMetricService;
import com.arextest.storage.metric.MatchStrategyMetricService;
import com.arextest.storage.metric.MetricListener;
Expand Down
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);
}
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ public interface MetricListener {
* record match strategy count with tags.
*/
void recordMatchingCount(String metricName, Map<String, String> tags);
}

/**
* record size with tags.
*/
void recordSize(String metricName, Map<String, String> tags, int size);
}
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -408,5 +408,5 @@
<url>https://github.com/arextest/arex-storage</url>


<version>1.0.57</version>
<version>1.0.58</version>
</project>

0 comments on commit 5de9b87

Please sign in to comment.