Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions src/main/java/umc/th/juinjang/monitoring/ApiFilterConfig.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
package umc.th.juinjang.monitoring;

import java.util.List;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;
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 umc.th.juinjang.monitoring.ApiLoggerFilter;

@Configuration
@Slf4j
public class ApiFilterConfig {

@Value("${logging.api.excluded-paths}")
private List<String> excludedUrls;
@Value("${logging.api.excluded-paths}")
private List<String> excludedUrls;

@Bean
public FilterRegistrationBean<ApiLoggerFilter> loggingFilter() {
FilterRegistrationBean<ApiLoggerFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ApiLoggerFilter(excludedUrls));
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 순서 설정
registrationBean.setUrlPatterns(List.of("/*"));
return registrationBean;
}
@Bean
public FilterRegistrationBean<ApiLoggerFilter> loggingFilter() {
FilterRegistrationBean<ApiLoggerFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ApiLoggerFilter(excludedUrls));
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE); // 순서 설정
registrationBean.setUrlPatterns(List.of("/*"));
return registrationBean;
}
}
Original file line number Diff line number Diff line change
@@ -1,52 +1,61 @@
package umc.th.juinjang.monitoring;

import jakarta.servlet.http.HttpServletRequest;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.springframework.web.util.ContentCachingRequestWrapper;

