/** | |
* 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 com.alibaba.jstorm.common.metric; | |
import java.util.Collections; | |
import java.util.Map; | |
import java.util.SortedMap; | |
import java.util.SortedSet; | |
import java.util.TreeMap; | |
import java.util.TreeSet; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.concurrent.ConcurrentMap; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import com.alibaba.jstorm.common.metric.window.Metric; | |
public class MetricRegistry implements MetricSet { | |
private static final Logger LOG = LoggerFactory.getLogger(MetricRegistry.class); | |
private static final long serialVersionUID = 8184106900230111064L; | |
public static final String NAME_SEPERATOR = "."; | |
/** | |
* Concatenates elements to form a dotted name, eliding any null values or | |
* empty strings. | |
* | |
* @param name the first element of the name | |
* @param names the remaining elements of the name | |
* @return {@code name} and {@code names} concatenated by periods | |
*/ | |
public static String name(String name, String... names) { | |
final StringBuilder builder = new StringBuilder(); | |
append(builder, name); | |
if (names != null) { | |
for (String s : names) { | |
append(builder, s); | |
} | |
} | |
return builder.toString(); | |
} | |
/** | |
* Concatenates a class name and elements to form a dotted name, eliding any | |
* null values or empty strings. | |
* | |
* @param klass the first element of the name | |
* @param names the remaining elements of the name | |
* @return {@code klass} and {@code names} concatenated by periods | |
*/ | |
public static String name(Class<?> klass, String... names) { | |
return name(klass.getName(), names); | |
} | |
private static void append(StringBuilder builder, String part) { | |
if (part != null && !part.isEmpty()) { | |
if (builder.length() > 0) { | |
builder.append(NAME_SEPERATOR); | |
} | |
builder.append(part); | |
} | |
} | |
protected final ConcurrentMap<String, Metric> metrics; | |
/** | |
* Creates a new {@link MetricRegistry}. | |
*/ | |
public MetricRegistry() { | |
this.metrics = buildMap(); | |
} | |
/** | |
* Creates a new {@link ConcurrentMap} implementation for use inside the | |
* registry. Override this to create a {@link MetricRegistry} with space- or | |
* time-bounded metric lifecycles, for example. | |
* | |
* @return a new {@link ConcurrentMap} | |
*/ | |
protected ConcurrentMap<String, Metric> buildMap() { | |
return new ConcurrentHashMap<String, Metric>(); | |
} | |
/** | |
* Given a {@link Metric}, registers it under the given name. | |
* | |
* @param name the name of the metric | |
* @param metric the metric | |
* @param <T> the type of the metric | |
* @return {@code metric} | |
* @throws IllegalArgumentException if the name is already registered | |
*/ | |
@SuppressWarnings("unchecked") | |
public <T extends Metric> T register(String name, T metric) | |
throws IllegalArgumentException { | |
if (metric instanceof MetricSet) { | |
registerAll(name, (MetricSet) metric); | |
} else { | |
final Metric existing = metrics.putIfAbsent(name, metric); | |
if (existing == null) { | |
// add one listener to notify | |
LOG.info("Successfully register metric of {}", name); | |
} else { | |
throw new IllegalArgumentException("A metric named " + name | |
+ " already exists"); | |
} | |
} | |
return metric; | |
} | |
/** | |
* Given a metric set, registers them. | |
* | |
* @param metrics a set of metrics | |
* @throws IllegalArgumentException if any of the names are already | |
* registered | |
*/ | |
public void registerAll(MetricSet metrics) throws IllegalArgumentException { | |
registerAll(null, metrics); | |
} | |
/** | |
* Removes the metric with the given name. | |
* | |
* @param name the name of the metric | |
* @return whether or not the metric was removed | |
*/ | |
public boolean remove(String name) { | |
final Metric metric = metrics.remove(name); | |
if (metric != null) { | |
// call listener to notify remove | |
LOG.info("Successfully unregister metric of {}", name); | |
return true; | |
} | |
return false; | |
} | |
/** | |
* Removes all metrics which match the given filter. | |
* | |
* @param filter a filter | |
*/ | |
public void removeMatching(MetricFilter filter) { | |
for (Map.Entry<String, Metric> entry : metrics.entrySet()) { | |
if (filter.matches(entry.getKey(), entry.getValue())) { | |
remove(entry.getKey()); | |
} | |
} | |
} | |
/** | |
* Returns a set of the names of all the metrics in the registry. | |
* | |
* @return the names of all the metrics | |
*/ | |
public SortedSet<String> getNames() { | |
return Collections.unmodifiableSortedSet(new TreeSet<String>(metrics | |
.keySet())); | |
} | |
/** | |
* Returns a map of all the gauges in the registry and their names. | |
* | |
* @return all the gauges in the registry | |
*/ | |
public SortedMap<String, Gauge> getGauges() { | |
return getGauges(MetricFilter.ALL); | |
} | |
/** | |
* Returns a map of all the gauges in the registry and their names which | |
* match the given filter. | |
* | |
* @param filter the metric filter to match | |
* @return all the gauges in the registry | |
*/ | |
public SortedMap<String, Gauge> getGauges(MetricFilter filter) { | |
return getMetrics(Gauge.class, filter); | |
} | |
/** | |
* Returns a map of all the counters in the registry and their names. | |
* | |
* @return all the counters in the registry | |
*/ | |
public SortedMap<String, Counter> getCounters() { | |
return getCounters(MetricFilter.ALL); | |
} | |
/** | |
* Returns a map of all the counters in the registry and their names which | |
* match the given filter. | |
* | |
* @param filter the metric filter to match | |
* @return all the counters in the registry | |
*/ | |
public SortedMap<String, Counter> getCounters(MetricFilter filter) { | |
return getMetrics(Counter.class, filter); | |
} | |
/** | |
* Returns a map of all the histograms in the registry and their names. | |
* | |
* @return all the histograms in the registry | |
*/ | |
public SortedMap<String, Histogram> getHistograms() { | |
return getHistograms(MetricFilter.ALL); | |
} | |
/** | |
* Returns a map of all the histograms in the registry and their names which | |
* match the given filter. | |
* | |
* @param filter the metric filter to match | |
* @return all the histograms in the registry | |
*/ | |
public SortedMap<String, Histogram> getHistograms(MetricFilter filter) { | |
return getMetrics(Histogram.class, filter); | |
} | |
/** | |
* Returns a map of all the meters in the registry and their names. | |
* | |
* @return all the meters in the registry | |
*/ | |
public SortedMap<String, Meter> getMeters() { | |
return getMeters(MetricFilter.ALL); | |
} | |
/** | |
* Returns a map of all the meters in the registry and their names which | |
* match the given filter. | |
* | |
* @param filter the metric filter to match | |
* @return all the meters in the registry | |
*/ | |
public SortedMap<String, Meter> getMeters(MetricFilter filter) { | |
return getMetrics(Meter.class, filter); | |
} | |
/** | |
* Returns a map of all the timers in the registry and their names. | |
* | |
* @return all the timers in the registry | |
*/ | |
public SortedMap<String, Timer> getTimers() { | |
return getTimers(MetricFilter.ALL); | |
} | |
/** | |
* Returns a map of all the timers in the registry and their names which | |
* match the given filter. | |
* | |
* @param filter the metric filter to match | |
* @return all the timers in the registry | |
*/ | |
public SortedMap<String, Timer> getTimers(MetricFilter filter) { | |
return getMetrics(Timer.class, filter); | |
} | |
@SuppressWarnings("unchecked") | |
private <T extends Metric> SortedMap<String, T> getMetrics(Class<T> klass, | |
MetricFilter filter) { | |
final TreeMap<String, T> timers = new TreeMap<String, T>(); | |
for (Map.Entry<String, Metric> entry : metrics.entrySet()) { | |
if (klass.isInstance(entry.getValue()) | |
&& filter.matches(entry.getKey(), entry.getValue())) { | |
timers.put(entry.getKey(), (T) entry.getValue()); | |
} | |
} | |
return Collections.unmodifiableSortedMap(timers); | |
} | |
private void registerAll(String prefix, MetricSet metrics) | |
throws IllegalArgumentException { | |
for (Map.Entry<String, Metric> entry : metrics.getMetrics().entrySet()) { | |
if (entry.getValue() instanceof MetricSet) { | |
registerAll(name(prefix, entry.getKey()), | |
(MetricSet) entry.getValue()); | |
} else { | |
register(name(prefix, entry.getKey()), entry.getValue()); | |
} | |
} | |
} | |
@Override | |
public Map<String, Metric> getMetrics() { | |
return Collections.unmodifiableMap(metrics); | |
} | |
/** | |
* Expose metrics is to improve performance | |
* | |
* @return | |
*/ | |
public Metric getMetric(String name) { | |
return metrics.get(name); | |
} | |
} |