SPRING

[SPRING] 스프링 로그 모니터링 전 로그 설계

도원좀비 2025. 6. 19. 20:09
모든 요청 흐름을 추적하고, 유저 인증 정보를 로그에 구조화하여 담는 방법에 대해 정리
특히 인증 기반 API를 운영하면서 실시간 대응과 문제 분석을 위해 로그 체계를 정리

1️⃣ 전체 로그 흐름 설계 목표

 

  • traceId 기반으로 요청 → 응답 → 예외 흐름 추적
  • 인증 정보 기반의 핵심 userId, userEmail 로그에 포함
  • 인증 실패나 예외 발생 시에도 동일한 맥락 유지
  • /admin/** 엔드포인트에 대해서는 별도 AOP 로그 확보

 

 

2️⃣ 필터 기반 MDC 구조화 처리

Spring의 OncePerRequestFilter를 활용하여 요청 시점에 traceId, IP, 인증 정보 등을 MDC에 세팅하고,
응답 및 예외 시에도 해당 정보를 그대로 사용

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
    throws ServletException, IOException {

    String uri = request.getRequestURI();
    String ip = getClientIP(request);
    String traceId = UUID.randomUUID().toString().replace("-", "");

    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    if (auth != null && auth.isAuthenticated() && !"anonymousUser".equals(auth.getPrincipal())) {
        Object principal = auth.getPrincipal();
        if (principal instanceof User user) {
            MDC.put("userId", String.valueOf(user.getId()));
            MDC.put("userEmail", user.getEmail());
        } else {
            MDC.put("userId", principal.toString());
            MDC.put("userEmail", "");
        }
    } else {
        MDC.put("userId", "비회원");
        MDC.put("userEmail", "");
    }

    MDC.put("traceId", traceId);
    MDC.put("ip", ip);

    log.info("[REQUEST] {} {} | IP={} | traceId={} | userId={} | userEmail={}", 
        request.getMethod(), uri, ip, traceId, MDC.get("userId"), MDC.get("userEmail"));

    try {
        chain.doFilter(request, response);
    } finally {
        log.info("[RESPONSE] {} {} | status={} | IP={} | traceId={} | userId={} | userEmail={}",
            request.getMethod(), uri, response.getStatus(), ip, traceId, MDC.get("userId"), MDC.get("userEmail"));
        MDC.clear();
    }
}

Logback 설정 예시

<pattern>
  %d{yyyy‑MM‑dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg
  | userId=%X{userId} | userEmail=%X{userEmail} | traceId=%X{traceId} | ip=%X{ip}%n
</pattern>

 

  • MDC.clear()를 finally에서 호출하여 메모리 누수 방지
  • 필터에서 세팅한 MDC는 컨트롤러, 서비스, 예외 핸들러 전 영역에서 자동 포함

3️⃣ AOP 기반 관리자 API 요청 로깅

관리자 API에 대해서는 LoggingAspect를 통해 HTTP 요청 시점에 별도 로그를 남김

@Aspect
@Slf4j
@Component
public class LoggingAspect {

    @Pointcut("within(@org.springframework.web.bind.annotation.RestController *)")
    public void restController() {}

    @Before("restController()")
    public void logRequestInfo(JoinPoint joinPoint) {
        var attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attr == null) return;
        HttpServletRequest req = attr.getRequest();
        String uri = req.getRequestURI();
        if (!uri.startsWith("/admin")) return;

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        if (!isAdmin(auth)) return;

        String method = req.getMethod();
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        String userInfo = getUserInfo(auth);

        log.info("[ADMIN API 요청] {} {} | Controller={}.{} | 유저={}",
            method, uri, className, methodName, userInfo);
    }

    private boolean isAdmin(Authentication auth) {
        return auth != null && auth.isAuthenticated() && auth.getAuthorities().stream()
                .anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"));
    }

    private String getUserInfo(Authentication auth) {
        Object principal = auth.getPrincipal();
        if (principal instanceof User user) {
            return String.format("id=%d, email=%s", user.getId(), user.getEmail());
        }
        return principal.toString();
    }
}

 

예시 출력 로그

[ADMIN API 요청] POST /admin/posts/1/restore
| Controller=PostAdminController.restore | 유저=id=2, email=admin@example.com

 

  • 인증된 ROLE_ADMIN 사용자에 한해 /admin/** 요청이 로깅됩니다.
  • 분리된 감사 로그를 Logstash 등 외부 시스템으로 전송할 수 있습니다.

4️⃣ 다음 작업

 

  • 이 구조를 FileBeat Logstash → Elasticsearch → Kibana에 연동
  • 로그스태시를 활용해서 로그 가공 및 각각 output
  • Alert 설정/이상 패턴 감지 
  • Prometheus + Grafana를 활용해 메트릭 모니터링 구축