Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom common metric tag feature #1708

Merged
merged 1 commit into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
lilai23 marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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
Loading