From 447c64fffb72fdb82e2f3c670e891ae1d00a8ec8 Mon Sep 17 00:00:00 2001 From: Randall Hauch Date: Mon, 5 Feb 2018 12:13:26 -0800 Subject: [PATCH] KAFKA-5987: Maintain order of metric tags in generated documentation The `MetricNameTemplate` is changed to used a `LinkedHashSet` to maintain the same order of the tags that are passed in. This tag order is then maintained when `Metrics.toHtmlTable` generates the MBean names for each of the metrics. The `SenderMetricsRegistry` and `FetcherMetricsRegistry` both contain templates used in the producer and consumer, respectively, and these were changed to use a `LinkedHashSet` to maintain the order of the tags. Before this change, the generated HTML documentation might use MBean names like the following and order them: ``` kafka.connect:type=sink-task-metrics,connector={connector},partition={partition},task={task},topic={topic} kafka.connect:type=sink-task-metrics,connector={connector},task={task} ``` However, after this change, the documentation would use the following order: ``` kafka.connect:type=sink-task-metrics,connector={connector},task={task} kafka.connect:type=sink-task-metrics,connector={connector},task={task},topic={topic},partition={partition} ``` This is more readable as the code that is creating the templates has control over the order of the tags. Note that JMX MBean names use ObjectName that does not maintain order of the properties (tags), so this change should have no impact on the actual JMX MBean names used in the metrics. cc wushujames Author: Randall Hauch Reviewers: James Cheng , Ewen Cheslack-Postava Closes #3985 from rhauch/kafka-5987 --- .../internals/FetcherMetricsRegistry.java | 3 +- .../internals/SenderMetricsRegistry.java | 8 +-- .../kafka/common/MetricNameTemplate.java | 58 +++++++++++++++---- .../apache/kafka/common/metrics/Metrics.java | 14 ++++- 4 files changed, 66 insertions(+), 17 deletions(-) diff --git a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetcherMetricsRegistry.java b/clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetcherMetricsRegistry.java index f15ac062abf..301363a638b 100644 --- a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetcherMetricsRegistry.java +++ b/clients/src/main/java/org/apache/kafka/clients/consumer/internals/FetcherMetricsRegistry.java @@ -18,6 +18,7 @@ package org.apache.kafka.clients.consumer.internals; import java.util.Arrays; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @@ -102,7 +103,7 @@ public class FetcherMetricsRegistry { "The maximum throttle time in ms", tags); /***** Topic level *****/ - Set topicTags = new HashSet<>(tags); + Set topicTags = new LinkedHashSet<>(tags); topicTags.add("topic"); this.topicFetchSizeAvg = new MetricNameTemplate("fetch-size-avg", groupName, diff --git a/clients/src/main/java/org/apache/kafka/clients/producer/internals/SenderMetricsRegistry.java b/clients/src/main/java/org/apache/kafka/clients/producer/internals/SenderMetricsRegistry.java index 21dbca61830..b01236f6218 100644 --- a/clients/src/main/java/org/apache/kafka/clients/producer/internals/SenderMetricsRegistry.java +++ b/clients/src/main/java/org/apache/kafka/clients/producer/internals/SenderMetricsRegistry.java @@ -17,7 +17,7 @@ package org.apache.kafka.clients.producer.internals; import java.util.ArrayList; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -70,12 +70,12 @@ public class SenderMetricsRegistry { private final Metrics metrics; private final Set tags; - private final HashSet topicTags; + private final LinkedHashSet topicTags; public SenderMetricsRegistry(Metrics metrics) { this.metrics = metrics; this.tags = this.metrics.config().tags().keySet(); - this.allTemplates = new ArrayList(); + this.allTemplates = new ArrayList<>(); /***** Client level *****/ @@ -126,7 +126,7 @@ public class SenderMetricsRegistry { "The maximum time in ms a request was throttled by a broker"); /***** Topic level *****/ - this.topicTags = new HashSet(tags); + this.topicTags = new LinkedHashSet<>(tags); this.topicTags.add("topic"); // We can't create the MetricName up front for these, because we don't know the topic name yet. diff --git a/clients/src/main/java/org/apache/kafka/common/MetricNameTemplate.java b/clients/src/main/java/org/apache/kafka/common/MetricNameTemplate.java index e3ea9950ef1..1b1de71037d 100644 --- a/clients/src/main/java/org/apache/kafka/common/MetricNameTemplate.java +++ b/clients/src/main/java/org/apache/kafka/common/MetricNameTemplate.java @@ -16,7 +16,7 @@ */ package org.apache.kafka.common; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; @@ -26,27 +26,45 @@ import org.apache.kafka.common.utils.Utils; * A template for a MetricName. It contains a name, group, and description, as * well as all the tags that will be used to create the mBean name. Tag values * are omitted from the template, but are filled in at runtime with their - * specified values. + * specified values. The order of the tags is maintained, if an ordered set + * is provided, so that the mBean names can be compared and sorted lexicographically. */ public class MetricNameTemplate { private final String name; private final String group; private final String description; - private Set tags; + private LinkedHashSet tags; - public MetricNameTemplate(String name, String group, String description, Set tags) { + /** + * Create a new template. Note that the order of the tags will be preserved if the supplied + * {@code tagsNames} set has an order. + * + * @param name the name of the metric; may not be null + * @param group the name of the group; may not be null + * @param description the description of the metric; may not be null + * @param tagsNames the set of metric tag names, which can/should be a set that maintains order; may not be null + */ + public MetricNameTemplate(String name, String group, String description, Set tagsNames) { this.name = Utils.notNull(name); this.group = Utils.notNull(group); this.description = Utils.notNull(description); - this.tags = Utils.notNull(tags); + this.tags = new LinkedHashSet<>(Utils.notNull(tagsNames)); } - - public MetricNameTemplate(String name, String group, String description, String... keys) { - this(name, group, description, getTags(keys)); + + /** + * Create a new template. Note that the order of the tags will be preserved. + * + * @param name the name of the metric; may not be null + * @param group the name of the group; may not be null + * @param description the description of the metric; may not be null + * @param tagsNames the names of the metric tags in the preferred order; none of the tag names should be null + */ + public MetricNameTemplate(String name, String group, String description, String... tagsNames) { + this(name, group, description, getTags(tagsNames)); } - private static Set getTags(String... keys) { - Set tags = new HashSet(); + private static LinkedHashSet getTags(String... keys) { + LinkedHashSet tags = new LinkedHashSet<>(); for (int i = 0; i < keys.length; i++) tags.add(keys[i]); @@ -54,18 +72,38 @@ public class MetricNameTemplate { return tags; } + /** + * Get the name of the metric. + * + * @return the metric name; never null + */ public String name() { return this.name; } + /** + * Get the name of the group. + * + * @return the group name; never null + */ public String group() { return this.group; } + /** + * Get the description of the metric. + * + * @return the metric description; never null + */ public String description() { return this.description; } + /** + * Get the set of tag names for the metric. + * + * @return the ordered set of tag names; never null but possibly empty + */ public Set tags() { return tags; } diff --git a/clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java b/clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java index 7f9fb9dad5a..8868ee7ac48 100644 --- a/clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java +++ b/clients/src/main/java/org/apache/kafka/common/metrics/Metrics.java @@ -232,12 +232,22 @@ public class Metrics implements Closeable { return tags; } - public static String toHtmlTable(String domain, List allMetrics) { + /** + * Use the specified domain and metric name templates to generate an HTML table documenting the metrics. A separate table section + * will be generated for each of the MBeans and the associated attributes. The MBean names are lexicographically sorted to + * determine the order of these sections. This order is therefore dependent upon the order of the + * tags in each {@link MetricNameTemplate}. + * + * @param domain the domain or prefix for the JMX MBean names; may not be null + * @param allMetrics the collection of all {@link MetricNameTemplate} instances each describing one metric; may not be null + * @return the string containing the HTML table; never null + */ + public static String toHtmlTable(String domain, Iterable allMetrics) { Map> beansAndAttributes = new TreeMap>(); try (Metrics metrics = new Metrics()) { for (MetricNameTemplate template : allMetrics) { - Map tags = new TreeMap(); + Map tags = new LinkedHashMap<>(); for (String s : template.tags()) { tags.put(s, "{" + s + "}"); }