Skip to content

Commit

Permalink
Merge pull request #1708 from zwmagic/zwmagic/common_tags
Browse files Browse the repository at this point in the history
Add custom common metric tag feature
  • Loading branch information
Sherlockhan authored Dec 25, 2024
2 parents ae572fd + b3eca51 commit b669b3b
Show file tree
Hide file tree
Showing 7 changed files with 242 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> tags = new HashMap<>();

private Tags() {
Expand Down Expand Up @@ -84,6 +92,26 @@ public static Tags of(Map<String, String> 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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
*/
Expand All @@ -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;
}
Expand All @@ -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.
* <p>
* 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<String> getCommonTagKeySet() {
if (StringUtils.isBlank(commonTagKeys)) {
return Collections.emptySet();
}
String[] array = commonTagKeys.split(COMMA);
return new HashSet<>(Arrays.asList(array));
}

}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> 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<String> getValueSupplier() {
return valueSupplier;
}

/**
* Get a set of all default-enabled tag keys.
*
* @return A set containing all default-enabled tag keys
*/
public static Set<String> 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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<String> commonTagKeys = metricConfig.getCommonTagKeySet();
if (CollectionUtils.isEmpty(commonTagKeys)) {
config.meterFilter(MeterFilter.maximumAllowableMetrics(metricConfig.getMaxTimeSeries()));
return;
}

List<io.micrometer.core.instrument.Tag> 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()));
}

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,9 @@ public static void addOrUpdateCounterMetricValue(String metricName, Map<String,
} else {
tagsMap = new HashMap<>();
}
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);
}

Expand Down

0 comments on commit b669b3b

Please sign in to comment.