DefaultMetricsProvider.java
/*
* 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.zookeeper.metrics.impl;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiConsumer;
import org.apache.zookeeper.metrics.Counter;
import org.apache.zookeeper.metrics.CounterSet;
import org.apache.zookeeper.metrics.Gauge;
import org.apache.zookeeper.metrics.GaugeSet;
import org.apache.zookeeper.metrics.MetricsContext;
import org.apache.zookeeper.metrics.MetricsProvider;
import org.apache.zookeeper.metrics.MetricsProviderLifeCycleException;
import org.apache.zookeeper.metrics.Summary;
import org.apache.zookeeper.metrics.SummarySet;
import org.apache.zookeeper.server.metric.AvgMinMaxCounter;
import org.apache.zookeeper.server.metric.AvgMinMaxCounterSet;
import org.apache.zookeeper.server.metric.AvgMinMaxPercentileCounter;
import org.apache.zookeeper.server.metric.AvgMinMaxPercentileCounterSet;
import org.apache.zookeeper.server.metric.SimpleCounter;
import org.apache.zookeeper.server.metric.SimpleCounterSet;
/**
* Default implementation of {@link MetricsProvider}.<br>
* It does not implement a real hierarchy of contexts, but metrics are flattened
* in a single namespace.<br>
* It is mostly useful to make the legacy 4 letter words interface work as
* expected.
*/
public class DefaultMetricsProvider implements MetricsProvider {
private final DefaultMetricsContext rootMetricsContext = new DefaultMetricsContext();
@Override
public void configure(Properties configuration) throws MetricsProviderLifeCycleException {
}
@Override
public void start() throws MetricsProviderLifeCycleException {
}
@Override
public MetricsContext getRootContext() {
return rootMetricsContext;
}
@Override
public void stop() {
// release all references to external objects
rootMetricsContext.gauges.clear();
rootMetricsContext.gaugeSets.clear();
}
@Override
public void dump(BiConsumer<String, Object> sink) {
rootMetricsContext.dump(sink);
}
@Override
public void resetAllValues() {
rootMetricsContext.reset();
}
private static final class DefaultMetricsContext implements MetricsContext {
private final ConcurrentMap<String, Gauge> gauges = new ConcurrentHashMap<>();
private final ConcurrentMap<String, GaugeSet> gaugeSets = new ConcurrentHashMap<>();
private final ConcurrentMap<String, SimpleCounter> counters = new ConcurrentHashMap<>();
private final ConcurrentMap<String, SimpleCounterSet> counterSets = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AvgMinMaxCounter> basicSummaries = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AvgMinMaxPercentileCounter> summaries = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AvgMinMaxCounterSet> basicSummarySets = new ConcurrentHashMap<>();
private final ConcurrentMap<String, AvgMinMaxPercentileCounterSet> summarySets = new ConcurrentHashMap<>();
@Override
public MetricsContext getContext(String name) {
// no hierarchy yet
return this;
}
@Override
public Counter getCounter(String name) {
return counters.computeIfAbsent(name, (n) -> {
return new SimpleCounter(n);
});
}
@Override
public CounterSet getCounterSet(final String name) {
Objects.requireNonNull(name, "Cannot register a CounterSet with null name");
return counterSets.computeIfAbsent(name, SimpleCounterSet::new);
}
@Override
public void registerGauge(String name, Gauge gauge) {
Objects.requireNonNull(gauge, "Cannot register a null Gauge for " + name);
gauges.put(name, gauge);
}
@Override
public void unregisterGauge(String name) {
gauges.remove(name);
}
@Override
public void registerGaugeSet(final String name, final GaugeSet gaugeSet) {
Objects.requireNonNull(name, "Cannot register a GaugeSet with null name");
Objects.requireNonNull(gaugeSet, "Cannot register a null GaugeSet for " + name);
gaugeSets.put(name, gaugeSet);
}
@Override
public void unregisterGaugeSet(final String name) {
Objects.requireNonNull(name, "Cannot unregister GaugeSet with null name");
gaugeSets.remove(name);
}
@Override
public Summary getSummary(String name, DetailLevel detailLevel) {
if (detailLevel == DetailLevel.BASIC) {
return basicSummaries.computeIfAbsent(name, (n) -> {
if (summaries.containsKey(n)) {
throw new IllegalArgumentException("Already registered a non basic summary as " + n);
}
return new AvgMinMaxCounter(name);
});
} else {
return summaries.computeIfAbsent(name, (n) -> {
if (basicSummaries.containsKey(n)) {
throw new IllegalArgumentException("Already registered a basic summary as " + n);
}
return new AvgMinMaxPercentileCounter(name);
});
}
}
@Override
public SummarySet getSummarySet(String name, DetailLevel detailLevel) {
if (detailLevel == DetailLevel.BASIC) {
return basicSummarySets.computeIfAbsent(name, (n) -> {
if (summarySets.containsKey(n)) {
throw new IllegalArgumentException("Already registered a non basic summary set as " + n);
}
return new AvgMinMaxCounterSet(name);
});
} else {
return summarySets.computeIfAbsent(name, (n) -> {
if (basicSummarySets.containsKey(n)) {
throw new IllegalArgumentException("Already registered a basic summary set as " + n);
}
return new AvgMinMaxPercentileCounterSet(name);
});
}
}
void dump(BiConsumer<String, Object> sink) {
gauges.forEach((name, metric) -> {
Number value = metric.get();
if (value != null) {
sink.accept(name, value);
}
});
gaugeSets.forEach((name, gaugeSet) ->
gaugeSet.values().forEach((key, value) -> {
if (key != null) {
sink.accept(key + "_" + name, value != null ? value : 0);
}
})
);
counters.values().forEach(metric -> {
metric.values().forEach(sink);
});
counterSets.values().forEach(metric -> {
metric.values().forEach(sink);
});
basicSummaries.values().forEach(metric -> {
metric.values().forEach(sink);
});
summaries.values().forEach(metric -> {
metric.values().forEach(sink);
});
basicSummarySets.values().forEach(metric -> {
metric.values().forEach(sink);
});
summarySets.values().forEach(metric -> {
metric.values().forEach(sink);
});
}
void reset() {
counters.values().forEach(metric -> {
metric.reset();
});
counterSets.values().forEach(metric -> {
metric.reset();
});
basicSummaries.values().forEach(metric -> {
metric.reset();
});
summaries.values().forEach(metric -> {
metric.reset();
});
basicSummarySets.values().forEach(metric -> {
metric.reset();
});
summarySets.values().forEach(metric -> {
metric.reset();
});
// no need to reset gauges
}
}
}