로그 수집 및 에러 알림 만들기

2024. 10. 18. 21:03·트러블슈팅/회사

로그 개발 목적

지금까지 어플리케이션에서 나오는 에러들은 프론트분들이 찾아주셔서 cloudwatch를 들어가서 확인하곤 했다.

하지만 cloudwatch의 로그 가시성이 너무 떨어지고 에러가 났을때 알람과 어디서 에러가났는지 정확히 알기 위해서 개발이 필요했다.

  1. 멀티 스레드 환경에서 같은 스레드의 작업을 찾을 수 있게 한다.
  2. 다른 스레드 작업이 있어도 하나의 트레이스 안에 묶일 수 있게 한다.
  3. 센트리에서 한번에 오류를 볼수있도록 해야한다.(클라우드워치까지 가지않고싶다.)

MDC

  • Log4j의 MDC는 스레드 로컬(thread-local) 변수를 이용하여 각 스레드마다 별도의 컨텍스트 정보를 저장한다.
  • 이를 통해 멀티스레드 환경에서도 로그 메시지에 일관된 컨텍스트를 포함시킬 수 있다.

TraceId 설정

aop 사용해 MDC tracID 유지(실패)

처음에 aop에 before에서 id를 저장하고 after에서 삭제하는 방법을 사용했지만

내가 설정한 log 출력 전에 after가 실행되서 tarceId가 유지되지 않아 하나의 스레드가 같은 traceid 가지지 않게 되었다.

서블릿 스레드풀 설정 변경(안좋은 방법)

스프링을 시작할때 서블릿에서 생성해주는 스레드 풀을 직접 조정해 각 스레드에 tarceId를 넣어주는 작업을 했다.

하지만 내가 서블릿의 스레드풀을 어떻게 만드는지 서블릿 스레드만의 최적화 방법 등 모르는 부분이 있기때문에 직접 스레드풀을 작업하는건 좋지 않다고생각했다.

@Bean
    public TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory()
...
    return new Thread(() -> {
        try {
            if (contextMap != null) {
                MDC.setContextMap(contextMap);
            } else {
                String traceId = UUID.randomUUID().toString();
                MDC.put("traceId", traceId);
                MDC.put("errorFlag", "false");
            }
            r.run();
        } finally {
            MDC.clear();
        }
    }, "bems-thread");
...
}

인터셉터 추가

요청을 받을때 미리 인터셉터에서 필요한 데이트를 세팅하고 끝날때 정리해주는 방식으로 만들었다.

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LogMdcInterceptor());
    }
}

public class LogMdcInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        MDC.put(필요한 데이터 세팅)
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
        Exception ex) {
        MDC.clear();
    }
}

위 그림처럼 멀티 스레드에서 요청이 와도 traceId로 하나의 요청에 따른 흐름을 제대로 읽을 수 있게 되었다.

스레드가 달라져도 traceId 가져가기

비동기 작업시 따로 설정한 스레드를 사용해 작업이 진행된다 이렇게 되면 가지고 있는 threadlocal특성을 지닌 mdc로는 설정한 속성들을 유지하기 힘들어진다.

public static class MDCCopyTaskDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        Map<String, String> contextMap = MDC.getCopyOfContextMap();
        return () -> {
            try {
                if (contextMap != null) {
                    MDC.setContextMap(contextMap);
                }
                runnable.run();
            } finally {
                MDC.clear();
            }
        };
    }
}

스레드를 바꿀때 MDC의 있는 속성을 복사해서 다음 스레드로 넘기는 작업을 하면 스레드가 달라져도 속성들을 지키면서 진행시킬 수 있다.

파라미터 출력

에러가 났을때 어떤 데이터에서 에러가 났는지 확인하기위해 arg를 가져와서 데이터를 만들어 주었다.

    private StringBuilder getErrorArgs(JoinPoint joinPoint, String traceId) {
        ObjectMapper objectMapper = new ObjectMapper();
        Object[] args = joinPoint.getArgs();

        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < args.length; i++) {
            try {
                String json = objectMapper.writeValueAsString(args[i]);
                stringBuilder.append(args[i].getClass()).append(":").append(json);
            } catch (Exception e) {
                log.warn("[traceId]: {},Failed to serialize argument {}: {}", traceId, i, e.getMessage());
            }
        }
        Sentry.setExtra("errorArgs", stringBuilder.toString());
        return stringBuilder;
    }

sentry 설정

plugins {
    id "io.sentry.jvm.gradle" version "4.12.0"
}

sentry {
    includeSourceContext = true

    org = "org"
    projectName = "project-be"
    authToken = ""
}

sentry의 가이드를 따라서 gradle을 추가 후 properties를 설정해주어 에러가 났을때 센트리로 전송이 되도록 설정했다.

위 설정을 안하고 dependency와 properties만 추가해도 센트리로 에러 전송은 되지만 센트리에서 제공하는 여러가지 기능들을 사용하려면 위 설정을 추가해줘야한다.

참고

https://techblog.woowahan.com/13429/#toc-3

'트러블슈팅 > 회사' 카테고리의 다른 글

Modbus protocol  (2) 2024.12.28
loki 적용하기  (0) 2024.11.04
modbus 데이터 흐름 구조  (0) 2024.10.15
instancio  (0) 2024.10.15
batch 인프라 재구성  (1) 2024.10.15
'트러블슈팅/회사' 카테고리의 다른 글
  • Modbus protocol
  • loki 적용하기
  • modbus 데이터 흐름 구조
  • instancio
jamin
jamin
  • jamin
    jamin
    jamin
  • 전체
    오늘
    어제
    • 전체 (82)
      • 트러블슈팅 (31)
        • 회사 (25)
        • 개인 (6)
      • 개념 저장소 (19)
        • coroutine (10)
        • spring reactive (9)
        • network (0)
      • 코딩 테스트 (31)
  • 태그

    다익스트라
    수학
    spring boot
    log
    대용량 데이터
    instancio
    누적합
    cluster mode
    백준 23758
    코딩테스트
    spring reactive
    batch
    분리집합
    Kotlin
    BFS
    coroutine
    다이나믹 프로그래밍
    그리디 알고리즘
    DP
    백준
    sinks
    dfs
    정렬
    error alram
    mirroring mode
    코테
    reactive
    경로압축
    그리디
    JPA
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
jamin
로그 수집 및 에러 알림 만들기
상단으로

티스토리툴바