public class ApiLogRequestLogGenerator extends ApiLogGenerator {

private final ContentCachingRequestWrapper request;

public ApiLogRequestLogGenerator(ContentCachingRequestWrapper request) {
this.request = request;
}

@Override
public String generateLog() {
StringBuilder logBuilder = new StringBuilder();

logBuilder.append("Request : ").append(" ");
logBuilder.append(getBaseLogInfo());
logBuilder.append("[method] ").append(request.getMethod()).append(" ");
logBuilder.append("[uri] ").append(getQuery()).append(" ");
logBuilder.append("[headers] ").append(getHeadersAsString(request)).append(" ");
logBuilder.append("[requestBody] ").append(getBody(request.getContentAsByteArray())).append(" ");

return logBuilder.toString();
}

private String getQuery() {
String uri = request.getRequestURI();
String queryString = request.getQueryString();

if (queryString != null) {
uri += "?" + queryString;
}
return uri;
}

private String getHeadersAsString(HttpServletRequest request) {
return Collections.list(request.getHeaderNames()).stream()
.filter(headerName -> !headerName.equalsIgnoreCase("Authorization"))
.filter(headerName -> !headerName.equalsIgnoreCase("refresh-token"))
.map(headerName -> headerName + "=" + request.getHeader(headerName))
.collect(Collectors.joining(", "));
}

protected String getBody(byte[] info) {
return new String(info, StandardCharsets.UTF_8).replace("\n", "").replace("\r", "");
}
private final ContentCachingRequestWrapper request;
private static final List<String> ALLOWED_HEADERS = List.of(
"x-forwarded-for", "x-real-ip", "user-agent", "content-type", "accept", "host"
);

public ApiLogRequestLogGenerator(ContentCachingRequestWrapper request) {
this.request = request;
}

@Override
public String generateLog() {
StringBuilder logBuilder = new StringBuilder();

logBuilder.append("Request : ").append(" ");
// logBuilder.append(getBaseLogInfo());
logBuilder.append("[method] ").append(request.getMethod()).append(" ");
logBuilder.append("[uri] ").append(getQuery()).append("\n");
logBuilder.append("[headers] ").append(getHeadersAsString(request)).append("\n");
logBuilder.append("[requestBody] ").append(getBody(request.getContentAsByteArray())).append(" ");

return logBuilder.toString();
}

private String getQuery() {
String uri = request.getRequestURI();
String queryString = request.getQueryString();

if (queryString != null) {
uri += "?" + queryString;
}
return uri;
}

private String getHeadersAsString(HttpServletRequest request) {
return ALLOWED_HEADERS.stream()
.map(header -> {
String value = request.getHeader(header);
return value != null ? header + "=" + value : null;
})
.filter(Objects::nonNull)
.collect(Collectors.joining(", "));
}

protected String getBody(byte[] info) {
return new String(info, StandardCharsets.UTF_8).replace("\n", "").replace("\r", "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,44 @@

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.nio.charset.StandardCharsets;

import org.springframework.web.util.ContentCachingResponseWrapper;

public class ApiLogResponseGenerator extends ApiLogGenerator {
private final ContentCachingResponseWrapper response;

public ApiLogResponseGenerator(ContentCachingResponseWrapper response) {
this.response = response;
}

@Override
public String generateLog() {
StringBuilder logBuilder = new StringBuilder();

logBuilder.append("Response : ").append(" ");
logBuilder.append(getBaseLogInfo());
logBuilder.append("[status] ").append(response.getStatus()).append(" ");
logBuilder.append("[responseBody] ").append(getBody(response.getContentAsByteArray())).append(" ");

return logBuilder.toString();
}

protected String getBody(byte[] info) {
String responseBody = "";
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(new String(info, StandardCharsets.UTF_8));
responseBody = replaceToken(rootNode.path("message").asText("N/A"));
} catch (Exception e) {
logger.error("responseBody 추출 오류");
}
return responseBody;
}

private String replaceToken(String responseBody) {
return responseBody.replaceAll("\"accessToken\":\"[^\"]*\"", "\"accessToken\":\"[TOKEN]\"")
.replaceAll("\"refreshToken\":\"[^\"]*\"", "\"refreshToken\":\"[TOKEN]\"");
}
private final ContentCachingResponseWrapper response;

public ApiLogResponseGenerator(ContentCachingResponseWrapper response) {
this.response = response;
}

@Override
public String generateLog() {
StringBuilder logBuilder = new StringBuilder();

logBuilder.append("Response : ").append(" ");
// logBuilder.append(getBaseLogInfo());
logBuilder.append("[status] ").append(response.getStatus()).append(" ");
logBuilder.append("[responseBody] ").append(getBody(response.getContentAsByteArray())).append(" ");

return logBuilder.toString();
}

protected String getBody(byte[] info) {
String responseBody = "";
try {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode rootNode = objectMapper.readTree(new String(info, StandardCharsets.UTF_8));
responseBody = replaceToken(rootNode.path("message").asText("N/A"));
} catch (Exception e) {
logger.error("responseBody 추출 오류");
}
return responseBody;
}

private String replaceToken(String responseBody) {
return responseBody.replaceAll("\"accessToken\":\"[^\"]*\"", "\"accessToken\":\"[TOKEN]\"")
.replaceAll("\"refreshToken\":\"[^\"]*\"", "\"refreshToken\":\"[TOKEN]\"");
}
}
76 changes: 40 additions & 36 deletions src/main/java/umc/th/juinjang/monitoring/ApiLoggerFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.util.List;
import java.util.UUID;

import lombok.extern.slf4j.Slf4j;

import org.slf4j.Logger;
import org.slf4j.MDC;
import org.springframework.util.AntPathMatcher;
Expand All @@ -18,40 +21,41 @@

@Slf4j
public class ApiLoggerFilter extends OncePerRequestFilter {
private static final Logger logger = getLogger(ApiLoggerFilter.class);
private final ApiLoggerFactory apiLoggerFactory;
private final List<String> EXCLUDED_URLS;

public ApiLoggerFilter(List<String> EXCLUDED_URLS) {
this.EXCLUDED_URLS = EXCLUDED_URLS;
this.apiLoggerFactory = new ApiLoggerFactory();
}

@Override
protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain) {
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);
ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(servletResponse);
registerRequestId(UUID.randomUUID().toString());

try {
if (shouldNotFilter(request)) {
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
apiLoggerFactory.createRequestLogger(request);
apiLoggerFactory.createResponseLogger(response);
response.copyBodyToResponse();
} catch (Exception e) {
logger.error("APILogger 필터 오류");
} finally {
MDC.clear();
}
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
AntPathMatcher pathMatcher = new AntPathMatcher();
return EXCLUDED_URLS.stream().anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI()));
}
private static final Logger logger = getLogger(ApiLoggerFilter.class);
private final ApiLoggerFactory apiLoggerFactory;
private final List<String> EXCLUDED_URLS;

public ApiLoggerFilter(List<String> EXCLUDED_URLS) {
this.EXCLUDED_URLS = EXCLUDED_URLS;
this.apiLoggerFactory = new ApiLoggerFactory();
}

@Override
protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
FilterChain chain) {
ContentCachingRequestWrapper request = new ContentCachingRequestWrapper(servletRequest);
ContentCachingResponseWrapper response = new ContentCachingResponseWrapper(servletResponse);
registerRequestId(UUID.randomUUID().toString());

try {
if (shouldNotFilter(request)) {
chain.doFilter(request, response);
return;
}
chain.doFilter(request, response);
apiLoggerFactory.createRequestLogger(request);
apiLoggerFactory.createResponseLogger(response);
response.copyBodyToResponse();
} catch (Exception e) {
logger.error("APILogger 필터 오류, message : {} ", e.getMessage());
} finally {
MDC.clear();
}
}

@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
AntPathMatcher pathMatcher = new AntPathMatcher();
return EXCLUDED_URLS.stream().anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI()));
}
}
Loading