diff --git a/spring-cloud-huawei-common/src/main/java/com/huaweicloud/common/configration/dynamic/GovernanceProperties.java b/spring-cloud-huawei-common/src/main/java/com/huaweicloud/common/configration/dynamic/GovernanceProperties.java index 6218d24e6..41f4e32c6 100644 --- a/spring-cloud-huawei-common/src/main/java/com/huaweicloud/common/configration/dynamic/GovernanceProperties.java +++ b/spring-cloud-huawei-common/src/main/java/com/huaweicloud/common/configration/dynamic/GovernanceProperties.java @@ -30,6 +30,8 @@ public class GovernanceProperties { public static final int WEB_FILTER_SERVICE_AUTH_ORDER = -60000; + public static final int WEB_FILTER_REQUEST_LOGGER_ORDER = -35000; + private static final int WEB_CLIENT_LOAD_BALANCE_BASE = 0; public static final int WEB_CLIENT_FAULT_INJECTION_ORDER = @@ -56,6 +58,9 @@ public class GovernanceProperties { public static final String WEBCLIENT_FAULT_INJECTION_ENABLED = PREFIX + "." + "webclient.faultInjection.enabled"; + public static final String WEBCLIENT_REQUEST_LOGGER_ENABLED = + PREFIX + "." + "webclient.requestLogger.enabled"; + public static final String GATEWAY_GOVERNANCE_ENABLED = PREFIX + "." + "gateway.governance.enabled"; public static final String GATEWAY_RETRY_ENABLED = PREFIX + "." + "gateway.retry.enabled"; @@ -78,6 +83,12 @@ public class GovernanceProperties { public static final String WEBFLUX_CONTEXT_MAPPER_ENABLED = PREFIX + "." + "webflux.contextMapper.enabled"; + public static final String WEBFLUX_REQUEST_LOGGER_ENABLED = + PREFIX + "." + "webflux.requestLogger.enabled"; + + public static final String WEBMVC_REQUEST_LOGGER_ENABLED = + PREFIX + "." + "webmvc.requestLogger.enabled"; + public static final String WEBMVC_RATE_LIMITING_ENABLED = PREFIX + "." + "webmvc.rateLimiting.enabled"; diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/RequestServiceInfoLoggerUtil.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/RequestServiceInfoLoggerUtil.java new file mode 100644 index 000000000..ab734f4ca --- /dev/null +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/RequestServiceInfoLoggerUtil.java @@ -0,0 +1,37 @@ +/* + + * Copyright (C) 2020-2024 Huawei Technologies Co., Ltd. All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.governance.adapters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.client.ServiceInstance; + +import com.huaweicloud.common.context.InvocationContext; +import com.huaweicloud.governance.GovernanceConst; + +public class RequestServiceInfoLoggerUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(RequestServiceInfoLoggerUtil.class); + + public static void logServiceInfo(InvocationContext context, Throwable e) { + if (context != null && context.getLocalContext(GovernanceConst.CONTEXT_CURRENT_INSTANCE) != null) { + ServiceInstance instance = context.getLocalContext(GovernanceConst.CONTEXT_CURRENT_INSTANCE); + LOGGER.error("request >>>>>>>>>>>>>> service {}[{}:{}] failed", instance.getServiceId(), instance.getHost(), + instance.getPort(), e); + } + } +} diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/ServiceInfoLoggerExchangeFilterFunction.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/ServiceInfoLoggerExchangeFilterFunction.java new file mode 100644 index 000000000..30b987c59 --- /dev/null +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/ServiceInfoLoggerExchangeFilterFunction.java @@ -0,0 +1,57 @@ +/* + + * Copyright (C) 2020-2024 Huawei Technologies Co., Ltd. All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.huaweicloud.governance.adapters.webclient; + +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.core.Ordered; +import org.springframework.web.reactive.function.client.ClientRequest; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.ExchangeFunction; + +import com.huaweicloud.governance.adapters.loadbalancer.RetryContext; + +import reactor.core.publisher.Mono; + +public class ServiceInfoLoggerExchangeFilterFunction implements ExchangeFilterFunction, Ordered { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceInfoLoggerExchangeFilterFunction.class); + + @Override + public Mono filter(ClientRequest request, ExchangeFunction next) { + return next.exchange(request).map(response -> logServiceInfo(response, request)); + } + + private ClientResponse logServiceInfo(ClientResponse response, ClientRequest request) { + if (response.statusCode().value() != 200) { + Optional invocationContext = request.attribute(RetryContext.RETRY_SERVICE_INSTANCE); + if (invocationContext.isPresent() && invocationContext.get() instanceof ServiceInstance instance) { + LOGGER.error("request >>>>>>>>>>>>>> service {}[{}:{}] failed", instance.getServiceId(), instance.getHost(), + instance.getPort()); + } + } + return response; + } + + @Override + public int getOrder() { + return Integer.MAX_VALUE; + } +} diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/WebClientConfiguration.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/WebClientConfiguration.java index b655b3c24..f8d017013 100644 --- a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/WebClientConfiguration.java +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webclient/WebClientConfiguration.java @@ -70,4 +70,11 @@ public ExchangeFilterFunction faultInjectionExchangeFilterFunction(FaultInjectio public StatusCodeExtractor clientResponseStatusCodeExtractor(Environment environment) { return new ClientResponseStatusCodeExtractor(environment); } + + @Bean + @ConditionalOnProperty(value = GovernanceProperties.WEBCLIENT_REQUEST_LOGGER_ENABLED, + havingValue = "true", matchIfMissing = true) + public ServiceInfoLoggerExchangeFilterFunction serviceInfoLoggerExchangeFilterFunction() { + return new ServiceInfoLoggerExchangeFilterFunction(); + } } diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/RequestServiceInfoLoggerWebFilter.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/RequestServiceInfoLoggerWebFilter.java new file mode 100644 index 000000000..1f26b372b --- /dev/null +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/RequestServiceInfoLoggerWebFilter.java @@ -0,0 +1,45 @@ +/* + + * Copyright (C) 2020-2024 Huawei Technologies Co., Ltd. All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.huaweicloud.governance.adapters.webflux; + +import org.springframework.boot.web.reactive.filter.OrderedWebFilter; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilterChain; + +import com.huaweicloud.common.configration.dynamic.GovernanceProperties; +import com.huaweicloud.common.context.InvocationContext; +import com.huaweicloud.common.context.InvocationContextHolder; +import com.huaweicloud.governance.adapters.RequestServiceInfoLoggerUtil; + +import reactor.core.publisher.Mono; + +public class RequestServiceInfoLoggerWebFilter implements OrderedWebFilter { + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + return chain.filter(exchange).onErrorResume(Exception.class, (t) -> { + InvocationContext context = exchange.getAttribute(InvocationContextHolder.ATTRIBUTE_KEY); + RequestServiceInfoLoggerUtil.logServiceInfo(context, t); + return Mono.error(t); + }); + } + + @Override + public int getOrder() { + return GovernanceProperties.WEB_FILTER_REQUEST_LOGGER_ORDER; + } +} diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/WebFluxConfiguration.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/WebFluxConfiguration.java index 472a393a1..6da05c7d8 100644 --- a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/WebFluxConfiguration.java +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webflux/WebFluxConfiguration.java @@ -73,4 +73,11 @@ public WebFilter identifierRateLimitingWebFilter(IdentifierRateLimitingHandler i public WebFilter contextMapperWebFilter(@Qualifier("contextMapperHandler") MapperHandler mapperHandler) { return new ContextMapperWebFilter(mapperHandler); } + + @Bean + @ConditionalOnProperty(value = GovernanceProperties.WEBFLUX_REQUEST_LOGGER_ENABLED, + havingValue = "true", matchIfMissing = true) + public WebFilter requestServiceInfoLoggerWebFilter() { + return new RequestServiceInfoLoggerWebFilter(); + } } diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/IdentifierRateLimitingFilter.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/IdentifierRateLimitingFilter.java index 9deff44dd..d0045437e 100644 --- a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/IdentifierRateLimitingFilter.java +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/IdentifierRateLimitingFilter.java @@ -71,8 +71,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha if (e instanceof RequestNotPermitted) { ((HttpServletResponse) response).setStatus(429); response.getWriter().print("rate limited."); - LOGGER.warn("the request is rate limit by policy : {}", - e.getMessage()); + LOGGER.warn("the request is rate limit by policy : {}", e.getMessage()); } else { throw e; } diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/RequestServiceInfoLoggerFilter.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/RequestServiceInfoLoggerFilter.java new file mode 100644 index 000000000..1b93043cd --- /dev/null +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/RequestServiceInfoLoggerFilter.java @@ -0,0 +1,45 @@ +/* + + * Copyright (C) 2020-2024 Huawei Technologies Co., Ltd. All rights reserved. + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.huaweicloud.governance.adapters.webmvc; + +import java.io.IOException; + +import com.huaweicloud.common.context.InvocationContextHolder; +import com.huaweicloud.governance.adapters.RequestServiceInfoLoggerUtil; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; + +public class RequestServiceInfoLoggerFilter implements Filter { + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + try { + if (WebMvcUtils.isNotHttpServlet(request, response)) { + chain.doFilter(request, response); + return; + } + chain.doFilter(request, response); + } catch (Throwable e) { + RequestServiceInfoLoggerUtil.logServiceInfo(InvocationContextHolder.getOrCreateInvocationContext(), e); + throw e; + } + } +} diff --git a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/WebMvcConfiguration.java b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/WebMvcConfiguration.java index f89861b61..97da60902 100644 --- a/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/WebMvcConfiguration.java +++ b/spring-cloud-huawei-governance/src/main/java/com/huaweicloud/governance/adapters/webmvc/WebMvcConfiguration.java @@ -54,6 +54,18 @@ public FilterRegistrationBean rateLimitingFilter( return registrationBean; } + @Bean + @ConditionalOnProperty(value = GovernanceProperties.WEBMVC_REQUEST_LOGGER_ENABLED, + havingValue = "true", matchIfMissing = true) + public FilterRegistrationBean requestServiceInfoLoggerFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new RequestServiceInfoLoggerFilter()); + registrationBean.addUrlPatterns("/*"); + registrationBean.setOrder(GovernanceProperties.WEB_FILTER_REQUEST_LOGGER_ORDER); + + return registrationBean; + } + @Bean @ConditionalOnProperty(value = GovernanceProperties.WEBMVC_IDENTIFIER_RATE_LIMITING_ENABLED, havingValue = "true", matchIfMissing = true)