blob: 0699aa7cef672d27a6326ca0b8aaaedae043e6e1 [file] [log] [blame]
/*
* 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.iotdb.metrics;
import org.apache.iotdb.metrics.config.MetricConfig;
import org.apache.iotdb.metrics.config.MetricConfigDescriptor;
import org.apache.iotdb.metrics.impl.DoNothingMetricManager;
import org.apache.iotdb.metrics.reporter.JmxReporter;
import org.apache.iotdb.metrics.type.AutoGauge;
import org.apache.iotdb.metrics.type.Counter;
import org.apache.iotdb.metrics.type.Gauge;
import org.apache.iotdb.metrics.type.Histogram;
import org.apache.iotdb.metrics.type.IMetric;
import org.apache.iotdb.metrics.type.Rate;
import org.apache.iotdb.metrics.type.Timer;
import org.apache.iotdb.metrics.utils.MetricInfo;
import org.apache.iotdb.metrics.utils.MetricLevel;
import org.apache.iotdb.metrics.utils.MetricType;
import org.apache.tsfile.utils.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
public abstract class AbstractMetricManager {
protected static final MetricConfig METRIC_CONFIG =
MetricConfigDescriptor.getInstance().getMetricConfig();
private static final String ALREADY_EXISTS = " is already used for a different type of name";
/** The map from metric name to metric metaInfo. */
protected Map<String, MetricInfo.MetaInfo> nameToMetaInfo;
/** The map from metricInfo to metric. */
protected Map<MetricInfo, IMetric> metrics;
/** The bind IoTDBJmxReporter */
protected JmxReporter bindJmxReporter = null;
protected AbstractMetricManager() {
nameToMetaInfo = new ConcurrentHashMap<>();
metrics = new ConcurrentHashMap<>();
}
/**
* Notify IoTDB JmxReporter to register metric in JMX
*
* @param metric the created metric
* @param metricInfo the created metric info
*/
private void notifyReporterOnAdd(IMetric metric, MetricInfo metricInfo) {
// if the reporter type is JMX, register the new metric
Optional.ofNullable(bindJmxReporter)
.ifPresent(x -> bindJmxReporter.registerMetric(metric, metricInfo));
}
/**
* Notify IoTDB JmxReporter to remove metric in JMX
*
* @param metric the removed metric
* @param metricInfo the removed metric info
*/
private void notifyReporterOnRemove(IMetric metric, MetricInfo metricInfo) {
// if the reporter type is JMX, unregister the new metric
Optional.ofNullable(bindJmxReporter)
.ifPresent(x -> bindJmxReporter.unregisterMetric(metric, metricInfo));
}
/**
* Get counter. return if exists, create if not.
*
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public Counter getOrCreateCounter(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_COUNTER;
}
MetricInfo metricInfo = new MetricInfo(MetricType.COUNTER, name, tags);
IMetric metric =
metrics.computeIfAbsent(
metricInfo,
key -> {
Counter counter = createCounter();
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
notifyReporterOnAdd(counter, metricInfo);
return counter;
});
if (metric instanceof Counter) {
return (Counter) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
protected abstract Counter createCounter();
/**
* Create autoGauge
*
* <p>AutoGauge keep a weak reference of the obj, so it will not prevent gc of the obj. Notice: if
* you call this gauge's value() when the obj has already been cleared by gc, then you will get
* 0L.
*
* @param name the name of name
* @param metricLevel the level of name
* @param obj which will be monitored automatically
* @param mapper use which to map the obj to a double value
*/
public <T> AutoGauge createAutoGauge(
String name, MetricLevel metricLevel, T obj, ToDoubleFunction<T> mapper, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_AUTO_GAUGE;
}
MetricInfo metricInfo = new MetricInfo(MetricType.AUTO_GAUGE, name, tags);
AutoGauge gauge = createAutoGauge(obj, mapper);
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
metrics.put(metricInfo, gauge);
notifyReporterOnAdd(gauge, metricInfo);
return gauge;
}
/**
* Create autoGauge according to metric framework.
*
* @param obj which will be monitored automatically
* @param mapper use which to map the obj to a double value
*/
protected abstract <T> AutoGauge createAutoGauge(T obj, ToDoubleFunction<T> mapper);
/**
* Get autoGauge.
*
* @param name the name of name
* @param metricLevel the level of name
* @throws IllegalArgumentException when there has different type metric with same name
*/
public AutoGauge getAutoGauge(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_AUTO_GAUGE;
}
MetricInfo metricInfo = new MetricInfo(MetricType.AUTO_GAUGE, name, tags);
IMetric metric = metrics.get(metricInfo);
if (metric == null) {
return DoNothingMetricManager.DO_NOTHING_AUTO_GAUGE;
} else if (metric instanceof AutoGauge) {
return (AutoGauge) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
/**
* Get counter. return if exists, create if not.
*
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public Gauge getOrCreateGauge(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_GAUGE;
}
MetricInfo metricInfo = new MetricInfo(MetricType.GAUGE, name, tags);
IMetric metric =
metrics.computeIfAbsent(
metricInfo,
key -> {
Gauge gauge = createGauge();
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
notifyReporterOnAdd(gauge, metricInfo);
return gauge;
});
if (metric instanceof Gauge) {
return (Gauge) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
/** Create gauge according to metric framework. */
protected abstract Gauge createGauge();
/**
* Get rate. return if exists, create if not.
*
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public Rate getOrCreateRate(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_RATE;
}
MetricInfo metricInfo = new MetricInfo(MetricType.RATE, name, tags);
IMetric metric =
metrics.computeIfAbsent(
metricInfo,
key -> {
Rate rate = createRate();
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
notifyReporterOnAdd(rate, metricInfo);
return rate;
});
if (metric instanceof Rate) {
return (Rate) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
/** Create rate according to metric framework. */
protected abstract Rate createRate();
/**
* Get histogram. return if exists, create if not.
*
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public Histogram getOrCreateHistogram(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_HISTOGRAM;
}
MetricInfo metricInfo = new MetricInfo(MetricType.HISTOGRAM, name, tags);
IMetric metric =
metrics.computeIfAbsent(
metricInfo,
key -> {
Histogram histogram = createHistogram(metricInfo);
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
notifyReporterOnAdd(histogram, metricInfo);
return histogram;
});
if (metric instanceof Histogram) {
return (Histogram) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
/**
* Create histogram according to metric framework.
*
* @param metricInfo the metricInfo of metric
*/
protected abstract Histogram createHistogram(MetricInfo metricInfo);
/**
* Get timer. return if exists, create if not.
*
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public Timer getOrCreateTimer(String name, MetricLevel metricLevel, String... tags) {
if (invalid(metricLevel, name, tags)) {
return DoNothingMetricManager.DO_NOTHING_TIMER;
}
MetricInfo metricInfo = new MetricInfo(MetricType.TIMER, name, tags);
IMetric metric =
metrics.computeIfAbsent(
metricInfo,
key -> {
Timer timer = createTimer();
nameToMetaInfo.put(name, metricInfo.getMetaInfo());
notifyReporterOnAdd(timer, metricInfo);
return timer;
});
if (metric instanceof Timer) {
return (Timer) metric;
}
throw new IllegalArgumentException(metricInfo + ALREADY_EXISTS);
}
/** Create timer according to metric framework. */
protected abstract Timer createTimer();
// endregion
// region update metric
/**
* Update counter. if exists, then update counter by delta. if not, then create and update.
*
* @param delta the value to update
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
*/
public Counter count(long delta, String name, MetricLevel metricLevel, String... tags) {
Counter counter = getOrCreateCounter(name, metricLevel, tags);
counter.inc(delta);
return counter;
}
/**
* Set value of gauge. if exists, then set gauge by value. if not, then create and set.
*
* @param value the value of gauge
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
*/
public Gauge gauge(long value, String name, MetricLevel metricLevel, String... tags) {
Gauge gauge = getOrCreateGauge(name, metricLevel, tags);
gauge.set(value);
return gauge;
}
/**
* Mark rate. if exists, then mark rate by value. if not, then create and mark.
*
* @param value the value to mark
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
*/
public Rate rate(long value, String name, MetricLevel metricLevel, String... tags) {
Rate rate = getOrCreateRate(name, metricLevel, tags);
rate.mark(value);
return rate;
}
/**
* Update histogram. if exists, then update histogram by value. if not, then create and update
*
* @param value the value to update
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
*/
public Histogram histogram(long value, String name, MetricLevel metricLevel, String... tags) {
Histogram histogram = getOrCreateHistogram(name, metricLevel, tags);
histogram.update(value);
return histogram;
}
/**
* Update timer. if exists, then update timer by delta and timeUnit. if not, then create and
* update
*
* @param delta the value to update
* @param timeUnit the unit of delta
* @param name the name of name
* @param metricLevel the level of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
*/
public Timer timer(
long delta, TimeUnit timeUnit, String name, MetricLevel metricLevel, String... tags) {
Timer timer = getOrCreateTimer(name, metricLevel, tags);
timer.update(delta, timeUnit);
return timer;
}
// endregion
// region get metric
/**
* Get all metric keys.
*
* @return [[name, [tags...]], ..., [name, [tags...]]]
*/
public List<Pair<String, String[]>> getAllMetricKeys() {
List<Pair<String, String[]>> keys = new ArrayList<>(metrics.size());
metrics.keySet().forEach(k -> keys.add(k.toStringArray()));
return keys;
}
/**
* Get all metrics.
*
* @return [name, [tags...]] -> metric
*/
public Map<MetricInfo, IMetric> getAllMetrics() {
return metrics;
}
/**
* Get metrics by type.
*
* @return [name, [tags...]] -> metric
*/
public Map<MetricInfo, IMetric> getMetricsByType(MetricType metricType) {
Map<MetricInfo, IMetric> result = new HashMap<>();
for (Map.Entry<MetricInfo, IMetric> entry : metrics.entrySet()) {
if (entry.getKey().getMetaInfo().getType() == metricType) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
// endregion
// region remove metric
/**
* remove name.
*
* @param type the type of name
* @param name the name of name
* @param tags string pairs, like sg="ln" will be "sg", "ln"
* @throws IllegalArgumentException when there has different type metric with same name
*/
public void remove(MetricType type, String name, String... tags) {
MetricInfo metricInfo = new MetricInfo(type, name, tags);
if (metrics.containsKey(metricInfo)) {
if (type == metricInfo.getMetaInfo().getType()) {
notifyReporterOnRemove(metrics.get(metricInfo), metricInfo);
nameToMetaInfo.remove(metricInfo.getName());
metrics.remove(metricInfo);
removeMetric(type, metricInfo);
} else {
throw new IllegalArgumentException(
metricInfo + " failed to remove because the mismatch of type. ");
}
}
}
protected abstract void removeMetric(MetricType type, MetricInfo metricInfo);
// endregion
/** Is metric service enabled in specific level. */
public boolean isEnableMetricInGivenLevel(MetricLevel metricLevel) {
return MetricLevel.higherOrEqual(metricLevel, METRIC_CONFIG.getMetricLevel());
}
public void setBindJmxReporter(JmxReporter reporter) {
this.bindJmxReporter = reporter;
}
/** Stop and clear metric manager. */
protected boolean stop() {
metrics = new ConcurrentHashMap<>();
nameToMetaInfo = new ConcurrentHashMap<>();
return stopFramework();
}
protected abstract boolean stopFramework();
private boolean invalid(MetricLevel metricLevel, String name, String... tags) {
if (!isEnableMetricInGivenLevel(metricLevel)) {
return true;
}
MetricInfo.MetaInfo metaInfo = nameToMetaInfo.get(name);
if (metaInfo == null) {
return false;
}
return !metaInfo.hasSameKey(tags);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AbstractMetricManager that = (AbstractMetricManager) o;
return Objects.equals(metrics, that.metrics);
}
@Override
public int hashCode() {
return Objects.hash(metrics);
}
}