본문 바로가기

aop

Sentry.io를 이용하여 Log를 한눈에 보기

728x90

1.  POM 의존성설정

 

      <!-- log-->
<dependency>
   <groupId>io.sentry</groupId>
   <artifactId>sentry-spring-boot-starter</artifactId>
   <version>6.14.0</version>
</dependency>

 

2. YML파일 설정

sentry:
  dsn: Sentry.io에서  발급받은 DSN키값

 

일단은 전처리 과정에서 받은 오류를 받아서 Type 값과 method를 입력시켜주었습니다.

 

package com.cos.photogramstart.handler.aop;

import com.cos.photogramstart.handler.ex.CustomVaildationApiException;
import com.cos.photogramstart.handler.ex.CustomVaildationException;
import io.sentry.Sentry;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;


@Component //RestController Service 든 Component 구현체들이다. Compoenet 를 상속해서 만들어져있음
@Aspect //aop 처리를 할수있는 핸들러
public class ValidationAdvice {

       private static final Logger log= LoggerFactory.getLogger(ValidationAdvice.class);

//
//    @Before("execution(* com.cos.photogramstart.web.api.*Controller.*(..))")
//    public void testCheck(){
//        // 어디서든지 요청을 받아올수있음
//        HttpServletRequest request= ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
//
//
//    }




    // web 패키지에서 Controller로 끝나는 모든클래스에서 모든 메서드의 파라미터가 뭐든상관없는거를 호출
    @Around("execution(* com.cos.photogramstart.web.api.*Controller.*(..))")
    public Object apiAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        System.out.println("web api 컨틀롤러==============");

        String type=proceedingJoinPoint.getSignature().getDeclaringTypeName();
        String method=proceedingJoinPoint.getSignature().getName();

        Object [] args=proceedingJoinPoint.getArgs();
        for (Object arg:args){
            if (arg instanceof BindingResult){
                System.out.println("유효성 검사를 하는 함수입니다");
                BindingResult bindingResult=(BindingResult) arg;
                if(bindingResult.hasErrors()){
                    Map<String,String> errorMap=new HashMap<>();

                    for(FieldError error:bindingResult.getFieldErrors()){
                        errorMap.put(error.getField(), error.getDefaultMessage());

                        log.warn(type+"."+method+"() => 필드:"+error.getField()+"메세지"+error.getDefaultMessage());
                        Sentry.captureMessage(type+"."+method+"() => 필드:"+error.getField()+"메세지"+error.getDefaultMessage());
                    }
                    throw new CustomVaildationApiException("유효성검사 실패함",errorMap);
                }
            }
        }
        // ProceedingJoinPoint 함수의 모든곳에 접근할수있는 변수
        // 호출한 함수보다 먼저 실행이됨

        return proceedingJoinPoint.proceed(); //호출한 함수가 실행됨
    }
    @Around("execution(* com.cos.photogramstart.web.*Controller.*(..))")
    public Object advice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // ProceedingJoinPoint 는  컨트롤러 메소드안에 접근할수있는 권한을 만듬

        System.out.println("web 컨틀롤러==============");

        String type=proceedingJoinPoint.getSignature().getDeclaringTypeName();
        String method=proceedingJoinPoint.getSignature().getName();

        Object [] args=proceedingJoinPoint.getArgs();
        for (Object arg:args){
            if (arg instanceof BindingResult){
                BindingResult bindingResult=(BindingResult) arg;
                if(bindingResult.hasErrors()){
                    Map<String,String> errorMap=new HashMap<>();
                    for(FieldError error:bindingResult.getFieldErrors()){
                        errorMap.put(error.getField(),error.getDefaultMessage());
                        log.warn(type+"."+method+"() => 필드:"+error.getField()+"메세지"+error.getDefaultMessage());
                        Sentry.captureMessage(type+"."+method+"() => 필드:"+error.getField()+"메세지"+error.getDefaultMessage());
                    }
                    throw new CustomVaildationException("유효성검사 실패함",errorMap);
                }
            }
        }
        return proceedingJoinPoint.proceed();
    }

}

3. Sentry.Capture(String) 을 통해서 Sentry.io에 메세지를 날립니다.

다음과 같이 어떤 HTTP메서드를 날렸을 때 어떠한 오류가 떳는지 필드오류가 나타나게 됩니다.

 

번외

 

5. logback-spring.xml 을 만들어서 파일안에 로그찍기

 

src->main->resources 안에 logback-spring.xml 파일만들기

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="LOGS_ABSOLUTE_PATH" value="./logs" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">
            <Pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{36} - %msg%n</Pattern>
        </layout>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOGS_ABSOLUTE_PATH}/logback.log</file>
        <encoder>
            <pattern>[%d{yyyy-MM-dd HH:mm:ss}:%-3relative][%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOGS_ABSOLUTE_PATH}/logback.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
            <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>5MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>

    <logger name="com.cos.photogramstart.handler" level="WARN">
        <appender-ref ref="FILE" />
    </logger>

</configuration>

 

 여기서 중요한점은 root-level입니다. 해당하는  STDOUT 표준입출력창을 통해 띄울 레벨을 정하는것입니다.

  File 쪽은 loggername="로깅 처리할 패키지를 써주시면됩니다" 

 

다음과 같이 logs파일안에 logback.log 가 생성되고 로그가 기록되게됩니다.

저는 파일안에 찍히는 로그 레벨을 WARN 부터 했으므로 WARN 및 ERROR 가 찍히게 될 것 입니다.

 

 

728x90

'aop' 카테고리의 다른 글

Spring Filter,Interceptor,AOP 차이  (0) 2023.02.19
AOP 전처리와 후처리 공통로직,핵심로직을 분리.  (0) 2023.02.13
AOP란 무엇인가  (0) 2022.08.17