mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-09 03:17:52 +00:00
refactor(server): slim down request logging to core parameters only
This commit is contained in:
parent
cd570ed208
commit
049f5acb64
3 changed files with 83 additions and 52 deletions
|
|
@ -15,24 +15,32 @@ import org.springframework.web.util.ContentCachingResponseWrapper;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Logs inbound HTTP requests and responses with truncation suitable for operational debugging.
|
||||
* Logs inbound HTTP requests with only core parameters to keep log files compact.
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
|
||||
public class RequestLoggingFilter extends OncePerRequestFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);
|
||||
private static final int MAX_LOG_BODY_LENGTH = 512;
|
||||
private static final int MAX_LOG_BODY_LENGTH = 200;
|
||||
|
||||
private static final Set<String> SKIP_PREFIXES = Set.of(
|
||||
"/actuator", "/favicon.ico", "/assets/"
|
||||
);
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
String uri = request.getRequestURI();
|
||||
if (shouldSkip(uri)) {
|
||||
filterChain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(request);
|
||||
ContentCachingResponseWrapper cachedResponse = new ContentCachingResponseWrapper(response);
|
||||
|
||||
|
|
@ -52,45 +60,43 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
|
|||
String queryString = request.getQueryString();
|
||||
String fullUrl = queryString != null ? requestUri + "?" + queryString : requestUri;
|
||||
|
||||
String contentType = request.getContentType();
|
||||
String userAgent = request.getHeader("User-Agent");
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n========== HTTP Request ==========\n");
|
||||
sb.append("URL: ").append(request.getMethod()).append(" ").append(fullUrl).append("\n");
|
||||
sb.append("Remote Address: ").append(request.getRemoteAddr()).append("\n");
|
||||
sb.append("Headers: ").append(getHeaders(request)).append("\n");
|
||||
sb.append(request.getMethod()).append(" ").append(fullUrl);
|
||||
sb.append(" | ").append(response.getStatus());
|
||||
sb.append(" | ").append(duration).append("ms");
|
||||
sb.append(" | ").append(request.getRemoteAddr());
|
||||
if (contentType != null) {
|
||||
sb.append(" | Content-Type: ").append(contentType);
|
||||
}
|
||||
if (userAgent != null) {
|
||||
sb.append(" | UA: ").append(truncate(userAgent, 80));
|
||||
}
|
||||
|
||||
String requestBody = getRequestBody(request);
|
||||
if (requestBody != null && !requestBody.isBlank()) {
|
||||
sb.append("Request Body: ").append(requestBody).append("\n");
|
||||
sb.append(" | Body: ").append(requestBody);
|
||||
}
|
||||
|
||||
sb.append("Response Status: ").append(response.getStatus()).append("\n");
|
||||
|
||||
String responseBody = getResponseBody(response);
|
||||
if (responseBody != null && !responseBody.isBlank()) {
|
||||
sb.append("Response Body: ").append(responseBody).append("\n");
|
||||
}
|
||||
|
||||
sb.append("Duration: ").append(duration).append("ms\n");
|
||||
sb.append("===================================");
|
||||
|
||||
log.info(sb.toString());
|
||||
}
|
||||
|
||||
private Map<String, String> getHeaders(HttpServletRequest request) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
String headerName = headerNames.nextElement();
|
||||
headers.put(headerName, request.getHeader(headerName));
|
||||
private boolean shouldSkip(String uri) {
|
||||
for (String prefix : SKIP_PREFIXES) {
|
||||
if (uri.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getRequestBody(ContentCachingRequestWrapper request) {
|
||||
byte[] buf = request.getContentAsByteArray();
|
||||
if (buf.length > 0) {
|
||||
try {
|
||||
return truncateBody(new String(buf, request.getCharacterEncoding()));
|
||||
return truncate(new String(buf, request.getCharacterEncoding()), MAX_LOG_BODY_LENGTH);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return "[unknown encoding]";
|
||||
}
|
||||
|
|
@ -98,23 +104,10 @@ public class RequestLoggingFilter extends OncePerRequestFilter {
|
|||
return null;
|
||||
}
|
||||
|
||||
private String getResponseBody(ContentCachingResponseWrapper response) {
|
||||
byte[] buf = response.getContentAsByteArray();
|
||||
if (buf.length > 0) {
|
||||
try {
|
||||
return truncateBody(new String(buf, response.getCharacterEncoding()));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return "[unknown encoding]";
|
||||
}
|
||||
private String truncate(String value, int maxLength) {
|
||||
if (value == null || value.length() <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String truncateBody(String body) {
|
||||
if (body == null || body.length() <= MAX_LOG_BODY_LENGTH) {
|
||||
return body;
|
||||
}
|
||||
return body.substring(0, MAX_LOG_BODY_LENGTH)
|
||||
+ "... [truncated, original length=" + body.length() + "]";
|
||||
return value.substring(0, maxLength) + "...[truncated]";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,5 +30,5 @@ skillhub:
|
|||
|
||||
logging:
|
||||
level:
|
||||
com.iflytek.skillhub: DEBUG
|
||||
org.springframework.security: DEBUG
|
||||
com.iflytek.skillhub: INFO
|
||||
org.springframework.security: WARN
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
class RequestLoggingFilterTest {
|
||||
|
||||
@Test
|
||||
void doFilterInternal_truncatesLongRequestAndResponseBodiesInLogs(CapturedOutput output)
|
||||
void doFilterInternal_truncatesLongRequestBodyAndOmitsResponseBody(CapturedOutput output)
|
||||
throws ServletException, IOException {
|
||||
RequestLoggingFilter filter = new RequestLoggingFilter();
|
||||
String longBody = "x".repeat(5_000);
|
||||
|
|
@ -39,10 +39,48 @@ class RequestLoggingFilterTest {
|
|||
|
||||
filter.doFilter(request, response, filterChain);
|
||||
|
||||
assertThat(output).contains("Request Body: " + "x".repeat(512) + "... [truncated, original length=5000]");
|
||||
assertThat(output).contains("Response Body: " + "x".repeat(512) + "... [truncated, original length=5000]");
|
||||
assertThat(output).doesNotContain("Request Body: " + longBody);
|
||||
assertThat(output).doesNotContain("Response Body: " + longBody);
|
||||
// Request body should be truncated at 200 chars
|
||||
assertThat(output).contains("Body: " + "x".repeat(200) + "...[truncated]");
|
||||
assertThat(output).doesNotContain("Body: " + longBody);
|
||||
// Response body should not be logged at all
|
||||
assertThat(output).doesNotContain("Response Body:");
|
||||
// Original response should still be intact
|
||||
assertThat(response.getContentAsString()).isEqualTo(longBody);
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInternal_skipsActuatorEndpoints(CapturedOutput output)
|
||||
throws ServletException, IOException {
|
||||
RequestLoggingFilter filter = new RequestLoggingFilter();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/actuator/health");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
FilterChain filterChain = (req, res) -> {};
|
||||
|
||||
filter.doFilter(request, response, filterChain);
|
||||
|
||||
assertThat(output).doesNotContain("/actuator/health");
|
||||
}
|
||||
|
||||
@Test
|
||||
void doFilterInternal_logsCoreSummaryFields(CapturedOutput output)
|
||||
throws ServletException, IOException {
|
||||
RequestLoggingFilter filter = new RequestLoggingFilter();
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/v1/skills");
|
||||
request.setRemoteAddr("127.0.0.1");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
|
||||
FilterChain filterChain = (req, res) -> {};
|
||||
|
||||
filter.doFilter(request, response, filterChain);
|
||||
|
||||
assertThat(output).contains("GET /api/v1/skills");
|
||||
assertThat(output).contains("200");
|
||||
assertThat(output).contains("127.0.0.1");
|
||||
assertThat(output).contains("ms");
|
||||
// Should not contain full headers dump
|
||||
assertThat(output).doesNotContain("Headers: {");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue