diff --git a/.gitignore b/.gitignore index 3c04657..de8b0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ out/ .vscode/ .gradle/ -.gradle/** \ No newline at end of file +.gradle/** + +samples/ +samples/** \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 41ea7ba..e8622fb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gradle:7.6.0-jdk17 AS dependencies +FROM gradle:8.12.1-jdk17 AS dependencies # Disable the Gradle daemon for Continuous Integration servers as correctness # is usually a priority over speed in CI environments. Using a fresh diff --git a/docker-compose.yaml b/docker-compose.yaml index 66ddf24..55495b7 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,24 +1,10 @@ version: '3.8' -networks: - your-hike: - external: true - services: - yh-backend-gateway: + jateway: build: ./ - container_name: yh-gateway - networks: - - your-hike - restart: always - environment: - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE: ${EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE?Adjust default zone to Eureka} - SPRING_CLOUD_CONFIG_URL: ${SPRING_CLOUD_CONFIG_URL?Specify config server URL} - SPRING_CONFIG_IMPORT: optional:configserver:${SPRING_CLOUD_CONFIG_URL?Specify config server URL} - AUTH_URL: ${AUTH_URL?Specify Auth URL} - SPRING_BOOT_ADMIN_CLIENT_URL: http://yh-admin-panel:8080/ - OTEL_EXPORTER_OTLP_ENDPOINT: ${OTEL_EXPORTER_OTLP_ENDPOINT?Specify OpenTelemetry exporter endpoint like http://localhost:4317} - OTEL_SERVICE_NAME: yh-gateway - volumes: - - /var/log/yh:/var/log/yh + container_name: jateway + restart: unless-stopped + ports: + - 8080:8080 diff --git a/service/build.gradle b/service/build.gradle index 821751e..5456653 100644 --- a/service/build.gradle +++ b/service/build.gradle @@ -22,8 +22,6 @@ dependencyManagement { dependencies { implementation "org.springframework.cloud:spring-cloud-starter-gateway:${gatewayStarterVersion}" - // https://mvnrepository.com/artifact/com.google.guava/guava - implementation group: 'com.google.guava', name: 'guava', version: '33.4.0-jre' // actuator implementation "org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}" diff --git a/service/src/main/java/me/bvn13/jateway/logging/GatewayRoutingLoggingFilter.java b/service/src/main/java/me/bvn13/jateway/logging/GatewayRoutingLoggingFilter.java deleted file mode 100644 index 8a07d67..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/GatewayRoutingLoggingFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.bvn13.jateway.logging; - -import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*; - -import java.net.URI; -import java.util.Collections; -import java.util.Set; - -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.cloud.gateway.route.Route; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Component; -import org.springframework.web.server.ServerWebExchange; - -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Mono; - -@Slf4j -@Order(Ordered.HIGHEST_PRECEDENCE) -@Component -public class GatewayRoutingLoggingFilter implements GlobalFilter { - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - - final Set uris = exchange.getAttributeOrDefault(GATEWAY_ORIGINAL_REQUEST_URL_ATTR, Collections.emptySet()); - final String originalUri = (uris.isEmpty()) - ? exchange.getRequest().getURI().toString() - : uris.iterator().next().toString(); - final Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); - final URI routeUrl = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); - log.info("Incoming request " + originalUri + " is routed with <" + route.getId() + "> to URL: " + routeUrl); - return chain.filter(exchange); - - } - -} diff --git a/service/src/main/java/me/bvn13/jateway/logging/IgnoringActuatorLoggingFilter.java b/service/src/main/java/me/bvn13/jateway/logging/IgnoringActuatorLoggingFilter.java deleted file mode 100644 index ba13479..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/IgnoringActuatorLoggingFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.bvn13.jateway.logging; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.spi.FilterReply; - -public class IgnoringActuatorLoggingFilter extends Filter { - private static final Pattern ACTUATOR = - Pattern.compile("GET \"/(health|prometheus)\", parameters=\\{}"); - private static final Pattern COMPLETED = - Pattern.compile("Completed 200 OK"); - - private final Set activeThreads = new HashSet<>(); - - @Override - public FilterReply decide(ILoggingEvent loggingEvent) { - if (isHealthOrPrometheus(loggingEvent.getMessage())) { - activeThreads.add(loggingEvent.getThreadName()); - return FilterReply.DENY; - } else if (isCompleted200Ok(loggingEvent.getMessage()) && activeThreads.remove(loggingEvent.getThreadName())) { - return FilterReply.DENY; - } else { - return FilterReply.ACCEPT; - } - } - - private boolean isHealthOrPrometheus(String message) { - return ACTUATOR.matcher(message).matches(); - } - - private boolean isCompleted200Ok(String message) { - return COMPLETED.matcher(message).matches(); - } -} diff --git a/service/src/main/java/me/bvn13/jateway/logging/LoggingConfig.java b/service/src/main/java/me/bvn13/jateway/logging/LoggingConfig.java deleted file mode 100644 index 8df8558..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/LoggingConfig.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.bvn13.jateway.logging; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import lombok.extern.slf4j.Slf4j; - - -@Slf4j -@Configuration -public class LoggingConfig { - - @Bean - public ReactiveLoggingFilter reactiveLoggingFilter() { - return new ReactiveLoggingFilter(); - } - -} diff --git a/service/src/main/java/me/bvn13/jateway/logging/LoggingFilter.java b/service/src/main/java/me/bvn13/jateway/logging/LoggingFilter.java new file mode 100644 index 0000000..6b82915 --- /dev/null +++ b/service/src/main/java/me/bvn13/jateway/logging/LoggingFilter.java @@ -0,0 +1,46 @@ +package me.bvn13.jateway.logging; + +import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.*; + +import java.net.URI; + +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.route.Route; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; + +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Slf4j +@Component +public class LoggingFilter implements GlobalFilter, Ordered { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + URI originalUri = request.getURI(); + String requestHeaders = request.getHeaders().toString(); + Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); + URI uri = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR); + String statusCode = String.valueOf(response.getStatusCode().value()); + String responseHeaders = response.getHeaders().toString(); + String msg = "" + route.getId() + " | " + statusCode + " | " + originalUri + " -> " + uri + " | request headers: " + requestHeaders + ", response headers: " + responseHeaders; + if (response.getStatusCode().isError()) { + log.error(msg); + } else { + log.info(msg); + } + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } +} diff --git a/service/src/main/java/me/bvn13/jateway/logging/ReactiveLoggingFilter.java b/service/src/main/java/me/bvn13/jateway/logging/ReactiveLoggingFilter.java deleted file mode 100644 index d4ac726..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/ReactiveLoggingFilter.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.bvn13.jateway.logging; - -import static me.bvn13.jateway.utils.DateFormats.*; - -import java.time.Instant; - -import org.springframework.cloud.gateway.filter.GatewayFilterChain; -import org.springframework.cloud.gateway.filter.GlobalFilter; -import org.springframework.core.Ordered; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.lang.Nullable; -import org.springframework.web.server.ServerWebExchange; -import org.springframework.web.server.ServerWebExchangeDecorator; - -import lombok.extern.slf4j.Slf4j; -import me.bvn13.jateway.utils.LogEscapeUtils; -import me.bvn13.jateway.utils.LogFormatUtils; -import reactor.core.publisher.Mono; - -@Slf4j -public class ReactiveLoggingFilter implements GlobalFilter, Ordered { - - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { - final Instant requestTime = Instant.now(); - - final StringBuilder reqBb = new StringBuilder(); - final StringBuilder respBb = new StringBuilder(); - - ServerWebExchangeDecorator exchangeDecorator = new ServerWebExchangeDecorator(exchange) { - @Override - public ServerHttpRequest getRequest() { - return new RequestLoggingWrapper(super.getRequest(), reqBb); - } - - @Override - public ServerHttpResponse getResponse() { - return new ResponseLoggingWrapper(super.getResponse(), respBb); - } - }; - return chain.filter(exchangeDecorator).doOnSuccess(aVoid -> { - log(requestTime, exchange.getRequest(), reqBb.toString(), exchange.getResponse(), respBb.toString(), null); - }).doOnError(throwable -> { - log(requestTime, exchange.getRequest(), reqBb.toString(), exchange.getResponse(), respBb.toString(), throwable); - }); - } - - - @Override - public int getOrder() { -// return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1; - return Ordered.HIGHEST_PRECEDENCE; - } - - private void log(Instant requestTime, ServerHttpRequest request, String requestBody, ServerHttpResponse response, String responseBody, @Nullable Throwable throwable) { - String message = formatMessage(requestTime, request, requestBody, response, responseBody); - - if (throwable == null) { - log.info(message); - } else { - log.info(message, throwable); - } - } - - private String formatMessage(Instant requestTime, ServerHttpRequest request, String requestBody, ServerHttpResponse response, String responseBody) { - StringBuilder req = new StringBuilder("Normalized "); - req.append(" " + request.getMethod() + " uri=" + request.getURI() + "\n") - .append("request_headers=[\n" + LogFormatUtils.formatHeaders(request.getHeaders()) + "]\n") - .append("request_time=" + formatLocalDateTime(requestTime) + "\n"); - if (!requestBody.isEmpty()) { - req.append("request_body=[\n" + requestBody + "\n]"); - } - String filteredReqMsg = req.toString(); - - StringBuilder resp = new StringBuilder(); - resp.append("\nResponse " + response.getStatusCode() + "\n" + - "response_headers=[\n" + LogFormatUtils.formatHeaders(response.getHeaders()) + "]\n"); - if (!responseBody.isEmpty()) { - resp.append("response_body=[\n" + responseBody + "\n]"); - } - - String filteredRespMsg = resp.toString(); - - return LogEscapeUtils.escapeWithLf(filteredReqMsg + filteredRespMsg); - } - -} diff --git a/service/src/main/java/me/bvn13/jateway/logging/RequestLoggingWrapper.java b/service/src/main/java/me/bvn13/jateway/logging/RequestLoggingWrapper.java deleted file mode 100644 index b0866e7..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/RequestLoggingWrapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package me.bvn13.jateway.logging; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpRequestDecorator; -import reactor.core.publisher.Flux; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.channels.Channels; -import java.nio.charset.StandardCharsets; - -@Slf4j -class RequestLoggingWrapper extends ServerHttpRequestDecorator { - private final StringBuilder bb; - - public RequestLoggingWrapper(ServerHttpRequest delegate, StringBuilder bb) { - super(delegate); - this.bb = bb; - } - - @Override - public Flux getBody() { - return super.getBody().doOnNext(data ->{ - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Channels.newChannel(baos).write(data.asByteBuffer().asReadOnlyBuffer()); - bb.append(new String(baos.toByteArray(), StandardCharsets.UTF_8)); - } catch (IOException ex) { - log.error("Error when attempt to collect request body", ex); - } - }); - } -} diff --git a/service/src/main/java/me/bvn13/jateway/logging/ResponseLoggingWrapper.java b/service/src/main/java/me/bvn13/jateway/logging/ResponseLoggingWrapper.java deleted file mode 100644 index e1a0f3f..0000000 --- a/service/src/main/java/me/bvn13/jateway/logging/ResponseLoggingWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.bvn13.jateway.logging; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.channels.Channels; -import java.nio.charset.StandardCharsets; - -import org.reactivestreams.Publisher; -import org.slf4j.Logger; -import org.springframework.core.io.buffer.DataBuffer; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.http.server.reactive.ServerHttpResponseDecorator; - -import lombok.extern.slf4j.Slf4j; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -@Slf4j -class ResponseLoggingWrapper extends ServerHttpResponseDecorator { - private final StringBuilder bb; - - public ResponseLoggingWrapper(ServerHttpResponse delegate, StringBuilder bb) { - super(delegate); - this.bb = bb; - } - - @Override - public Mono writeWith(Publisher body) { - final Logger l = log; - Flux buffer = Flux.from(body); - return super.writeWith(buffer.doOnNext( data -> { - try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) { - Channels.newChannel(baos).write(data.asByteBuffer().asReadOnlyBuffer()); - bb.append(new String(baos.toByteArray(), StandardCharsets.UTF_8)); - } catch (IOException ex) { - l.error("Error when attempt to collect response body", ex); - } - })); - } -} diff --git a/service/src/main/java/me/bvn13/jateway/utils/DateFormats.java b/service/src/main/java/me/bvn13/jateway/utils/DateFormats.java deleted file mode 100644 index 74ab3a4..0000000 --- a/service/src/main/java/me/bvn13/jateway/utils/DateFormats.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.bvn13.jateway.utils; - -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; - -import jakarta.annotation.Nonnull; - -public class DateFormats { - private static final String OFFSET_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss+Z"; - private static final DateTimeFormatter LOCAL_DATE_TIME = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS") - .withZone(ZoneId.systemDefault()); - private static final DateTimeFormatter OFFSET_DATE_TIME = DateTimeFormatter.ofPattern(OFFSET_DATE_TIME_FORMAT); - - @Nonnull - public static String formatLocalDateTime(@Nonnull Instant timestamp) { - return LOCAL_DATE_TIME.format(timestamp); - } - - public static String format(OffsetDateTime offsetDateTime) { - return OFFSET_DATE_TIME.format(offsetDateTime); - } - private DateFormats() { - } -} diff --git a/service/src/main/java/me/bvn13/jateway/utils/LogEscapeUtils.java b/service/src/main/java/me/bvn13/jateway/utils/LogEscapeUtils.java deleted file mode 100644 index a820a15..0000000 --- a/service/src/main/java/me/bvn13/jateway/utils/LogEscapeUtils.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.bvn13.jateway.utils; - -import com.google.common.escape.Escaper; -import com.google.common.net.PercentEscaper; - -import jakarta.annotation.Nullable; - -public class LogEscapeUtils { - private static final Escaper ESCAPER_WITH_LF = new PercentEscaper("\n $#%!?={}[]()<>\"':;,._/\\*-+@&№^" + - "абвгдеёжзийклмнопрстуфхцчшщъыьэюя" + - "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ", false); - - public static String escapeWithLf(@Nullable String str) { - if (str == null) { - return "null"; - } - str = str.replace("\r", ""); - str = str.replace("\t", " "); - return ESCAPER_WITH_LF.escape(str); - } - - private LogEscapeUtils() { - } -} diff --git a/service/src/main/java/me/bvn13/jateway/utils/LogFormatUtils.java b/service/src/main/java/me/bvn13/jateway/utils/LogFormatUtils.java deleted file mode 100644 index 7b93383..0000000 --- a/service/src/main/java/me/bvn13/jateway/utils/LogFormatUtils.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.bvn13.jateway.utils; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.List; -import java.util.Map; - -public class LogFormatUtils { - public static String formatHeaders(Map> responseHeaders) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - responseHeaders.forEach((name, values) -> { - values.forEach(value -> pw.println(name + ": " + value)); - }); - return sw.toString(); - } -} diff --git a/service/src/main/resources/application.yaml b/service/src/main/resources/application.yaml index b4a082e..117b5c6 100644 --- a/service/src/main/resources/application.yaml +++ b/service/src/main/resources/application.yaml @@ -1,12 +1,21 @@ +server: + port: 8080 spring: application: name: jateway profiles: active: dev + cloud: + gateway: + routes: + - id: simple_route + uri: http://example.com + predicates: + - Method=GET management: server: - port: 8203 + port: 8081 endpoints: enabled-by-default: false web: @@ -36,9 +45,9 @@ management: logging: level: root: info - org.springframework.web.filter.CommonsRequestLoggingFilter: debug - org.springframework.web.server.adapter.HttpWebHandlerAdapter: debug - org.springframework.web.reactive.handler.SimpleUrlHandlerMapping: debug - org.springframework.web.reactive.resource.ResourceWebHandler: debug - org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler: debug -# org.springframework.web: debug + # org.springframework.web.filter.CommonsRequestLoggingFilter: debug + # org.springframework.web.server.adapter.HttpWebHandlerAdapter: debug + # org.springframework.web.reactive.handler.SimpleUrlHandlerMapping: debug + # org.springframework.web.reactive.resource.ResourceWebHandler: debug + # org.springframework.boot.autoconfigure.web.reactive.error.AbstractErrorWebExceptionHandler: debug + # org.springframework.web: debug diff --git a/service/src/main/resources/logback.xml b/service/src/main/resources/logback.xml index 9d7095b..f0a12bc 100644 --- a/service/src/main/resources/logback.xml +++ b/service/src/main/resources/logback.xml @@ -1,12 +1,11 @@ - - %date %level [%thread] %logger{10} [%file:%line] %msg%n + %date | %level | %msg%n - +