Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5232433
KAFKA-9960: implement KIP-606 to add metadata context to MetricsReporter
xiaodongdu May 13, 2020
82c0a0d
add changes to connect as part of KIP 606 implementation
xiaodongdu May 13, 2020
5d388cf
fix string constant
xiaodongdu May 13, 2020
92d6389
Changes to address comments
xiaodongdu May 13, 2020
e893850
Fix a issue when setting group id
xiaodongdu May 14, 2020
2b36bc6
use group.id from original config when pass connect.group.id to client
xiaodongdu May 14, 2020
24bc78e
remove redundant call, add more unit test
xiaodongdu May 14, 2020
65195e1
More changes to address code review comments
xiaodongdu May 15, 2020
9511de8
More changes to address code review comments
xiaodongdu May 15, 2020
4964aef
More changes to address code review coments
xiaodongdu May 16, 2020
8fd4ef5
More changes to address code review coments
xiaodongdu May 16, 2020
69e4b7c
add an integration test, strip off metrics.context from metrics context
xiaodongdu May 18, 2020
0e1a5b6
More changes to address PR review
xiaodongdu May 18, 2020
386a5da
Remove support of reading metric context properties for broker
xiaodongdu May 18, 2020
6005c9a
KAFKA-9960: implement KIP-606 to add metadata context to MetricsReporter
xiaodongdu May 13, 2020
d78a1e0
Remove cluster id from a few constructors and use utility method to g…
xiaodongdu May 20, 2020
2484d71
Merge branch 'kafka-9960-kip-606' of github.com:xiaodongdu/kafka into…
xiaodongdu May 20, 2020
85f7421
remove clusterId from WorkGroupMember constructor
xiaodongdu May 20, 2020
3e7cc54
Merge remote-tracking branch 'origin/trunk' into kafka-9960-kip-606
xiaodongdu May 22, 2020
f30c8f0
move string constants to config class
xiaodongdu May 23, 2020
bd58373
revert changes for KafkaConfig
xiaodongdu May 23, 2020
26a2a76
Merge remote-tracking branch 'origin/trunk' into kafka-9960-kip-606
xiaodongdu May 24, 2020
a409085
Address code review comments: rename method name, remove unused varia…
xiaodongdu May 24, 2020
d8c5d2a
update comment
xiaodongdu May 24, 2020
1a1cf89
Update MetricsContext based on KIP changes, address more code review …
xiaodongdu May 26, 2020
15bdb8d
Update MetricsContext based on KIP changes, address more code review …
xiaodongdu May 26, 2020
7f5c69a
More fix regarding code review
xiaodongdu May 26, 2020
5598fa1
Merge branch 'kafka-9960-kip-606' of github.com:xiaodongdu/kafka into…
xiaodongdu May 26, 2020
98371e4
Minor changes for KafkaServer
xiaodongdu May 26, 2020
1c42644
update variable name
xiaodongdu May 26, 2020
6f93472
Remove unused variable
xiaodongdu May 26, 2020
071872f
Update javadoc and add annotation
xiaodongdu May 27, 2020
7bcd057
Update javadoc
xiaodongdu May 27, 2020
319f5c5
Merge remote-tracking branch 'origin/trunk' into kafka-9960-kip-606
xiaodongdu May 27, 2020
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
2 changes: 1 addition & 1 deletion checkstyle/suppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<suppress checks="ClassFanOutComplexity"
files="DistributedHerder(|Test).java"/>
<suppress checks="ClassFanOutComplexity"
files="Worker.java"/>
files="Worker(|Test).java"/>
<suppress checks="MethodLength"
files="(KafkaConfigBackingStore|RequestResponseTest|WorkerSinkTaskTest).java"/>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public class CommonClientConfigs {
public static final String METRIC_REPORTER_CLASSES_CONFIG = "metric.reporters";
public static final String METRIC_REPORTER_CLASSES_DOC = "A list of classes to use as metrics reporters. Implementing the <code>org.apache.kafka.common.metrics.MetricsReporter</code> interface allows plugging in classes that will be notified of new metric creation. The JmxReporter is always included to register JMX statistics.";

public static final String METRICS_CONTEXT_PREFIX = "metrics.context.";

public static final String SECURITY_PROTOCOL_CONFIG = "security.protocol";
public static final String SECURITY_PROTOCOL_DOC = "Protocol used to communicate with brokers. Valid values are: " +
Utils.join(SecurityProtocol.names(), ", ") + ".";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.apache.kafka.clients.ClientRequest;
import org.apache.kafka.clients.ClientResponse;
import org.apache.kafka.clients.ClientUtils;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.KafkaClient;
import org.apache.kafka.clients.NetworkClient;
import org.apache.kafka.clients.StaleMetadataException;
Expand Down Expand Up @@ -124,8 +125,10 @@
import org.apache.kafka.common.message.OffsetDeleteRequestData.OffsetDeleteRequestTopicCollection;
import org.apache.kafka.common.message.RenewDelegationTokenRequestData;
import org.apache.kafka.common.metrics.JmxReporter;
import org.apache.kafka.common.metrics.KafkaMetricsContext;
import org.apache.kafka.common.metrics.MetricConfig;
import org.apache.kafka.common.metrics.Metrics;
import org.apache.kafka.common.metrics.MetricsContext;
import org.apache.kafka.common.metrics.MetricsReporter;
import org.apache.kafka.common.metrics.Sensor;
import org.apache.kafka.common.network.ChannelBuilder;
Expand Down Expand Up @@ -462,10 +465,12 @@ static KafkaAdminClient createInternal(AdminClientConfig config, TimeoutProcesso
.timeWindow(config.getLong(AdminClientConfig.METRICS_SAMPLE_WINDOW_MS_CONFIG), TimeUnit.MILLISECONDS)
.recordLevel(Sensor.RecordingLevel.forName(config.getString(AdminClientConfig.METRICS_RECORDING_LEVEL_CONFIG)))
.tags(metricTags);
JmxReporter jmxReporter = new JmxReporter(JMX_PREFIX);
JmxReporter jmxReporter = new JmxReporter();
jmxReporter.configure(config.originals());
reporters.add(jmxReporter);
metrics = new Metrics(metricConfig, reporters, time);
MetricsContext metricsContext = new KafkaMetricsContext(JMX_PREFIX,
config.originalsWithPrefix(CommonClientConfigs.METRICS_CONTEXT_PREFIX));
metrics = new Metrics(metricConfig, reporters, time, metricsContext);
String metricGrpPrefix = "admin-client";
channelBuilder = ClientUtils.createChannelBuilder(config, time, logContext);
selector = new Selector(config.getLong(AdminClientConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,10 @@
import org.apache.kafka.common.errors.TimeoutException;
import org.apache.kafka.common.internals.ClusterResourceListeners;
import org.apache.kafka.common.metrics.JmxReporter;
import org.apache.kafka.common.metrics.KafkaMetricsContext;
import org.apache.kafka.common.metrics.MetricConfig;
import org.apache.kafka.common.metrics.Metrics;
import org.apache.kafka.common.metrics.MetricsContext;
import org.apache.kafka.common.metrics.MetricsReporter;
import org.apache.kafka.common.metrics.Sensor;
import org.apache.kafka.common.network.ChannelBuilder;
Expand Down Expand Up @@ -868,10 +870,12 @@ private static Metrics buildMetrics(ConsumerConfig config, Time time, String cli
.tags(metricsTags);
List<MetricsReporter> reporters = config.getConfiguredInstances(ConsumerConfig.METRIC_REPORTER_CLASSES_CONFIG,
MetricsReporter.class, Collections.singletonMap(ConsumerConfig.CLIENT_ID_CONFIG, clientId));
JmxReporter jmxReporter = new JmxReporter(JMX_PREFIX);
JmxReporter jmxReporter = new JmxReporter();
jmxReporter.configure(config.originals());
reporters.add(jmxReporter);
return new Metrics(metricConfig, reporters, time);
MetricsContext metricsContext = new KafkaMetricsContext(JMX_PREFIX,
config.originalsWithPrefix(CommonClientConfigs.METRICS_CONTEXT_PREFIX));
return new Metrics(metricConfig, reporters, time, metricsContext);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.kafka.clients.ApiVersions;
import org.apache.kafka.clients.ClientDnsLookup;
import org.apache.kafka.clients.ClientUtils;
import org.apache.kafka.clients.CommonClientConfigs;
import org.apache.kafka.clients.KafkaClient;
import org.apache.kafka.clients.NetworkClient;
import org.apache.kafka.clients.consumer.ConsumerGroupMetadata;
Expand Down Expand Up @@ -54,8 +55,10 @@
import org.apache.kafka.common.header.internals.RecordHeaders;
import org.apache.kafka.common.internals.ClusterResourceListeners;
import org.apache.kafka.common.metrics.JmxReporter;
import org.apache.kafka.common.metrics.KafkaMetricsContext;
import org.apache.kafka.common.metrics.MetricConfig;
import org.apache.kafka.common.metrics.Metrics;
import org.apache.kafka.common.metrics.MetricsContext;
import org.apache.kafka.common.metrics.MetricsReporter;
import org.apache.kafka.common.metrics.Sensor;
import org.apache.kafka.common.network.ChannelBuilder;
Expand Down Expand Up @@ -351,10 +354,12 @@ public KafkaProducer(Properties properties, Serializer<K> keySerializer, Seriali
List<MetricsReporter> reporters = config.getConfiguredInstances(ProducerConfig.METRIC_REPORTER_CLASSES_CONFIG,
MetricsReporter.class,
Collections.singletonMap(ProducerConfig.CLIENT_ID_CONFIG, clientId));
JmxReporter jmxReporter = new JmxReporter(JMX_PREFIX);
JmxReporter jmxReporter = new JmxReporter();
jmxReporter.configure(userProvidedConfigs);
reporters.add(jmxReporter);
this.metrics = new Metrics(metricConfig, reporters, time);
MetricsContext metricsContext = new KafkaMetricsContext(JMX_PREFIX,
config.originalsWithPrefix(CommonClientConfigs.METRICS_CONTEXT_PREFIX));
this.metrics = new Metrics(metricConfig, reporters, time, metricsContext);
this.partitioner = config.getConfiguredInstance(ProducerConfig.PARTITIONER_CLASS_CONFIG, Partitioner.class);
long retryBackoffMs = config.getLong(ProducerConfig.RETRY_BACKOFF_MS_CONFIG);
if (keySerializer == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -71,8 +72,13 @@ public JmxReporter() {

/**
* Create a JMX reporter that prefixes all metrics with the given string.
* @deprecated Since 2.6.0. Use {@link JmxReporter#JmxReporter()}
* Initialize JmxReporter with {@link JmxReporter#contextChange(MetricsContext)}
* Populate prefix by adding _namespace/prefix key value pair to {@link MetricsContext}
*/
@Deprecated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xvrl can we remove the explicit construction of JmxReporter and rely on the plugin loading mechanism after we remove this non-zero-arg constructor? Maybe something for 3.0?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I agree. We'll have to decide whether to make the config include this reporter by default or so something else. There are some backwards compatibility implications, but probably better to have a separate discussion for this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, thanks 👍

public JmxReporter(String prefix) {
Objects.requireNonNull(prefix);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we throw an exception here, or just replace the null value with an empty string?

@xiaodongdu xiaodongdu May 23, 2020

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the logic here: if null, set it to empty string.

this.prefix = prefix;
}

Expand Down Expand Up @@ -318,4 +324,15 @@ public static Predicate<String> compilePredicate(Map<String, ?> configs) {
+ ".(whitelist/blacklist) is not a valid regular expression");
}
}

@Override
public void contextChange(MetricsContext metricsContext) {
Objects.requireNonNull(metricsContext.metadata().get(MetricsContext.NAMESPACE));
Comment thread
mumrah marked this conversation as resolved.
Outdated
synchronized (LOCK) {
if (!mbeans.isEmpty()) {
throw new IllegalStateException("JMX MetricsContext can only be updated before JMX metrics are created");
}
prefix = metricsContext.metadata().get(MetricsContext.NAMESPACE);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.kafka.common.metrics;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
* A implementation of MetricsContext, it encapsulates required metrics context properties for Kafka services and clients
*/
public class KafkaMetricsContext implements MetricsContext {
/**
* Client or Service's metadata map.
*/
private Map<String, String> metadata = new HashMap<>();
Comment thread
mumrah marked this conversation as resolved.
Outdated

/**
* Create a MetricsContext with namespace, no service or client properties
* @param namespace value for _namespace key
*/
public KafkaMetricsContext(String namespace) {
this(namespace, new HashMap<>());
}

/**
* Create a MetricsContext with namespace, service or client properties
* @param namespace value for _namespace key
* @param metadata metadata additional entries to add to the context.
* values will be converted to string using Object.toString()
*/
public KafkaMetricsContext(String namespace, Map<String, ?> metadata) {
this.metadata.put(MetricsContext.NAMESPACE, namespace);
metadata.forEach((key, value) -> this.metadata.put(key, value.toString()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use putIfAbsent to avoid silently overwriting over labels set upstream.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we it's ok for the component that owns the reporter to take precedence over the labels passed from upstream. We did not specify the behavior in the KIP, so implementations should use namespacing of labels to avoid this. If in practice we find this behavior is less desirable, we can file a follow-on KIP, since the interface is till evolving.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly I'm concerned about the case where some composite may share similar labels to the underlying client it manages. If we allow the downstream client to overwrite such a label we will lose a portion of the upstream components context.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the client currently only injects the labels passed in via client properties, so that wouldn't happen


}

public Map<String, String> metadata() {
return Collections.unmodifiableMap(metadata);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,18 @@ public Metrics(MetricConfig defaultConfig, List<MetricsReporter> reporters, Time
this(defaultConfig, reporters, time, false);
}

/**
* Create a metrics repository with a default config, metric reporters and metric context
* Expiration of Sensors is disabled.
* @param defaultConfig The default config
* @param reporters The metrics reporters
* @param time The time instance to use with the metrics
* @param metricsContext The metricsContext to initialize metrics reporter with
*/
public Metrics(MetricConfig defaultConfig, List<MetricsReporter> reporters, Time time, MetricsContext metricsContext) {
this(defaultConfig, reporters, time, false, metricsContext);
}

/**
* Create a metrics repository with a default config, given metric reporters and the ability to expire eligible sensors
* @param defaultConfig The default config
Expand All @@ -129,14 +141,30 @@ public Metrics(MetricConfig defaultConfig, List<MetricsReporter> reporters, Time
* @param enableExpiration true if the metrics instance can garbage collect inactive sensors, false otherwise
*/
public Metrics(MetricConfig defaultConfig, List<MetricsReporter> reporters, Time time, boolean enableExpiration) {
this(defaultConfig, reporters, time, enableExpiration, new KafkaMetricsContext(""));
}

/**
* Create a metrics repository with a default config, given metric reporters, the ability to expire eligible sensors
* and MetricContext
* @param defaultConfig The default config
* @param reporters The metrics reporters
* @param time The time instance to use with the metrics
* @param enableExpiration true if the metrics instance can garbage collect inactive sensors, false otherwise
* @param metricsContext The metricsContext to initialize metrics reporter with
*/
public Metrics(MetricConfig defaultConfig, List<MetricsReporter> reporters, Time time, boolean enableExpiration,
MetricsContext metricsContext) {
this.config = defaultConfig;
this.sensors = new ConcurrentHashMap<>();
this.metrics = new ConcurrentHashMap<>();
this.childrenSensors = new ConcurrentHashMap<>();
this.reporters = Objects.requireNonNull(reporters);
this.time = time;
for (MetricsReporter reporter : reporters)
for (MetricsReporter reporter : reporters) {
reporter.contextChange(metricsContext);
reporter.init(new ArrayList<>());
}

// Create the ThreadPoolExecutor only if expiration of Sensors is enabled.
if (enableExpiration) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.kafka.common.metrics;

import org.apache.kafka.common.annotation.InterfaceStability;

import java.util.Map;

/**
* MetricsContext encapsulates additional metadata about metrics exposed via a
* {@link org.apache.kafka.common.metrics.MetricsReporter}
*
* The metadata map provides following information:
* - a <code>_namespace</node> field indicating the component exposing metrics
* e.g. kafka.server, kafka.consumer
* {@link JmxReporter} uses this as prefix for mbean names
*
* - for clients and streams libraries: any freeform fields passed in via
* client properties in the form of `metrics.context.<key>=<value>
*
* - for kafka brokers: kafka.broker.id, kafka.cluster.id
* - for connect workers: connect.kafka.cluster.id, connect.group.id
*/
@InterfaceStability.Evolving
public interface MetricsContext {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking through the PR, it seems that MetricsContext is a short lived object used to pass values to the MetricReporters as they are constructed. Since the usage appears to be write-once, it might be better to expose a subset of Map rather than the full thing. E.g., String get(String field) and Iterator<String> fields or something.

If we think this might evolve into a mutable long-lived object, then a Map is probably better. Just a thought.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think having an interface gives us more flexibility to evolve the API, without breaking backwards compatibility.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't suggesting that we eliminate the interface, I definitely think having one is a good choice (for the reasons you mentioned). What I meant was in this interface, we expose a Map as the collection of metrics tags/labels. But since it appears that the usage is intended to be read-only, maybe a Map isn't the best choice. Here's what I was thinking:

interface MetricsContext {
  String namespace();
  String get(String field);
  Collection<String> fields();
}

(included the namespace suggestion from my other comment as well).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping a Map interface makes it more convenient to work with. It gives you all the helper methods and streaming interfaces rather than having to hand-roll those things for someone consuming the api.

/* predefined fields */
String NAMESPACE = "_namespace"; // metrics namespace, formerly jmx prefix

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we define this as a field on the interface?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the interface is a natural way to expose predefined constants an API might need. I don't see a need to have a separate class for this yet.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I shouldn't have said "field" since that implies a concrete class. See above comment above for an example of what I meant.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we should give namespace a special status over other fields, it just happens to be the only one we currently define by default for backwards compatibility reasons. If we find ourselves adding more of those, I agreee it would be worth revisiting how we expose pre-defined fields.


/**
* Returns metadata fields
*/
Map<String, String> metadata();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata is very overloaded, can we think of a different name here?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any suggestions? maybe contextLabels?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I updated the KIP to reflect this change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated code to reflect KIP changes

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.Set;

import org.apache.kafka.common.Reconfigurable;
import org.apache.kafka.common.annotation.InterfaceStability;
import org.apache.kafka.common.config.ConfigException;

/**
Expand Down Expand Up @@ -65,4 +66,13 @@ default void validateReconfiguration(Map<String, ?> configs) throws ConfigExcept
default void reconfigure(Map<String, ?> configs) {
}

/**
* Callback method providing context metadata for the
Comment thread
mumrah marked this conversation as resolved.
Outdated
* service or library exposing metrics
*
* @param metricsContext the metric context
*/
@InterfaceStability.Evolving
default void contextChange(MetricsContext metricsContext) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

someone on the ML commented that we might want to name this contextChanged (past tense). I don't have a strong feeling either way. Do you have any thoughts @mumrah @rhauch?

@rhauch rhauch May 26, 2020

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't have a strong preference. Past tense is a little odd, but I think changeContext(...) or setContext(...) are present-tense and more conventional.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be good to identify when this is called relative to other methods. For example, it is always called before init(...) is called. But can it be called again, or is that the only time this method is called?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

due to the way jmxreporter is initialized in Kafka today, it already gets called both before and after init()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be called multiple times. Not sure if we should mention that in Javadoc, other methods in this class we are not mention if it can be called multiple times even though they can be called multiple times.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no clear call pattern, then it's fine to not say anything. However, JmxReporter.contextChange(...) seems to assume that contextChange(...) will be called before any metrics are added via init(...).

If that call pattern is true, then I think we should document it. If it's also true it can be called later, then mention this as well. For example, the JavaDoc text on contextChange(...) could be something like:

Sets the context labels for the service or library that is exposing metrics.
This will be called before {@link #init(List)} and may be called anytime after that.

WDYT?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Updated javadoc.

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,14 @@
import org.apache.kafka.test.MockConsumerInterceptor;
import org.apache.kafka.test.MockMetricsReporter;
import org.apache.kafka.test.TestUtils;
import org.apache.kafka.common.metrics.stats.Avg;

import org.junit.Assert;
import org.junit.Test;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.util.ArrayList;
Expand Down Expand Up @@ -376,6 +381,23 @@ public void testPause() {
consumer.close();
}

@Test
public void testConsumerJmxPrefix() throws Exception {
Map<String, Object> config = new HashMap<>();
config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9999");
config.put(ConsumerConfig.SEND_BUFFER_CONFIG, Selectable.USE_DEFAULT_BUFFER_SIZE);
config.put(ConsumerConfig.RECEIVE_BUFFER_CONFIG, Selectable.USE_DEFAULT_BUFFER_SIZE);
config.put("client.id", "client-1");
KafkaConsumer<byte[], byte[]> consumer = new KafkaConsumer<>(
config, new ByteArrayDeserializer(), new ByteArrayDeserializer());
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
MetricName testMetricName = consumer.metrics.metricName("test-metric",
"grp1", "test metric");
consumer.metrics.addMetric(testMetricName, new Avg());
Assert.assertNotNull(server.getObjectInstance(new ObjectName("kafka.consumer:type=grp1,client-id=client-1")));
consumer.close();
}

private KafkaConsumer<byte[], byte[]> newConsumer(String groupId) {
return newConsumer(groupId, Optional.empty());
}
Expand Down
Loading