Spring Cloud Gateway LoggingFilter

최준호·2022년 2월 22일
0

Microservice Architecture

목록 보기
8/32
post-thumbnail

👏Filter를 만들기 전 apply 뜯어보기

우리가 이전 포스트들에서 항상 아무생각 없이 override하여 사용했던 apply를 뜯어보자.

@Override
public GatewayFilter apply(Config config) {
    return ((exchange, chain) -> {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        log.info("Global PRE baseMessage : {}", config.getBaseMessage());

        if(config.isPreLogger()){
            log.info("Global Filter Start : request id = {}", request.getId());
        }


        //custom post filter
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            if(config.isPostLogger()){
                log.info("Global Filter End : response code = {}", response.getStatusCode());
            }
        }));
    });
}

다음과 같이 exchange와 chain 매개변수를 받아서 람다식으로 풀어서 사용했는데 우리가 사용하는 exchange와 chain은 어디서 오는 것일까?

@Override
public GatewayFilter apply(Config config) {
    GatewayFilter filter = new OrderedGatewayFilter();
    
    return filter;
}

우리가 지금까지 람다로 만들어서 return하던 GatewayFilter 객체를 직접 생성하려고 하면 new OrderedGatewayFilter가 구현체가 되어 GatewayFilter를 생성하여 왔다. 이 OrderedGatewayFilter의 코드를 살펴보면

위 코드들을 확인할 수 있고 우리가 사용했던 exchange와 chain을 매개변수로 받는 filter mehtod를 확인할 수 있다.

Spring 5.x부터 RxJava(비동기 통신)을 지원하면서 더이상 MVC 패턴이 아닌 WebFlux 기술을 사용한다. WebFlux에서는 Servlet의 Request와 Response를 사용하지 않고 ServerRequest와 ServerResponse 객체를 사용하는데 해당 객체들을 사용하게끔 도와주는 것이 ServerWebExchange 객체이다.

그리고 GatewayFilterchain을 사용하여 Filter들을 연결하는 것에 사용한다.

@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {

    public LoggingFilter() {
        super(Config.class);
    }

    @Data
    public static class Config{
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }

    @Override
    public GatewayFilter apply(Config config) {
        GatewayFilter filter = new OrderedGatewayFilter((exchange, chain)->{
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            log.info("Logging Filter baseMessage : {}", config.getBaseMessage());

            if(config.isPreLogger()){
                log.info("Logging PRE Start : request id = {}", request.getId());
            }


            //custom post filter
            return chain.filter(exchange).then(Mono.fromRunnable(()->{
                if(config.isPostLogger()){
                    log.info("Logging POST End : response code = {}", response.getStatusCode());
                }
            }));
        }, Ordered.HIGHEST_PRECEDENCE); //우선순위

        return filter;
    }
}

filter를 다음과 같이 작성할 수 있다.

그 후에 second service에만 Logging Filter를 적용시켜 보자.

server:
  port: 8000

eureka: #eureka 세팅은 현재 사용 안함
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka

spring:
  application:  #gateway service 이름름    name: apigateway-service
  cloud:
    gateway:  #gateway 설정
      default-filters:
        - name : GlobalFilter #Global Filter로 지정된 java 파일 이름
          args:
            baseMessage: Spring Cloud Gateway Global Filter
            preLogger: true
            postLogger: true
      routes:
        - id: first-service #gateway로 연결될 서비스 이름
          uri: http://127.0.0.1:8001/ #gateway로 연결될 서비스 uri
          predicates: #gateway로 연결될 서비스의 url 매핑
            - Path=/first-service/**
          filters:
#            - AddRequestHeader=first-request, first-request-header2
#            - AddResponseHeader=first-response, first-response-header2
            - CustomFilter
        - id: second-service
          uri: http://127.0.0.1:8002/
          predicates:
            - Path=/second-service/**
          filters:
#            - AddRequestHeader=second-request, second-request-header2
#            - AddResponseHeader=second-response, second-response-header2
            - name: CustomFilter
            - name: LoggingFilter
              args:
                baseMessage: Hi, Logging Filter.
                preLogger: true
                postLogger: true

전체 코드이다.

Service 실행

first service를 실행한 결과는 이전과 동일하게 출력이된다. 우리가 second service에 설정했으므로 first service에는 전혀 아무런 변경이 없기 때문이다.

하지만 second service는 우리가 설정한대로
Logging -> Global -> Custom 순서로 실행되었다. 이 우선순위를 변경하기 위해서는

@Override
public GatewayFilter apply(Config config) {
    GatewayFilter filter = new OrderedGatewayFilter((exchange, chain)->{
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();

        log.info("Logging Filter baseMessage : {}", config.getBaseMessage());

        if(config.isPreLogger()){
            log.info("Logging PRE Start : request id = {}", request.getId());
        }


        //custom post filter
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            if(config.isPostLogger()){
                log.info("Logging POST End : response code = {}", response.getStatusCode());
            }
        }));
    }, Ordered.LOWEST_PRECEDENCE); //우선순위

    return filter;
}

마지막 우선순위를 LOWEST로 변경해준다면

다음과 같이 순서를 변경할 수 있다.

profile
코딩을 깔끔하게 하고 싶어하는 초보 개발자 (편하게 글을 쓰기위해 반말체를 사용하고 있습니다! 양해 부탁드려요!) 현재 KakaoVX 근무중입니다!

0개의 댓글