1+ package com .memory .interceptor ;
2+
3+ import com .fasterxml .jackson .databind .ObjectMapper ;
4+ import jakarta .servlet .http .HttpServletRequest ;
5+ import jakarta .servlet .http .HttpServletResponse ;
6+ import lombok .RequiredArgsConstructor ;
7+ import lombok .extern .slf4j .Slf4j ;
8+ import org .slf4j .MDC ;
9+ import org .springframework .stereotype .Component ;
10+ import org .springframework .web .method .HandlerMethod ;
11+ import org .springframework .web .servlet .HandlerInterceptor ;
12+ import org .springframework .web .servlet .ModelAndView ;
13+
14+ import java .time .LocalDateTime ;
15+ import java .time .format .DateTimeFormatter ;
16+ import java .util .Enumeration ;
17+ import java .util .HashMap ;
18+ import java .util .Map ;
19+ import java .util .UUID ;
20+
21+ @ Slf4j
22+ @ Component
23+ @ RequiredArgsConstructor
24+ public class ApiLoggingInterceptor implements HandlerInterceptor {
25+
26+ private final ObjectMapper objectMapper ;
27+ private static final String REQUEST_TIME_ATTRIBUTE = "requestTime" ;
28+ private static final String TRACE_ID_ATTRIBUTE = "traceId" ;
29+
30+ @ Override
31+ public boolean preHandle (HttpServletRequest request , HttpServletResponse response , Object handler ) throws Exception {
32+ long startTime = System .currentTimeMillis ();
33+ String traceId = UUID .randomUUID ().toString ().substring (0 , 8 );
34+
35+ request .setAttribute (REQUEST_TIME_ATTRIBUTE , startTime );
36+ request .setAttribute (TRACE_ID_ATTRIBUTE , traceId );
37+
38+ MDC .put ("traceId" , traceId );
39+ MDC .put ("method" , request .getMethod ());
40+ MDC .put ("uri" , request .getRequestURI ());
41+ MDC .put ("userAgent" , request .getHeader ("User-Agent" ));
42+ MDC .put ("remoteAddr" , getClientIpAddress (request ));
43+
44+ if (handler instanceof HandlerMethod handlerMethod ) {
45+ String controllerName = handlerMethod .getBeanType ().getSimpleName ();
46+ String methodName = handlerMethod .getMethod ().getName ();
47+ MDC .put ("controller" , controllerName );
48+ MDC .put ("action" , methodName );
49+ }
50+
51+ Map <String , Object > logData = new HashMap <>();
52+ logData .put ("type" , "API_REQUEST" );
53+ logData .put ("timestamp" , LocalDateTime .now ().format (DateTimeFormatter .ISO_LOCAL_DATE_TIME ));
54+ logData .put ("traceId" , traceId );
55+ logData .put ("method" , request .getMethod ());
56+ logData .put ("uri" , request .getRequestURI ());
57+ logData .put ("queryString" , request .getQueryString ());
58+ logData .put ("userAgent" , request .getHeader ("User-Agent" ));
59+ logData .put ("remoteAddr" , getClientIpAddress (request ));
60+ logData .put ("headers" , getHeaders (request ));
61+
62+ if (handler instanceof HandlerMethod handlerMethod ) {
63+ logData .put ("controller" , handlerMethod .getBeanType ().getSimpleName ());
64+ logData .put ("method" , handlerMethod .getMethod ().getName ());
65+ }
66+
67+ log .info ("API Request: {}" , objectMapper .writeValueAsString (logData ));
68+
69+ return true ;
70+ }
71+
72+ @ Override
73+ public void postHandle (HttpServletRequest request , HttpServletResponse response , Object handler ,
74+ ModelAndView modelAndView ) throws Exception {
75+ // 특별한 처리가 필요한 경우 여기에 구현
76+ }
77+
78+ @ Override
79+ public void afterCompletion (HttpServletRequest request , HttpServletResponse response , Object handler ,
80+ Exception ex ) throws Exception {
81+ try {
82+ long startTime = (Long ) request .getAttribute (REQUEST_TIME_ATTRIBUTE );
83+ String traceId = (String ) request .getAttribute (TRACE_ID_ATTRIBUTE );
84+ long endTime = System .currentTimeMillis ();
85+ long processingTime = endTime - startTime ;
86+
87+ Map <String , Object > logData = new HashMap <>();
88+ logData .put ("type" , "API_RESPONSE" );
89+ logData .put ("timestamp" , LocalDateTime .now ().format (DateTimeFormatter .ISO_LOCAL_DATE_TIME ));
90+ logData .put ("traceId" , traceId );
91+ logData .put ("method" , request .getMethod ());
92+ logData .put ("uri" , request .getRequestURI ());
93+ logData .put ("statusCode" , response .getStatus ());
94+ logData .put ("processingTime" , processingTime );
95+ logData .put ("success" , response .getStatus () < 400 );
96+
97+ if (ex != null ) {
98+ logData .put ("exception" , ex .getClass ().getSimpleName ());
99+ logData .put ("errorMessage" , ex .getMessage ());
100+ log .error ("API Response with Exception: {}" , objectMapper .writeValueAsString (logData ), ex );
101+ } else {
102+ if (response .getStatus () >= 400 ) {
103+ log .warn ("API Response: {}" , objectMapper .writeValueAsString (logData ));
104+ } else {
105+ log .info ("API Response: {}" , objectMapper .writeValueAsString (logData ));
106+ }
107+ }
108+ } finally {
109+ MDC .clear ();
110+ }
111+ }
112+
113+ private String getClientIpAddress (HttpServletRequest request ) {
114+ String xForwardedFor = request .getHeader ("X-Forwarded-For" );
115+ if (xForwardedFor != null && !xForwardedFor .isEmpty ()) {
116+ return xForwardedFor .split ("," )[0 ].trim ();
117+ }
118+
119+ String xRealIp = request .getHeader ("X-Real-IP" );
120+ if (xRealIp != null && !xRealIp .isEmpty ()) {
121+ return xRealIp ;
122+ }
123+
124+ return request .getRemoteAddr ();
125+ }
126+
127+ private Map <String , String > getHeaders (HttpServletRequest request ) {
128+ Map <String , String > headers = new HashMap <>();
129+ Enumeration <String > headerNames = request .getHeaderNames ();
130+
131+ while (headerNames .hasMoreElements ()) {
132+ String headerName = headerNames .nextElement ();
133+ // 민감한 정보는 로그에서 제외
134+ if (!headerName .toLowerCase ().contains ("authorization" ) &&
135+ !headerName .toLowerCase ().contains ("cookie" )) {
136+ headers .put (headerName , request .getHeader (headerName ));
137+ }
138+ }
139+
140+ return headers ;
141+ }
142+ }
0 commit comments