Add support for error code metrics (#12125) (#12661)

* Add metrics support of error code

* Add metrics support of error code

* Add metrics support of error code

* add license

* remove hashcode override

* Update DefaultSubDispatcher.java

* Update ErrorCodeSampleTest.java

* Update DefaultMetricsCollector.java

---------

Co-authored-by: songxiaosheng <songxiaosheng@elastic.link>
This commit is contained in:
namelessssssssssss 2023-08-09 17:56:47 +08:00 committed by GitHub
parent 04f3fe7d2b
commit 5ae875d951
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 455 additions and 48 deletions

View File

@ -47,6 +47,8 @@ public interface MetricsConstants {
String TAG_CHANGE_TYPE = "change.type";
String TAG_ERROR_CODE = "error";
String ENABLE_JVM_METRICS_KEY = "enable.jvm";
String AGGREGATION_COLLECTOR_KEY = "aggregation";

View File

@ -0,0 +1,32 @@
/*
* 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.dubbo.common.logger;
/**
* Loggers that can register to listen to log messages.
*/
public interface ListenableLogger extends ErrorTypeAwareLogger{
/**
* Register a listener to this loggerand get notified when a log happens.
*
* @param listener log listener
*/
void registerListen(LogListener listener);
}

View File

@ -0,0 +1,27 @@
/*
* 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.dubbo.common.logger;
/**
* Log Listener, can registered to an {@link ListenableLogger}.
*/
public interface LogListener {
void onMessage(String code, String msg);
}

View File

@ -18,16 +18,22 @@
package org.apache.dubbo.common.logger.support;
import org.apache.dubbo.common.Version;
import org.apache.dubbo.common.logger.ErrorTypeAwareLogger;
import org.apache.dubbo.common.logger.ListenableLogger;
import org.apache.dubbo.common.logger.LogListener;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.utils.NetUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
/**
* A fail-safe (ignoring exception thrown by logger) wrapper of error type aware logger.
*/
public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements ErrorTypeAwareLogger {
public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements ListenableLogger {
/**
* Template address for formatting.
@ -36,6 +42,16 @@ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements Erro
private static final Pattern ERROR_CODE_PATTERN = Pattern.compile("\\d+-\\d+");
/**
* Listeners that listened to all Loggers.
*/
private static final List<LogListener> GLOBAL_LISTENERS = Collections.synchronizedList(new ArrayList<>());
/**
* Listeners that listened to this listener.
*/
private final AtomicReference<List<LogListener>> listeners = new AtomicReference<>();
public FailsafeErrorTypeAwareLogger(Logger logger) {
super(logger);
}
@ -80,6 +96,7 @@ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements Erro
}
try {
onEvent(code, msg);
getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
} catch (Throwable t) {
// ignored.
@ -93,6 +110,7 @@ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements Erro
}
try {
onEvent(code, msg);
getLogger().warn(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
} catch (Throwable t) {
// ignored.
@ -106,6 +124,7 @@ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements Erro
}
try {
onEvent(code, msg);
getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg));
} catch (Throwable t) {
// ignored.
@ -119,9 +138,45 @@ public class FailsafeErrorTypeAwareLogger extends FailsafeLogger implements Erro
}
try {
onEvent(code, msg);
getLogger().error(appendContextMessageWithInstructions(code, cause, extendedInformation, msg), e);
} catch (Throwable t) {
// ignored.
}
}
public static void registerGlobalListen(LogListener listener) {
GLOBAL_LISTENERS.add(listener);
}
@Override
public void registerListen(LogListener listener) {
listeners.getAndUpdate(logListeners -> {
if (logListeners == null) {
logListeners = Collections.synchronizedList(new ArrayList<>());
}
logListeners.add(listener);
return logListeners;
});
}
private void onEvent(String code, String msg) {
Optional.ofNullable(listeners.get()).ifPresent(
logListeners -> logListeners.forEach(logListener -> {
try {
logListener.onMessage(code, msg);
} catch (Exception e) {
// ignored.
}
}));
GLOBAL_LISTENERS.forEach(logListener -> {
try {
logListener.onMessage(code, msg);
} catch (Exception e) {
// ignored.
}
});
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.dubbo.metrics.model;
import java.util.HashMap;
import java.util.Map;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_APPLICATION_NAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_ERROR_CODE;
/**
* ErrorCodeMetric. Provide tags for error code metrics.
*/
public class ErrorCodeMetric implements Metric {
private final String errorCode;
private final String applicationName;
public ErrorCodeMetric(String applicationName, String errorCode) {
this.errorCode = errorCode;
this.applicationName = applicationName;
}
@Override
public Map<String, String> getTags() {
Map<String, String> tags = new HashMap<>();
tags.put(TAG_ERROR_CODE, errorCode);
tags.put(TAG_APPLICATION_NAME, applicationName);
return tags;
}
public String getErrorCode() {
return errorCode;
}
}

View File

@ -29,5 +29,6 @@ public enum MetricsCategory {
REGISTRY,
METADATA,
THREAD_POOL,
ERROR_CODE,
NETTY,
}

View File

@ -85,6 +85,7 @@ public class ThreadPoolMetric implements Metric{
return Objects.hash(applicationName, threadPoolName);
}
@Override
public Map<String, String> getTags() {
Map<String, String> tags = new HashMap<>();
tags.put(TAG_IP, getLocalHost());

View File

@ -30,6 +30,7 @@ import static org.apache.dubbo.common.constants.MetricsConstants.TAG_HOSTNAME;
import static org.apache.dubbo.common.constants.MetricsConstants.TAG_IP;
public class ThreadPoolRejectMetric implements Metric{
private String applicationName;
private String threadPoolName;
@ -69,6 +70,7 @@ public class ThreadPoolRejectMetric implements Metric{
return Objects.hash(applicationName, threadPoolName);
}
@Override
public Map<String, String> getTags() {
Map<String, String> tags = new HashMap<>();
tags.put(TAG_IP, getLocalHost());

View File

@ -124,6 +124,9 @@ public enum MetricsKey {
// consumer metrics key
INVOKER_NO_AVAILABLE_COUNT("dubbo.consumer.invoker.no.available.count", "Request Throw No Invoker Available Exception Count"),
// count the number of occurrences of each error code
ERROR_CODE_COUNT("dubbo.error.code.count","The Count Of Occurrences for Each Error Code"),
// netty metrics key
NETTY_ALLOCATOR_HEAP_MEMORY_USED("netty.allocator.memory.used", "Netty Allocator Memory Used"),
NETTY_ALLOCATOR_DIRECT_MEMORY_USED("netty.allocator.direct.memory.used", "Netty Allocator Direct Memory Used"),

View File

@ -20,10 +20,11 @@ package org.apache.dubbo.metrics.collector;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.metrics.DefaultConstants;
import org.apache.dubbo.metrics.collector.sample.MetricsCountSampleConfigurer;
import org.apache.dubbo.metrics.collector.sample.ErrorCodeSampler;
import org.apache.dubbo.metrics.collector.sample.MetricsSampler;
import org.apache.dubbo.metrics.collector.sample.SimpleMetricsCountSampler;
import org.apache.dubbo.metrics.collector.sample.ThreadPoolMetricsSampler;
import org.apache.dubbo.metrics.collector.sample.MetricsCountSampleConfigurer;
import org.apache.dubbo.metrics.data.BaseStatComposite;
import org.apache.dubbo.metrics.data.MethodStatComposite;
import org.apache.dubbo.metrics.data.RtStatComposite;
@ -53,9 +54,15 @@ public class DefaultMetricsCollector extends CombMetricsCollector<RequestEvent>
private boolean collectEnabled = false;
private volatile boolean threadpoolCollectEnabled = false;
private final ThreadPoolMetricsSampler threadPoolSampler = new ThreadPoolMetricsSampler(this);
private final ErrorCodeSampler errorCodeSampler;
private String applicationName;
private final ApplicationModel applicationModel;
private final List<MetricsSampler> samplers = new ArrayList<>();
public DefaultMetricsCollector(ApplicationModel applicationModel) {
@ -74,8 +81,9 @@ public class DefaultMetricsCollector extends CombMetricsCollector<RequestEvent>
}
});
super.setEventMulticaster(new DefaultSubDispatcher(this));
samplers.add(applicationSampler);
samplers.add(threadPoolSampler);
this.samplers.add(applicationSampler);
this.samplers.add(threadPoolSampler);
this.errorCodeSampler = new ErrorCodeSampler(this);
this.applicationModel = applicationModel;
}
@ -99,6 +107,7 @@ public class DefaultMetricsCollector extends CombMetricsCollector<RequestEvent>
this.collectEnabled = collectEnabled;
}
@Override
public boolean isCollectEnabled() {
return collectEnabled;
}

View File

@ -0,0 +1,41 @@
/*
* 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.dubbo.metrics.collector.sample;
import org.apache.dubbo.common.logger.LogListener;
import org.apache.dubbo.common.logger.support.FailsafeErrorTypeAwareLogger;
import org.apache.dubbo.metrics.model.key.MetricsKey;
/**
* Listen the log of all {@link FailsafeErrorTypeAwareLogger} instances, and add error code count to {@link ErrorCodeSampler}.
*/
public class ErrorCodeMetricsListenRegister implements LogListener {
private final ErrorCodeSampler errorCodeSampler;
public ErrorCodeMetricsListenRegister(ErrorCodeSampler errorCodeSampler){
FailsafeErrorTypeAwareLogger.registerGlobalListen(this);
this.errorCodeSampler = errorCodeSampler;
this.errorCodeSampler.addMetricName(MetricsKey.ERROR_CODE_COUNT.getName());
}
@Override
public void onMessage(String code, String msg) {
errorCodeSampler.inc(code, MetricsKey.ERROR_CODE_COUNT.getName());
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.dubbo.metrics.collector.sample;
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
import org.apache.dubbo.metrics.model.ErrorCodeMetric;
import org.apache.dubbo.metrics.model.MetricsCategory;
import org.apache.dubbo.metrics.model.key.MetricsKey;
import org.apache.dubbo.metrics.model.sample.CounterMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* This sampler is used to count the number of occurrences of each error code.
*/
public class ErrorCodeSampler extends MetricsNameCountSampler<String, String, ErrorCodeMetric> {
private final ErrorCodeMetricsListenRegister register;
/**
* Map<ErrorCode,Metric>
*/
private final Map<String, ErrorCodeMetric> errorCodeMetrics;
public ErrorCodeSampler(DefaultMetricsCollector collector) {
super(collector, MetricsCategory.ERROR_CODE, MetricsKey.ERROR_CODE_COUNT);
this.register = new ErrorCodeMetricsListenRegister(this);
this.errorCodeMetrics = new ConcurrentHashMap<>();
}
@Override
protected MetricSample provideMetricsSample(ErrorCodeMetric metric, AtomicLong count, MetricsKey metricsKey, MetricsCategory metricsCategory) {
return new CounterMetricSample<>(
metricsKey.getNameByType(metric.getErrorCode()),
metricsKey.getDescription(),
metric.getTags(),
metricsCategory,
count
);
}
@Override
protected void countConfigure(MetricsCountSampleConfigurer<String, String, ErrorCodeMetric> sampleConfigure) {
sampleConfigure.configureMetrics(configure -> {
String errorCode = configure.getSource();
ErrorCodeMetric metric = errorCodeMetrics.get(errorCode);
if (metric == null) {
metric = new ErrorCodeMetric(collector.getApplicationModel().getApplicationName(), errorCode);
errorCodeMetrics.put(errorCode, metric);
}
return metric;
});
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.dubbo.metrics.collector.sample;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
import org.apache.dubbo.metrics.model.Metric;
import org.apache.dubbo.metrics.model.MetricsCategory;
import org.apache.dubbo.metrics.model.key.MetricsKey;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
public abstract class MetricsNameCountSampler<S, K, M extends Metric> extends SimpleMetricsCountSampler<S, K, M> {
protected final DefaultMetricsCollector collector;
protected final Set<K> metricNames = new ConcurrentHashSet<>();
protected final MetricsCategory metricsCategory;
protected final MetricsKey metricsKey;
public MetricsNameCountSampler(DefaultMetricsCollector collector,MetricsCategory metricsCategory,MetricsKey metricsKey) {
this.metricsCategory = metricsCategory;
this.metricsKey = metricsKey;
this.collector = collector;
this.collector.addSampler(this);
}
public void addMetricName(K name) {
this.metricNames.add(name);
}
@Override
public List<MetricSample> sample() {
List<MetricSample> metricSamples = new ArrayList<>();
metricNames.forEach(name -> collect(metricSamples, name));
return metricSamples;
}
private void collect(List<MetricSample> samples, K metricName) {
getCount(metricName)
.filter(e -> !e.isEmpty())
.ifPresent(map -> map.forEach((k, v) ->
samples.add(provideMetricsSample(k,v,metricsKey,metricsCategory))));
}
protected abstract MetricSample provideMetricsSample(M metric,AtomicLong count,MetricsKey metricsKey,MetricsCategory metricsCategory);
}

View File

@ -36,6 +36,7 @@ public abstract class SimpleMetricsCountSampler<S, K, M extends Metric>
implements MetricsCountSampler<S, K, M> {
private final ConcurrentMap<M, AtomicLong> EMPTY_COUNT = new ConcurrentHashMap<>();
private final Map<K, ConcurrentMap<M, AtomicLong>> metricCounter = new ConcurrentHashMap<>();
@Override

View File

@ -16,65 +16,33 @@
*/
package org.apache.dubbo.metrics.collector.sample;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
import org.apache.dubbo.metrics.model.Metric;
import org.apache.dubbo.metrics.model.MetricsCategory;
import org.apache.dubbo.metrics.model.key.MetricsKey;
import org.apache.dubbo.metrics.model.ThreadPoolRejectMetric;
import org.apache.dubbo.metrics.model.key.MetricsKey;
import org.apache.dubbo.metrics.model.sample.GaugeMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.ToDoubleFunction;
import static org.apache.dubbo.metrics.model.MetricsCategory.THREAD_POOL;
public class ThreadRejectMetricsCountSampler extends SimpleMetricsCountSampler<String, String, ThreadPoolRejectMetric> {
public class ThreadRejectMetricsCountSampler extends MetricsNameCountSampler<String, String, ThreadPoolRejectMetric> {
private final DefaultMetricsCollector collector;
private final Set<String> metricNames = new ConcurrentHashSet<>();
public ThreadRejectMetricsCountSampler(DefaultMetricsCollector collector) {
this.collector = collector;
this.collector.addSampler(this);
}
public void addMetricName(String name){
this.metricNames.add(name);
super(collector, THREAD_POOL,MetricsKey.THREAD_POOL_THREAD_REJECT_COUNT);
}
@Override
public List<MetricSample> sample() {
List<MetricSample> metricSamples = new ArrayList<>();
metricNames.stream().forEach(name->collect(metricSamples,name));
return metricSamples;
}
private void collect(List<MetricSample> list, String metricName) {
count(list, metricName, MetricsKey.THREAD_POOL_THREAD_REJECT_COUNT);
}
private <T extends Metric> void count(List<MetricSample> list, String metricName, MetricsKey metricsKey) {
getCount(metricName).filter(e -> !e.isEmpty())
.ifPresent(map -> map.forEach((k, v) ->
list.add(getGaugeMetricSample(metricsKey, k, THREAD_POOL, v, AtomicLong::get))));
}
private <T> GaugeMetricSample<T> getGaugeMetricSample(MetricsKey metricsKey,
ThreadPoolRejectMetric methodMetric,
MetricsCategory metricsCategory,
T value,
ToDoubleFunction<T> apply) {
protected MetricSample provideMetricsSample(ThreadPoolRejectMetric metric, AtomicLong count, MetricsKey metricsKey, MetricsCategory metricsCategory) {
return new GaugeMetricSample<>(
metricsKey.getNameByType(methodMetric.getThreadPoolName()),
metricsKey.getNameByType(metric.getThreadPoolName()),
metricsKey.getDescription(),
methodMetric.getTags(),
metric.getTags(),
metricsCategory,
value,
apply);
count,
AtomicLong::get);
}
@Override

View File

@ -96,6 +96,7 @@ public final class DefaultSubDispatcher extends SimpleMetricsEventMulticaster {
MetricsSupport.incrAndAddRt(targetKey, dynamicPlaceType, (MethodMetricsCollector) collector, event);
}
)));
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.dubbo.metrics.metrics.model.sample;
import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.common.utils.ReflectionUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.metrics.collector.DefaultMetricsCollector;
import org.apache.dubbo.metrics.collector.sample.ErrorCodeMetricsListenRegister;
import org.apache.dubbo.metrics.collector.sample.ErrorCodeSampler;
import org.apache.dubbo.metrics.model.sample.CounterMetricSample;
import org.apache.dubbo.metrics.model.sample.MetricSample;
import org.apache.dubbo.rpc.model.ApplicationModel;
import org.apache.dubbo.rpc.model.FrameworkModel;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class ErrorCodeSampleTest {
@Test
void testErrorCodeMetric() {
FrameworkModel frameworkModel = FrameworkModel.defaultModel();
ApplicationModel applicationModel = frameworkModel.newApplication();
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName("MyApplication1");
applicationModel.getApplicationConfigManager().setApplication(applicationConfig);
DefaultMetricsCollector defaultMetricsCollector = new DefaultMetricsCollector(applicationModel);
defaultMetricsCollector.setCollectEnabled(true);
ErrorCodeSampler sampler = (ErrorCodeSampler) ReflectionUtils.getField(defaultMetricsCollector, "errorCodeSampler");
ErrorCodeMetricsListenRegister register = (ErrorCodeMetricsListenRegister) ReflectionUtils.getField(sampler, "register");
register.onMessage("0-1", null);
register.onMessage("0-1", null);
register.onMessage("0-2", null);
register.onMessage("0-2", null);
register.onMessage("1-2", null);
register.onMessage("1-2", null);
register.onMessage("1-3", null);
register.onMessage("1-3", null);
List<MetricSample> samples = defaultMetricsCollector.collect();
Assert.assertTrue(samples.size() == 4,"Wrong number of samples.");
samples.forEach(metricSample -> Assert.assertTrue(((AtomicLong)((CounterMetricSample<?>)metricSample).getValue()).get() == 2L,"Sample count error."));
System.out.println(samples);
}
}