From 9022abbc493d445dd9d8181a6fdb01f87924795d Mon Sep 17 00:00:00 2001 From: hanbingleixue Date: Tue, 29 Oct 2024 17:00:08 +0800 Subject: [PATCH] Add metric collection function in router plugin Signed-off-by: hanbingleixue --- .../sermant-router/config/config.yaml | 2 + .../dubbo-router-plugin/pom.xml | 2 +- .../AlibabaDubboMonitorFilterDeclarer.java | 48 ++++++++ .../ApacheDubboMonitorFilterDeclarer.java | 48 ++++++++ .../handler/LaneContextFilterHandler.java | 8 ++ .../AlibabaDubboMonitorFilterInterceptor.java | 54 +++++++++ .../ApacheDubboMonitorFilterInterceptor.java | 54 +++++++++ ....core.plugin.agent.declarer.PluginDeclarer | 2 + .../router/common/config/RouterConfig.java | 14 +++ .../common/constants/RouterConstant.java | 41 +++++++ .../router/common/metric/MetricInfo.java | 81 +++++++++++++ .../common/metric/MetricThreadLocal.java | 58 +++++++++ .../router/common/metric/MetricsManager.java | 112 ++++++++++++++++++ .../router/common/xds/XdsRouterHandler.java | 5 + .../config/strategy/AbstractRuleStrategy.java | 40 +++++++ .../spring/handler/AbstractHandler.java | 25 ++++ .../spring/handler/LaneMappingHandler.java | 1 + .../spring/handler/LaneRequestTagHandler.java | 1 + .../AbstractMetricInterceptor.java | 58 +++++++++ .../ClientHttpRequestInterceptor.java | 15 ++- .../interceptor/FeignClientInterceptor.java | 25 +++- .../HttpAsyncClient4xInterceptor.java | 22 +++- .../interceptor/HttpClient4xInterceptor.java | 16 ++- .../HttpUrlConnectionConnectInterceptor.java | 12 +- .../spring/interceptor/MarkInterceptor.java | 2 +- .../interceptor/OkHttp3ClientInterceptor.java | 16 ++- ...HttpClientInterceptorChainInterceptor.java | 16 ++- .../interceptor/RestTemplateInterceptor.java | 9 ++ 28 files changed, 769 insertions(+), 18 deletions(-) create mode 100644 sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/AlibabaDubboMonitorFilterDeclarer.java create mode 100644 sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/ApacheDubboMonitorFilterDeclarer.java create mode 100644 sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/AlibabaDubboMonitorFilterInterceptor.java create mode 100644 sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/ApacheDubboMonitorFilterInterceptor.java create mode 100644 sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricInfo.java create mode 100644 sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricThreadLocal.java create mode 100644 sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricsManager.java create mode 100644 sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/AbstractMetricInterceptor.java diff --git a/sermant-plugins/sermant-router/config/config.yaml b/sermant-plugins/sermant-router/config/config.yaml index 22d028fdf3..f3114ac1d9 100644 --- a/sermant-plugins/sermant-router/config/config.yaml +++ b/sermant-plugins/sermant-router/config/config.yaml @@ -19,6 +19,8 @@ router.plugin: enabled-spring-zone-router: false # compatibility router config 1.0, default is false not support enabled-previous-rule: false + # Whether to Enable Metrics Collection + enable-metric: false transmit.plugin: # Whether to transmit the label on the direct new thread enabled-thread: true diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/pom.xml b/sermant-plugins/sermant-router/dubbo-router-plugin/pom.xml index 8958a4c162..4ec3d65051 100644 --- a/sermant-plugins/sermant-router/dubbo-router-plugin/pom.xml +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/pom.xml @@ -36,7 +36,7 @@ org.apache.dubbo dubbo-config-api ${dubbo.version} - test + provided com.alibaba diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/AlibabaDubboMonitorFilterDeclarer.java b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/AlibabaDubboMonitorFilterDeclarer.java new file mode 100644 index 0000000000..3e755a09fb --- /dev/null +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/AlibabaDubboMonitorFilterDeclarer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.dubbo.declarer; + +import io.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer; +import io.sermant.core.plugin.agent.declarer.InterceptDeclarer; +import io.sermant.core.plugin.agent.matcher.ClassMatcher; +import io.sermant.core.plugin.agent.matcher.MethodMatcher; +import io.sermant.router.dubbo.interceptor.AlibabaDubboMonitorFilterInterceptor; + +/** + * Enhanced Declarer of the MonitorFilter + * + * @author zhp + * @since 2024-10-23 + */ +public class AlibabaDubboMonitorFilterDeclarer extends AbstractPluginDeclarer { + private static final String ENHANCE_CLASS = "com.alibaba.dubbo.monitor.support.MonitorFilter"; + + private static final String METHOD_NAME = "invoke"; + + @Override + public ClassMatcher getClassMatcher() { + return ClassMatcher.nameEquals(ENHANCE_CLASS); + } + + @Override + public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) { + return new InterceptDeclarer[]{ + InterceptDeclarer.build(MethodMatcher.nameEquals(METHOD_NAME), + new AlibabaDubboMonitorFilterInterceptor()) + }; + } +} diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/ApacheDubboMonitorFilterDeclarer.java b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/ApacheDubboMonitorFilterDeclarer.java new file mode 100644 index 0000000000..097043e27e --- /dev/null +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/declarer/ApacheDubboMonitorFilterDeclarer.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.dubbo.declarer; + +import io.sermant.core.plugin.agent.declarer.AbstractPluginDeclarer; +import io.sermant.core.plugin.agent.declarer.InterceptDeclarer; +import io.sermant.core.plugin.agent.matcher.ClassMatcher; +import io.sermant.core.plugin.agent.matcher.MethodMatcher; +import io.sermant.router.dubbo.interceptor.ApacheDubboMonitorFilterInterceptor; + +/** + * Enhanced Declarer of the MonitorFilter + * + * @author zhp + * @since 2024-10-23 + */ +public class ApacheDubboMonitorFilterDeclarer extends AbstractPluginDeclarer { + private static final String ENHANCE_CLASS = "org.apache.dubbo.monitor.support.MonitorFilter"; + + private static final String METHOD_NAME = "invoke"; + + @Override + public ClassMatcher getClassMatcher() { + return ClassMatcher.nameEquals(ENHANCE_CLASS); + } + + @Override + public InterceptDeclarer[] getInterceptDeclarers(ClassLoader classLoader) { + return new InterceptDeclarer[]{ + InterceptDeclarer.build(MethodMatcher.nameEquals(METHOD_NAME), + new ApacheDubboMonitorFilterInterceptor()) + }; + } +} diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/handler/LaneContextFilterHandler.java b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/handler/LaneContextFilterHandler.java index aac6091383..7e9c91d300 100644 --- a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/handler/LaneContextFilterHandler.java +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/handler/LaneContextFilterHandler.java @@ -19,6 +19,7 @@ import io.sermant.core.common.LoggerFactory; import io.sermant.core.plugin.service.PluginServiceManager; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricsManager; import io.sermant.router.common.utils.CollectionUtils; import io.sermant.router.common.utils.DubboReflectUtils; import io.sermant.router.dubbo.service.LaneContextFilterService; @@ -77,6 +78,13 @@ public Map> getRequestTag(Object invoker, Object invocation if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Lane is " + requestTag); } + laneTag.forEach((key, values) -> { + if (CollectionUtils.isEmpty(values)) { + return; + } + MetricsManager.addOrUpdateMetricValue(RouterConstant.LANE_COUNT, RouterConstant.LANE_TAG_NAME, + key + ":" + values.get(0), 1); + }); return requestTag; } diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/AlibabaDubboMonitorFilterInterceptor.java b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/AlibabaDubboMonitorFilterInterceptor.java new file mode 100644 index 0000000000..0b85680ada --- /dev/null +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/AlibabaDubboMonitorFilterInterceptor.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.dubbo.interceptor; + +import com.alibaba.dubbo.common.URL; +import com.alibaba.dubbo.rpc.Invoker; + +import io.sermant.core.plugin.agent.entity.ExecuteContext; +import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricsManager; + +/** + * Interceptor of the MonitorFilter, Collecting Metric Information + * + * @author zhp + * @since 2024-10-23 + */ +public class AlibabaDubboMonitorFilterInterceptor extends AbstractInterceptor { + @Override + public ExecuteContext before(ExecuteContext context) throws Exception { + return context; + } + + @Override + public ExecuteContext after(ExecuteContext context) throws Exception { + Object[] arguments = context.getArguments(); + if (arguments == null || arguments.length == 0) { + return context; + } + if (arguments[0] instanceof Invoker) { + Invoker invoker = (Invoker) arguments[0]; + URL url = invoker.getUrl(); + String address = url.getHost() + RouterConstant.URL_CONNECTOR + url.getPort(); + MetricsManager.addOrUpdateMetricValue(RouterConstant.REQUEST_COUNT, RouterConstant.REQUEST_COUNT_TAG_NAME, + address, 1); + } + return context; + } +} diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/ApacheDubboMonitorFilterInterceptor.java b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/ApacheDubboMonitorFilterInterceptor.java new file mode 100644 index 0000000000..e0a585222a --- /dev/null +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/java/io/sermant/router/dubbo/interceptor/ApacheDubboMonitorFilterInterceptor.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.dubbo.interceptor; + +import io.sermant.core.plugin.agent.entity.ExecuteContext; +import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricsManager; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.rpc.Invoker; + +/** + * Interceptor of the MonitorFilter, Collecting Metric Information + * + * @author zhp + * @since 2024-10-23 + */ +public class ApacheDubboMonitorFilterInterceptor extends AbstractInterceptor { + @Override + public ExecuteContext before(ExecuteContext context) throws Exception { + return context; + } + + @Override + public ExecuteContext after(ExecuteContext context) throws Exception { + Object[] arguments = context.getArguments(); + if (arguments == null || arguments.length == 0) { + return context; + } + if (arguments[0] instanceof Invoker) { + Invoker invoker = (Invoker) arguments[0]; + URL url = invoker.getUrl(); + String address = url.getHost() + RouterConstant.URL_CONNECTOR + url.getPort(); + MetricsManager.addOrUpdateMetricValue(RouterConstant.ROUTE_COUNT_TAG_NAME, RouterConstant.REQUEST_COUNT, + address, 1); + } + return context; + } +} diff --git a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer index 9ad32b1758..da5a65269e 100644 --- a/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer +++ b/sermant-plugins/sermant-router/dubbo-router-plugin/src/main/resources/META-INF/services/io.sermant.core.plugin.agent.declarer.PluginDeclarer @@ -8,3 +8,5 @@ io.sermant.router.dubbo.declarer.ClusterUtilsDeclarer io.sermant.router.dubbo.declarer.ContextFilterDeclarer io.sermant.router.dubbo.declarer.SpringApplicationDeclarer io.sermant.router.dubbo.declarer.AbstractConfigDeclarer +io.sermant.router.dubbo.declarer.ApacheDubboMonitorFilterDeclarer +io.sermant.router.dubbo.declarer.AlibabaDubboMonitorFilterDeclarer diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/config/RouterConfig.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/config/RouterConfig.java index c03e45ad84..7074430330 100644 --- a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/config/RouterConfig.java +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/config/RouterConfig.java @@ -107,6 +107,12 @@ public class RouterConfig implements PluginConfig { @ConfigFieldKey("enabled-previous-rule") private boolean enabledPreviousRule = false; + /** + * Whether to Enable Metrics Collection + */ + @ConfigFieldKey("enable-metric") + private boolean enableMetric = false; + /** * Constructor */ @@ -225,4 +231,12 @@ public boolean isEnabledSpringCloudXdsRouteSecure() { public void setEnabledSpringCloudXdsRouteSecure(boolean enabledSpringCloudXdsRouteSecure) { this.enabledSpringCloudXdsRouteSecure = enabledSpringCloudXdsRouteSecure; } + + public boolean isEnableMetric() { + return enableMetric; + } + + public void setEnableMetric(boolean enableMetric) { + this.enableMetric = enableMetric; + } } diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/constants/RouterConstant.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/constants/RouterConstant.java index f180b2e8ce..536824f1ea 100644 --- a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/constants/RouterConstant.java +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/constants/RouterConstant.java @@ -193,6 +193,47 @@ public class RouterConstant { */ public static final String ESCAPED_POINT = "\\."; + /** + * Metric Name for Request Count + */ + public static final String REQUEST_COUNT = "requestCount"; + + /** + * Metric Name for Route Count + */ + public static final String ROUTE_COUNT = "routeCount"; + + /** + * Metric Name for lane Count + */ + public static final String LANE_COUNT = "laneCount"; + + /** + * Tag Name for Request Count + */ + public static final String REQUEST_COUNT_TAG_NAME = "address"; + + /** + * Tag Name for Route Count + */ + public static final String ROUTE_COUNT_TAG_NAME = "routeTag"; + + /** + * Tag Name for match Strategy + */ + public static final String MATCH_STRATEGY_TAG_NAME = "matchType"; + + /** + * Tag Name for lane Count + */ + public static final String LANE_TAG_NAME = "laneTag"; + + + /** + * URL Connector + */ + public static final String URL_CONNECTOR = ":"; + private RouterConstant() { } } diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricInfo.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricInfo.java new file mode 100644 index 0000000000..fc8c8a048d --- /dev/null +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricInfo.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.common.metric; + +import io.sermant.core.utils.StringUtils; + +import java.util.Map; +import java.util.Objects; + +/** + * Routing Metrics information + * + * @author zhp + * @since 2024-10-16 + */ +public class MetricInfo { + /** + * Metric Name + */ + private String metricName; + + /** + * Tag Information + */ + private Map tags; + + @Override + public int hashCode() { + return Objects.hash(metricName, tags); + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || getClass() != object.getClass()) { + return false; + } + MetricInfo metricInfo = (MetricInfo) object; + if (!StringUtils.equals(this.metricName, metricInfo.getMetricName())) { + return false; + } + return Objects.equals(this.tags, metricInfo.getTags()); + } + + public String getMetricName() { + return metricName; + } + + public void setMetricName(String metricName) { + this.metricName = metricName; + } + + public Map getTags() { + return tags; + } + + public void setTags(Map tags) { + this.tags = tags; + } + + public MetricInfo(String metricName, Map tags) { + this.metricName = metricName; + this.tags = tags; + } +} diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricThreadLocal.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricThreadLocal.java new file mode 100644 index 0000000000..804d201e48 --- /dev/null +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricThreadLocal.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.common.metric; + +/** + * Metrics Thread Local + * + * @author zhp + * @since 2024-10-16 + */ +public class MetricThreadLocal { + private static final ThreadLocal FLAG = new ThreadLocal<>(); + + /** + * Constructor + */ + private MetricThreadLocal() { + } + + /** + * Set flag information + * + * @param flag execute flag + */ + public static void setFlag(Boolean flag) { + FLAG.set(flag); + } + + /** + * Get flag information + * + * @return flag information + */ + public static boolean getFlag() { + return FLAG.get() == Boolean.TRUE; + } + + /** + * remove flag information + */ + public static void removeFlag() { + FLAG.remove(); + } +} diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricsManager.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricsManager.java new file mode 100644 index 0000000000..eb0e6a6eaa --- /dev/null +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/metric/MetricsManager.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.common.metric; + +import io.sermant.core.common.LoggerFactory; +import io.sermant.core.plugin.config.PluginConfigManager; +import io.sermant.core.service.ServiceManager; +import io.sermant.core.service.metric.api.Counter; +import io.sermant.core.service.metric.api.MetricService; +import io.sermant.core.service.metric.api.Summary; +import io.sermant.core.service.metric.api.Tags; +import io.sermant.core.utils.StringUtils; +import io.sermant.router.common.config.RouterConfig; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Routing Metrics Management Class + * + * @author zhp + * @since 2024-10-16 + */ +public class MetricsManager { + private static final Logger LOGGER = LoggerFactory.getLogger(); + + private static final Map COUNT_MAP = new ConcurrentHashMap<>(); + + private static final Map SUMMARY_MAP = new ConcurrentHashMap<>(); + + private static final RouterConfig ROUTER_CONFIG = PluginConfigManager.getPluginConfig(RouterConfig.class); + + private static MetricService metricService = null; + + static { + try { + metricService = ServiceManager.getService(MetricService.class); + } catch (IllegalArgumentException e) { + LOGGER.log(Level.SEVERE, "Failed to load metrics service", e); + } + } + + /** + * Constructor + */ + private MetricsManager() { + } + + /** + * Add or update the value of the metric. + * + * @param metricName metric Name + * @param tagName tag Name + * @param tagValue tag Value + * @param value count add value + */ + public static void addOrUpdateMetricValue(String metricName, String tagName, String tagValue, double value) { + if (metricService == null || !ROUTER_CONFIG.isEnableMetric()) { + return; + } + Map tags = new HashMap<>(); + if (!StringUtils.isEmpty(tagName) && !StringUtils.isEmpty(tagValue)) { + tags.put(tagName, tagValue); + } + Counter counter = COUNT_MAP.computeIfAbsent(new MetricInfo(metricName, tags), + metricInfo -> metricService.counter(metricName, Tags.of(tags))); + counter.increment(value); + } + + /** + * Add or update the value of the metric. + * + * @param metricName metric Name + * @param tags tag information + * @param value count add value + * @param metricClass the type 0f the class + */ + public static void addOrUpdateMetricValue(String metricName, Map tags, double value, + Class metricClass) { + if (metricService == null || !ROUTER_CONFIG.isEnableMetric()) { + return; + } + if (metricClass == Summary.class) { + Summary summary = SUMMARY_MAP.computeIfAbsent(new MetricInfo(metricName, tags), + metricInfo -> metricService.summary(metricName, Tags.of(tags))); + summary.record(value); + return; + } + if (metricClass == Counter.class) { + Counter counter = COUNT_MAP.computeIfAbsent(new MetricInfo(metricName, tags), + metricInfo -> metricService.counter(metricName, Tags.of(tags))); + counter.increment(value); + } + } +} diff --git a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/xds/XdsRouterHandler.java b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/xds/XdsRouterHandler.java index eb5dbdffe4..f7c1e22c8d 100644 --- a/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/xds/XdsRouterHandler.java +++ b/sermant-plugins/sermant-router/router-common/src/main/java/io/sermant/router/common/xds/XdsRouterHandler.java @@ -18,6 +18,7 @@ import io.sermant.core.common.LoggerFactory; import io.sermant.core.service.ServiceManager; +import io.sermant.core.service.metric.api.Counter; import io.sermant.core.service.xds.XdsCoreService; import io.sermant.core.service.xds.XdsRouteService; import io.sermant.core.service.xds.XdsServiceDiscovery; @@ -33,6 +34,8 @@ import io.sermant.core.service.xds.entity.XdsRouteMatch; import io.sermant.core.utils.CollectionUtils; import io.sermant.core.utils.StringUtils; +import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricsManager; import io.sermant.router.common.utils.XdsRouterUtils; import java.util.Collections; @@ -148,6 +151,8 @@ private Set handleXdsRoute(XdsRoute route, String serviceName) if (routeAction.isWeighted()) { cluster = selectClusterByWeight(routeAction.getWeightedClusters()); } + MetricsManager.addOrUpdateMetricValue(RouterConstant.ROUTE_COUNT, RouterConstant.ROUTE_COUNT_TAG_NAME, + "cluster: " + cluster, 1); // get service instance of cluster Optional loadAssigmentOptional = diff --git a/sermant-plugins/sermant-router/router-config-common/src/main/java/io/sermant/router/config/strategy/AbstractRuleStrategy.java b/sermant-plugins/sermant-router/router-config-common/src/main/java/io/sermant/router/config/strategy/AbstractRuleStrategy.java index 596c561857..3f2dc3e7ef 100644 --- a/sermant-plugins/sermant-router/router-config-common/src/main/java/io/sermant/router/config/strategy/AbstractRuleStrategy.java +++ b/sermant-plugins/sermant-router/router-config-common/src/main/java/io/sermant/router/config/strategy/AbstractRuleStrategy.java @@ -19,7 +19,10 @@ import com.alibaba.fastjson.JSONObject; import io.sermant.core.common.LoggerFactory; +import io.sermant.core.service.metric.api.Counter; +import io.sermant.router.common.constants.RouterConstant; import io.sermant.router.common.event.PolicyEvent; +import io.sermant.router.common.metric.MetricsManager; import io.sermant.router.common.utils.CollectionUtils; import io.sermant.router.config.entity.Match; import io.sermant.router.config.entity.Policy; @@ -30,6 +33,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -182,6 +186,7 @@ public List getMismatchInstances(String serviceName, List instances, List< private List getInstances(InstanceStrategy instanceStrategy, T tags, String serviceName, List instances, boolean isReturnAllInstancesWhenMismatch) { + countRouteNum(tags, instanceStrategy); List resultList = new ArrayList<>(); for (I instance : instances) { if (instanceStrategy.isMatch(instance, tags, mapper)) { @@ -210,4 +215,39 @@ private InstanceStrategy getStrategy(boolean isMatch) { return isMatch ? (InstanceStrategy) matchInstanceStrategy : (InstanceStrategy) mismatchInstanceStrategy; } + + /** + * count Request + * + * @param tagsObject Tag information of routing rules + * @param strategy This is the label routing policy used to determine whether the instance matches + */ + private void countRouteNum(Object tagsObject, InstanceStrategy strategy) { + String strategyType = strategy == matchInstanceStrategy ? "match" : "mismatch"; + if (tagsObject instanceof Map) { + ((Map) tagsObject).forEach((key, value) -> { + Map tagsMap = new HashMap<>(); + tagsMap.put(RouterConstant.MATCH_STRATEGY_TAG_NAME, strategyType); + tagsMap.put(RouterConstant.ROUTE_COUNT_TAG_NAME, key + ":" + value); + MetricsManager.addOrUpdateMetricValue(RouterConstant.ROUTE_COUNT, tagsMap, 1, Counter.class); + }); + return; + } + if (!(tagsObject instanceof List)) { + return; + } + List> tagsList = (List>) tagsObject; + if (CollectionUtils.isEmpty(tagsList)) { + MetricsManager.addOrUpdateMetricValue(RouterConstant.ROUTE_COUNT, null, 1, Counter.class); + return; + } + tagsList.forEach(valueMap -> { + valueMap.forEach((key, value) -> { + Map tagsMap = new HashMap<>(); + tagsMap.put(RouterConstant.MATCH_STRATEGY_TAG_NAME, strategyType); + tagsMap.put(RouterConstant.ROUTE_COUNT_TAG_NAME, key + ":" + value); + MetricsManager.addOrUpdateMetricValue(RouterConstant.ROUTE_COUNT, tagsMap, 1, Counter.class); + }); + }); + } } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/AbstractHandler.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/AbstractHandler.java index 99d564dc84..a489434704 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/AbstractHandler.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/AbstractHandler.java @@ -16,11 +16,14 @@ package io.sermant.router.spring.handler; +import io.sermant.router.common.constants.RouterConstant; import io.sermant.router.common.handler.Handler; +import io.sermant.router.common.metric.MetricsManager; import io.sermant.router.common.utils.CollectionUtils; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -51,4 +54,26 @@ protected Map> getRequestTag(Map> head } return map; } + + /** + * Collect Lane Count Metric. + * + * @param laneTag lane tag + */ + protected void collectLaneCountMetric(Map> laneTag) { + Set tagValues = new HashSet<>(); + laneTag.forEach((key, values) -> { + if (CollectionUtils.isEmpty(values)) { + return; + } + values.forEach(tagValue -> { + if (tagValues.contains(tagValue)) { + return; + } + tagValues.add(tagValue); + MetricsManager.addOrUpdateMetricValue(RouterConstant.LANE_COUNT, RouterConstant.LANE_TAG_NAME, + key + ":" + tagValue, 1); + }); + }); + } } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneMappingHandler.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneMappingHandler.java index 1a4366bb7b..d5e6da72e7 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneMappingHandler.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneMappingHandler.java @@ -75,6 +75,7 @@ public Map> getRequestTag(String path, String methodName, M LOGGER.fine("Lane is empty."); return tags; } + collectLaneCountMetric(laneTag); // If there is a marker in the upstream transmission that is the same as the one in this staining, // the upstream transmission shall prevail diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneRequestTagHandler.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneRequestTagHandler.java index 23f0176278..f18cb0a65b 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneRequestTagHandler.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/handler/LaneRequestTagHandler.java @@ -81,6 +81,7 @@ public Map> getRequestTag(String path, String methodName, M if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine("Lane is " + tags); } + collectLaneCountMetric(laneTag); return tags; } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/AbstractMetricInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/AbstractMetricInterceptor.java new file mode 100644 index 0000000000..53f9c2ae11 --- /dev/null +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/AbstractMetricInterceptor.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024-2024 Sermant Authors. 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 io.sermant.router.spring.interceptor; + +import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; +import io.sermant.core.plugin.config.PluginConfigManager; +import io.sermant.router.common.config.RouterConfig; +import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricsManager; + +import java.net.URI; +import java.net.URL; + +/** + * Abstract Metric Interceptor + * + * @author zhp + * @since 2024-10-16 + */ +public abstract class AbstractMetricInterceptor extends AbstractInterceptor { + protected static final RouterConfig ROUTER_CONFIG = PluginConfigManager.getPluginConfig(RouterConfig.class); + + /** + * Collect Request Metrics Information + * + * @param uri uri + */ + protected void collectRequestMetricsInfo(URI uri) { + String address = uri.getHost() + RouterConstant.URL_CONNECTOR + uri.getPort(); + MetricsManager.addOrUpdateMetricValue(RouterConstant.REQUEST_COUNT, RouterConstant.REQUEST_COUNT_TAG_NAME, + address, 1); + } + + /** + * Collect Request Metrics Information + * + * @param url url + */ + protected void collectRequestMetricsInfo(URL url) { + String address = url.getHost() + RouterConstant.URL_CONNECTOR + url.getPort(); + MetricsManager.addOrUpdateMetricValue(RouterConstant.REQUEST_COUNT, RouterConstant.REQUEST_COUNT_TAG_NAME, + address, 1); + } +} diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/ClientHttpRequestInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/ClientHttpRequestInterceptor.java index f0edb56a3e..19d4a608da 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/ClientHttpRequestInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/ClientHttpRequestInterceptor.java @@ -17,8 +17,8 @@ package io.sermant.router.spring.interceptor; import io.sermant.core.plugin.agent.entity.ExecuteContext; -import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; import io.sermant.core.utils.LogUtils; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.request.RequestTag; import io.sermant.router.common.utils.ThreadLocalUtils; @@ -36,10 +36,13 @@ * @author provenceee * @since 2022-07-12 */ -public class ClientHttpRequestInterceptor extends AbstractInterceptor { +public class ClientHttpRequestInterceptor extends AbstractMetricInterceptor { @Override public ExecuteContext before(ExecuteContext context) { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } Object obj = context.getObject(); if (obj instanceof HttpRequest && ThreadLocalUtils.getRequestData() == null) { HttpRequest request = (HttpRequest) obj; @@ -53,6 +56,14 @@ public ExecuteContext before(ExecuteContext context) { @Override public ExecuteContext after(ExecuteContext context) { + // To prevent execution twice, after mounting and registering the plugin, this interception point is + // executed twice for a single request + Object obj = context.getObject(); + if (MetricThreadLocal.getFlag() && obj instanceof HttpRequest) { + HttpRequest request = (HttpRequest) obj; + collectRequestMetricsInfo(request.getURI()); + MetricThreadLocal.removeFlag(); + } ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); return context; diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/FeignClientInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/FeignClientInterceptor.java index 179f14b56c..bb3b3b5921 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/FeignClientInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/FeignClientInterceptor.java @@ -20,12 +20,13 @@ import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault; import feign.Request; +import io.sermant.core.common.LoggerFactory; import io.sermant.core.plugin.agent.entity.ExecuteContext; -import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; import io.sermant.core.plugin.config.PluginConfigManager; import io.sermant.core.utils.LogUtils; import io.sermant.core.utils.StringUtils; import io.sermant.router.common.config.TransmitConfig; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.request.RequestTag; import io.sermant.router.common.utils.CollectionUtils; @@ -33,6 +34,8 @@ import io.sermant.router.common.utils.ReflectUtils; import io.sermant.router.common.utils.ThreadLocalUtils; +import java.net.MalformedURLException; +import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -41,6 +44,8 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; /** * The client enhancement class, initiates the feign request method @@ -48,7 +53,9 @@ * @author provenceee * @since 2022-07-12 */ -public class FeignClientInterceptor extends AbstractInterceptor { +public class FeignClientInterceptor extends AbstractMetricInterceptor { + private static final Logger LOGGER = LoggerFactory.getLogger(); + private static final int EXPECT_LENGTH = 4; private final boolean canLoadHystrix; @@ -63,6 +70,9 @@ public FeignClientInterceptor() { @Override public ExecuteContext before(ExecuteContext context) { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } Object argument = context.getArguments()[0]; if (argument instanceof Request) { Request request = (Request) argument; @@ -76,6 +86,17 @@ public ExecuteContext before(ExecuteContext context) { @Override public ExecuteContext after(ExecuteContext context) { + Object argument = context.getArguments()[0]; + if (argument instanceof Request && MetricThreadLocal.getFlag()) { + Request request = (Request) argument; + try { + URL url = new URL(request.url()); + collectRequestMetricsInfo(url); + MetricThreadLocal.removeFlag(); + } catch (MalformedURLException e) { + LOGGER.log(Level.SEVERE, "Failed to create URL object based on connection information", e); + } + } ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); return context; diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpAsyncClient4xInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpAsyncClient4xInterceptor.java index 2a23590146..75f495ae82 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpAsyncClient4xInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpAsyncClient4xInterceptor.java @@ -24,6 +24,7 @@ import io.sermant.core.utils.StringUtils; import io.sermant.router.common.config.RouterConfig; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.utils.FlowContextUtils; import io.sermant.router.common.utils.ThreadLocalUtils; @@ -57,9 +58,9 @@ public class HttpAsyncClient4xInterceptor extends MarkInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(); - private static final int HTTPCONTEXT_INDEX = 2; + private static final int HTTP_CONTEXT_INDEX = 2; - private RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); + private final RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); /** * Pre trigger point @@ -71,6 +72,9 @@ public class HttpAsyncClient4xInterceptor extends MarkInterceptor { @Override public ExecuteContext doBefore(ExecuteContext context) throws Exception { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } Object httpAsyncRequestProducerArgument = context.getArguments()[0]; if (!(httpAsyncRequestProducerArgument instanceof HttpAsyncRequestProducer)) { return context; @@ -79,7 +83,7 @@ public ExecuteContext doBefore(ExecuteContext context) throws Exception { = (HttpAsyncRequestProducer) httpAsyncRequestProducerArgument; HttpRequest httpRequest = httpAsyncRequestProducer.generateRequest(); handleXdsRouterAndUpdateHttpRequest(httpRequest, context); - Object argument = context.getArguments()[HTTPCONTEXT_INDEX]; + Object argument = context.getArguments()[HTTP_CONTEXT_INDEX]; if (!(argument instanceof HttpContext)) { return context; } @@ -95,7 +99,7 @@ private void parseTags(HttpContext httpContext, HttpRequest httpRequest) { Object attribute = httpContext.getAttribute(FlowContextUtils.getTagName()); if (attribute != null) { Map> map = FlowContextUtils.decodeTags(String.valueOf(attribute)); - if (map != null && map.size() > 0) { + if (map != null && !map.isEmpty()) { ThreadLocalUtils.setRequestData(new RequestData( map, httpRequest.getRequestLine().getUri(), httpRequest.getRequestLine().getMethod())); } @@ -115,6 +119,16 @@ private void parseTags(HttpContext httpContext, HttpRequest httpRequest) { @Override public ExecuteContext after(ExecuteContext context) throws Exception { LogUtils.printHttpRequestAfterPoint(context); + Object httpAsyncRequestProducerArgument = context.getArguments()[0]; + if (!(httpAsyncRequestProducerArgument instanceof HttpAsyncRequestProducer) || !MetricThreadLocal.getFlag()) { + return context; + } + HttpAsyncRequestProducer httpAsyncRequestProducer + = (HttpAsyncRequestProducer) httpAsyncRequestProducerArgument; + HttpRequest httpRequest = httpAsyncRequestProducer.generateRequest(); + URI uri = URI.create(httpRequest.getRequestLine().getUri()); + collectRequestMetricsInfo(uri); + MetricThreadLocal.removeFlag(); return context; } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpClient4xInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpClient4xInterceptor.java index 9fd717d474..30e78aef2d 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpClient4xInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpClient4xInterceptor.java @@ -24,6 +24,7 @@ import io.sermant.core.utils.StringUtils; import io.sermant.router.common.config.RouterConfig; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.utils.CollectionUtils; import io.sermant.router.common.utils.FlowContextUtils; @@ -52,18 +53,20 @@ public class HttpClient4xInterceptor extends MarkInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(); - private RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); + private final RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); /** * Pre-trigger point * * @param context Execution context * @return Execution context - * @throws Exception Execution exception */ @Override - public ExecuteContext doBefore(ExecuteContext context) throws Exception { + public ExecuteContext doBefore(ExecuteContext context) { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } Object[] arguments = context.getArguments(); Object httpRequestObject = arguments[1]; @@ -104,6 +107,13 @@ public ExecuteContext doBefore(ExecuteContext context) throws Exception { */ @Override public ExecuteContext after(ExecuteContext context) throws Exception { + Object[] arguments = context.getArguments(); + Object httpRequestObject = arguments[1]; + if (httpRequestObject instanceof HttpRequestBase && MetricThreadLocal.getFlag()) { + final HttpRequestBase httpRequest = (HttpRequestBase) httpRequestObject; + collectRequestMetricsInfo(httpRequest.getURI()); + MetricThreadLocal.removeFlag(); + } ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); return context; diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java index 6cd7decb9f..89660ea140 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/HttpUrlConnectionConnectInterceptor.java @@ -18,7 +18,6 @@ import io.sermant.core.common.LoggerFactory; import io.sermant.core.plugin.agent.entity.ExecuteContext; -import io.sermant.core.plugin.agent.interceptor.AbstractInterceptor; import io.sermant.core.plugin.config.PluginConfigManager; import io.sermant.core.service.xds.entity.ServiceInstance; import io.sermant.core.utils.LogUtils; @@ -26,6 +25,7 @@ import io.sermant.core.utils.StringUtils; import io.sermant.router.common.config.RouterConfig; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.utils.CollectionUtils; import io.sermant.router.common.utils.FlowContextUtils; @@ -47,7 +47,7 @@ * @author yuzl Yu Zhenlong * @since 2022-10-25 */ -public class HttpUrlConnectionConnectInterceptor extends AbstractInterceptor { +public class HttpUrlConnectionConnectInterceptor extends AbstractMetricInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(); private RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); @@ -55,6 +55,9 @@ public class HttpUrlConnectionConnectInterceptor extends AbstractInterceptor { @Override public ExecuteContext before(ExecuteContext context) { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } if (!(context.getObject() instanceof HttpURLConnection)) { return context; } @@ -89,6 +92,11 @@ private String getPath(HttpURLConnection connection) { @Override public ExecuteContext after(ExecuteContext context) { + if (MetricThreadLocal.getFlag()) { + HttpURLConnection connection = (HttpURLConnection) context.getObject(); + collectRequestMetricsInfo(connection.getURL()); + MetricThreadLocal.removeFlag(); + } ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); return context; diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/MarkInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/MarkInterceptor.java index 2501194d5b..6ddf074a3b 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/MarkInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/MarkInterceptor.java @@ -25,7 +25,7 @@ * @author yangrh * @since 2022-11-04 */ -public abstract class MarkInterceptor implements Interceptor { +public abstract class MarkInterceptor extends AbstractMetricInterceptor implements Interceptor { /** * Shared by multiple interceptors */ diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java index c85ae717e0..eaeff635f0 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttp3ClientInterceptor.java @@ -24,6 +24,7 @@ import io.sermant.core.utils.StringUtils; import io.sermant.router.common.config.RouterConfig; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.utils.FlowContextUtils; import io.sermant.router.common.utils.ThreadLocalUtils; @@ -47,7 +48,7 @@ public class OkHttp3ClientInterceptor extends MarkInterceptor { private static final String FIELD_NAME = "originalRequest"; - private RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); + private final RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); /** * Pre-trigger point @@ -59,6 +60,9 @@ public class OkHttp3ClientInterceptor extends MarkInterceptor { @Override public ExecuteContext doBefore(ExecuteContext context) throws Exception { LogUtils.printHttpRequestBeforePoint(context); + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } final Optional rawRequest = getRequest(context); if (!rawRequest.isPresent()) { return context; @@ -91,6 +95,16 @@ public ExecuteContext doBefore(ExecuteContext context) throws Exception { public ExecuteContext after(ExecuteContext context) throws Exception { ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); + if (!MetricThreadLocal.getFlag()) { + return context; + } + final Optional rawRequest = getRequest(context); + if (rawRequest.isPresent()) { + Request request = rawRequest.get(); + URI uri = request.url().uri(); + collectRequestMetricsInfo(uri); + MetricThreadLocal.removeFlag(); + } return context; } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttpClientInterceptorChainInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttpClientInterceptorChainInterceptor.java index b095e28f51..dc205c9781 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttpClientInterceptorChainInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/OkHttpClientInterceptorChainInterceptor.java @@ -21,11 +21,11 @@ import io.sermant.core.common.LoggerFactory; import io.sermant.core.plugin.agent.entity.ExecuteContext; -import io.sermant.core.plugin.agent.interceptor.Interceptor; import io.sermant.core.plugin.config.PluginConfigManager; import io.sermant.core.service.xds.entity.ServiceInstance; import io.sermant.router.common.config.RouterConfig; import io.sermant.router.common.constants.RouterConstant; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.spring.utils.BaseHttpRouterUtils; import java.io.IOException; @@ -43,7 +43,7 @@ * @author daizhenyu * @since 2024-09-06 */ -public class OkHttpClientInterceptorChainInterceptor implements Interceptor { +public class OkHttpClientInterceptorChainInterceptor extends AbstractMetricInterceptor { private static final Logger LOGGER = LoggerFactory.getLogger(); private RouterConfig routerConfig = PluginConfigManager.getPluginConfig(RouterConfig.class); @@ -58,6 +58,12 @@ public class OkHttpClientInterceptorChainInterceptor implements Interceptor { @Override public ExecuteContext before(ExecuteContext context) throws Exception { Object[] arguments = context.getArguments(); + if (!(arguments[0] instanceof Request)) { + return context; + } + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } handleXdsRouterAndUpdateHttpRequest(arguments); return context; } @@ -71,6 +77,12 @@ public ExecuteContext before(ExecuteContext context) throws Exception { */ @Override public ExecuteContext after(ExecuteContext context) throws Exception { + Object[] arguments = context.getArguments(); + if (arguments[0] instanceof Request && MetricThreadLocal.getFlag()) { + Request request = (Request) arguments[0]; + collectRequestMetricsInfo(request.uri()); + MetricThreadLocal.removeFlag(); + } return context; } diff --git a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/RestTemplateInterceptor.java b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/RestTemplateInterceptor.java index 921a5aa932..c294b88856 100644 --- a/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/RestTemplateInterceptor.java +++ b/sermant-plugins/sermant-router/spring-router-plugin/src/main/java/io/sermant/router/spring/interceptor/RestTemplateInterceptor.java @@ -19,6 +19,7 @@ import io.sermant.core.plugin.agent.entity.ExecuteContext; import io.sermant.core.utils.LogUtils; import io.sermant.core.utils.StringUtils; +import io.sermant.router.common.metric.MetricThreadLocal; import io.sermant.router.common.request.RequestData; import io.sermant.router.common.utils.FlowContextUtils; import io.sermant.router.common.utils.ThreadLocalUtils; @@ -53,6 +54,9 @@ public ExecuteContext doBefore(ExecuteContext context) { parseTags(callback, arguments[0], arguments[1]); } } + if (ROUTER_CONFIG.isEnableMetric()) { + MetricThreadLocal.setFlag(true); + } return context; } @@ -87,6 +91,11 @@ private RequestData getRequestData(Map> tags, Object url, O public ExecuteContext after(ExecuteContext context) throws Exception { ThreadLocalUtils.removeRequestData(); LogUtils.printHttpRequestAfterPoint(context); + Object[] arguments = context.getArguments(); + if (arguments[0] instanceof URI && MetricThreadLocal.getFlag()) { + collectRequestMetricsInfo((URI) arguments[0]); + MetricThreadLocal.removeFlag(); + } return context; }