From b3eca51c203c578cdb0cde19067c25db835b32c3 Mon Sep 17 00:00:00 2001 From: zwmagic Date: Wed, 18 Dec 2024 23:26:33 +0800 Subject: [PATCH] Add custom common metric tag key feature Signed-off-by: zwmagic --- .../config/config.properties | 2 + .../sermant/core/service/metric/api/Tags.java | 28 ++++ .../service/metric/config/MetricConfig.java | 43 ++++++ .../metric/entity/MetricCommonTagEnum.java | 142 ++++++++++++++++++ .../implement/service/metric/MeterMetric.java | 31 +++- .../common/constants/RouterConstant.java | 5 - .../router/common/metric/MetricsManager.java | 4 +- 7 files changed, 242 insertions(+), 13 deletions(-) create mode 100644 sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/entity/MetricCommonTagEnum.java diff --git a/sermant-agentcore/sermant-agentcore-config/config/config.properties b/sermant-agentcore/sermant-agentcore-config/config/config.properties index 72375ead96..ef68db98bd 100644 --- a/sermant-agentcore/sermant-agentcore-config/config/config.properties +++ b/sermant-agentcore/sermant-agentcore-config/config/config.properties @@ -128,3 +128,5 @@ service.meta.zone= metric.type=prometheus # The maximum number of metrics. metric.maxTimeSeries=1000 +# Defines the common tag keys for metrics, with multiple keys separated by commas, the default values include "agent", "agent.app.name", "agent.ip" and "scope". For a complete list of available tag keys, refer to{@link io.sermant.core.service.metric.entity.MetricCommonTagEnum}. +metric.common.tag.keys=agent,agent.app.name,agent.ip,scope diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java index e58b301c96..bacc2e1197 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/api/Tags.java @@ -35,6 +35,14 @@ * @since 2024-08-16 */ public final class Tags { + /** + * Key for the scope tag + */ + public static final String SCOPE = "scope"; + /** + * Value for the core scope tag + */ + public static final String SCOPE_VALUE_CORE = "core"; private final Map tags = new HashMap<>(); private Tags() { @@ -84,6 +92,26 @@ public static Tags of(Map tags) { return result; } + /** + * Adds a core scope tag. + * + * @return Tags Returns the current Tags instance to support method chaining. + */ + public Tags addCoreScope() { + return addScope(SCOPE_VALUE_CORE); + } + + /** + * Adds a scope tag. + * + * @param scopeValue scope value + * @return Tags Returns the current Tags instance to support method chaining. + */ + public Tags addScope(String scopeValue) { + tags.put(SCOPE, StringUtils.isEmpty(scopeValue) ? "undefined" : scopeValue); + return this; + } + /** * Adds a key-value pair to the current Tags object. * diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java index 38070b2fd5..68147e3cbb 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/config/MetricConfig.java @@ -19,6 +19,13 @@ import io.sermant.core.config.common.BaseConfig; import io.sermant.core.config.common.ConfigFieldKey; import io.sermant.core.config.common.ConfigTypeKey; +import io.sermant.core.service.metric.entity.MetricCommonTagEnum; +import io.sermant.core.utils.StringUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; /** * Metric Configuration @@ -30,6 +37,8 @@ public class MetricConfig implements BaseConfig { private static final int MAXIMUM_TIME_SERIES_VALUE = 1000; + private static final String COMMA = ","; + /** * The metric type, currently supports prometheus. */ @@ -42,6 +51,15 @@ public class MetricConfig implements BaseConfig { @ConfigFieldKey("maxTimeSeries") private Integer maxTimeSeries = MAXIMUM_TIME_SERIES_VALUE; + /** + * Defines the common tag keys for metrics, with multiple keys separated by commas. + * The default values include "agent", "agent.app.name", and "agent.ip". + * For a complete list of available tag keys, refer to + * {@link io.sermant.core.service.metric.entity.MetricCommonTagEnum}. + */ + @ConfigFieldKey("common.tag.keys") + private String commonTagKeys = String.join(COMMA, MetricCommonTagEnum.getDefaultKeys()); + public String getType() { return type; } @@ -57,4 +75,29 @@ public void setMaxTimeSeries(Integer maximumTimeSeries) { public Integer getMaxTimeSeries() { return maxTimeSeries; } + + public String getCommonTagKeys() { + return commonTagKeys; + } + + public void setCommonTagKeys(String commonTagKeys) { + this.commonTagKeys = commonTagKeys; + } + + /** + * Gets the list of custom default tags. + *

+ * If the custom default tags string is blank or null, an empty list is returned to avoid null pointer exceptions. + * The custom default tags string is split by commas and converted into a list. + * + * @return A set of custom default tags, or an empty set if not set or blank. + */ + public Set getCommonTagKeySet() { + if (StringUtils.isBlank(commonTagKeys)) { + return Collections.emptySet(); + } + String[] array = commonTagKeys.split(COMMA); + return new HashSet<>(Arrays.asList(array)); + } + } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/entity/MetricCommonTagEnum.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/entity/MetricCommonTagEnum.java new file mode 100644 index 0000000000..c60df09b59 --- /dev/null +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/metric/entity/MetricCommonTagEnum.java @@ -0,0 +1,142 @@ +/* + * 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.core.service.metric.entity; + +import io.sermant.core.common.BootArgsIndexer; +import io.sermant.core.utils.NetworkUtils; +import io.sermant.core.utils.StringUtils; + +import java.util.Arrays; +import java.util.Set; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * Enum class to define and manage metric tags with their keys and value supply strategies. + * + * @author zwmagic + * @since 2024-12-18 + */ +public enum MetricCommonTagEnum { + + /** + * Define an AGENT tag with key "agent" and a constant value "sermant". + */ + AGENT("agent", true, () -> "sermant"), + /** + * Define an AGENT_APP_NAME tag with key "agent.app.name" and a value supplied by appName. + */ + AGENT_APP_NAME("agent.app.name", true, BootArgsIndexer::getAppName), + /** + * Define an AGENT_IP tag with key "agent.ip" and a value supplied by machine ip. + */ + AGENT_IP("agent.ip", true, NetworkUtils::getMachineIp), + /** + * Define an SCOPE tag with key "scope" and a value supplied by core or plugin. + */ + SCOPE("scope", true, () -> "undefined"), + /** + * Define an AGENT_SERVICE_NAME tag with key "agent.service.name" and a value supplied by serviceName + */ + AGENT_SERVICE_NAME("agent.service.name", false, BootArgsIndexer::getServiceName), + /** + * Define an AGENT_APP_TYPE tag with key "agent.app.type" and a value supplied by appType + */ + AGENT_APP_TYPE("agent.app.type", false, BootArgsIndexer::getAppType), + /** + * Define an AGENT_ARTIFACT tag with key "agent.artifact" and a value supplied by artifact + */ + AGENT_ARTIFACT("agent.artifact", false, BootArgsIndexer::getArtifact), + /** + * Define an AGENT_VERSION tag with key "agent.version" and a value supplied by agent version + */ + AGENT_VERSION("agent.version", false, BootArgsIndexer::getCoreVersion),; + + private final String key; + private final boolean defaultEnable; + private final Supplier valueSupplier; + + /** + * Constructor to initialize MetricTagKeyEnum enum members. + * + * @param key The key of the tag + * @param defaultEnable default enable for the tag + * @param valueSupplier The strategy to provide the tag's value + */ + MetricCommonTagEnum(String key, boolean defaultEnable, Supplier valueSupplier) { + this.key = key; + this.defaultEnable = defaultEnable; + this.valueSupplier = valueSupplier; + } + + /** + * Get the key of the tag. + * + * @return The key of the tag + */ + public String getKey() { + return key; + } + + /** + * Check if the tag is default enabled. + * + * @return true if the tag is default enabled, false otherwise + */ + public boolean isDefaultEnable() { + return defaultEnable; + } + + /** + * Get the strategy to provide the tag's value. + * + * @return The strategy to provide the tag's value + */ + public Supplier getValueSupplier() { + return valueSupplier; + } + + /** + * Get a set of all default-enabled tag keys. + * + * @return A set containing all default-enabled tag keys + */ + public static Set getDefaultKeys() { + return Arrays.stream(MetricCommonTagEnum.values()).filter(MetricCommonTagEnum::isDefaultEnable) + .map(MetricCommonTagEnum::getKey).collect(Collectors.toSet()); + } + + /** + * Get the value of the tag corresponding to the given key. + * If the provided key is empty or does not correspond to any tag, return an empty string. + * + * @param key The key of the tag + * @return The value of the tag, or an empty string if no matching tag exists + */ + public static String of(String key) { + if (StringUtils.isEmpty(key)) { + return StringUtils.EMPTY; + } + for (MetricCommonTagEnum tagKeyEnum : MetricCommonTagEnum.values()) { + if (tagKeyEnum.getKey().equals(key)) { + return tagKeyEnum.getValueSupplier().get(); + } + } + return StringUtils.EMPTY; + } + +} diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java index 90ca783e13..6d4cbbb279 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/service/metric/MeterMetric.java @@ -23,7 +23,6 @@ import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.config.MeterFilter; -import io.sermant.core.common.BootArgsIndexer; import io.sermant.core.service.metric.api.Counter; import io.sermant.core.service.metric.api.DistributionStatisticConfig; import io.sermant.core.service.metric.api.Gauge; @@ -32,12 +31,16 @@ import io.sermant.core.service.metric.api.Tags; import io.sermant.core.service.metric.api.Timer; import io.sermant.core.service.metric.config.MetricConfig; +import io.sermant.core.service.metric.entity.MetricCommonTagEnum; +import io.sermant.core.utils.CollectionUtils; import io.sermant.core.utils.StringUtils; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; /** * metric @@ -59,10 +62,26 @@ public class MeterMetric implements Metric { public MeterMetric(MeterRegistryProvider registryProvider, MetricConfig metricConfig) { this.registry = registryProvider.getRegistry(); MeterRegistry.Config config = this.registry.config(); - config.commonTags( - "agent", "sermant", - "agent.app.name", BootArgsIndexer.getAppName() - ); + Set commonTagKeys = metricConfig.getCommonTagKeySet(); + if (CollectionUtils.isEmpty(commonTagKeys)) { + config.meterFilter(MeterFilter.maximumAllowableMetrics(metricConfig.getMaxTimeSeries())); + return; + } + + List tags = new ArrayList<>(); + for (String tagKey : commonTagKeys) { + if (StringUtils.isEmpty(tagKey)) { + continue; + } + String tagValue = MetricCommonTagEnum.of(tagKey); + if (StringUtils.isEmpty(tagValue)) { + continue; + } + tags.add(io.micrometer.core.instrument.Tag.of(tagKey, tagValue)); + } + if (!CollectionUtils.isEmpty(tags)) { + config.commonTags(tags); + } config.meterFilter(MeterFilter.maximumAllowableMetrics(metricConfig.getMaxTimeSeries())); } @@ -89,7 +108,7 @@ public Timer timer(String metricName, Tags tags, String description) { @Override public Summary summary(String metricName, Tags tags, String description, - DistributionStatisticConfig distributionStatisticConfig) { + DistributionStatisticConfig distributionStatisticConfig) { Builder summaryBuilder = DistributionSummary.builder(metricName) .tags(getMeterTags(tags)) .description(description); 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 b5e3b56243..c353779322 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 @@ -233,11 +233,6 @@ public class RouterConstant { */ public static final String LANE_TAG = "lane_tag"; - /** - * Scope for metrics - */ - public static final String SCOPE = "scope"; - /** * the name for the service.meta.parameters tag */ 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 index 1a0029b076..ea1062c28f 100644 --- 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 @@ -92,9 +92,9 @@ public static void addOrUpdateCounterMetricValue(String metricName, Map(); } - tagsMap.put(RouterConstant.SCOPE, "service-router"); Counter counter = COUNT_MAP.computeIfAbsent(new MetricInfo(metricName, tagsMap), - metricInfo -> metricService.counter(metricName, Tags.of(tagsMap))); + metricInfo -> metricService.counter(metricName, + Tags.of(tagsMap).addScope("service-router"))); counter.increment(value); }