DataNodeMetrics
Take DataNodeMetrics
as an example, the type of DefaultMetricsSystem.instance()
is MetricsSystemImpl
. A Metrics class must has a MetricsRegistry field, or has @Metrics annotation.
public static DataNodeMetrics create(Configuration conf, String dnName) {
String sessionId = conf.get(DFSConfigKeys.DFS_METRICS_SESSION_ID_KEY);
MetricsSystem ms = DefaultMetricsSystem.instance();
JvmMetrics jm = JvmMetrics.create("DataNode", sessionId, ms);
String name = "DataNodeActivity-"+ (dnName.isEmpty()
? "UndefinedDataNodeName"+ DFSUtil.getRandom().nextInt()
: dnName.replace(':', '-'));
// Percentile measurement is off by default, by watching no intervals
int[] intervals =
conf.getInts(DFSConfigKeys.DFS_METRICS_PERCENTILES_INTERVALS_KEY);
return ms.register(name, null, new DataNodeMetrics(name, sessionId,
intervals, jm));
}
MetricsSystemImpl.register
At first, it instantiates a MetricsSourceBuilder, and bouid a source using the builder.
@Override public synchronized <T>
T register(String name, String desc, T source) {
MetricsSourceBuilder sb = MetricsAnnotations.newSourceBuilder(source);
final MetricsSource s = sb.build();
MetricsAnnotations.newSourceBuilder
public static MetricsSourceBuilder newSourceBuilder(Object source) {
return new MetricsSourceBuilder(source,
DefaultMetricsFactory.getAnnotatedMetricsFactory());
}
DefaultMetricsFactory.getAnnotatedMetricsFactory
Returns a MutableMetricsFactory
public static MutableMetricsFactory getAnnotatedMetricsFactory() {
return INSTANCE.getInstance(MutableMetricsFactory.class);
}
MetricsSourceBuilder
MetricsSourceBuilder(Object source, MutableMetricsFactory factory) {
this.source = checkNotNull(source, "source");
this.factory = checkNotNull(factory, "mutable metrics factory");
Class<?> cls = source.getClass();
registry = initRegistry(source);
for (Field field : ReflectionUtils.getDeclaredFieldsIncludingInherited(cls)) {
add(source, field);
}
for (Method method : ReflectionUtils.getDeclaredMethodsIncludingInherited(cls)) {
add(source, method);
}
}
At first, it finds a field with type MetricsRegistry. And then, it finds a annotation of class. It is @Metrics(about="DataNode metrics", context="dfs")
in this case.
private MetricsRegistry initRegistry(Object source) {
Class<?> cls = source.getClass();
MetricsRegistry r = null;
// Get the registry if it already exists.
for (Field field : ReflectionUtils.getDeclaredFieldsIncludingInherited(cls)) {
if (field.getType() != MetricsRegistry.class) continue;
try {
field.setAccessible(true);
r = (MetricsRegistry) field.get(source);
hasRegistry = r != null;
break;
}
catch (Exception e) {
LOG.warn("Error accessing field "+ field, e);
continue;
}
}
// Create a new registry according to annotation
for (Annotation annotation : cls.getAnnotations()) {
if (annotation instanceof Metrics) {
Metrics ma = (Metrics) annotation;
info = factory.getInfo(cls, ma);
if (r == null) {
r = new MetricsRegistry(info);
}
r.setContext(ma.context());
}
}
if (r == null) return new MetricsRegistry(cls.getSimpleName());
return r;
}
After find the registry, it calls add(source, field)
to add every field, and add(source, method)
to add every method.
MetricsSourceBuilder(Object source, MutableMetricsFactory factory) {
this.source = checkNotNull(source, "source");
this.factory = checkNotNull(factory, "mutable metrics factory");
Class<?> cls = source.getClass();
registry = initRegistry(source);
for (Field field : ReflectionUtils.getDeclaredFieldsIncludingInherited(cls)) {
add(source, field);
}
for (Method method : ReflectionUtils.getDeclaredMethodsIncludingInherited(cls)) {
add(source, method);
}
MetricsSourceBuilder.add
to add a field, the field must has Metric
annotation, and then calls factory.newForField
to get a MutableMetric. At last, it calls field.set(source, mutable)
to set value to the field.
private void add(Object source, Field field) {
for (Annotation annotation : field.getAnnotations()) {
if (!(annotation instanceof Metric)) continue;
try {
// skip fields already set
field.setAccessible(true);
if (field.get(source) != null) continue;
}
catch (Exception e) {
LOG.warn("Error accessing field "+ field +" annotated with"+
annotation, e);
continue;
}
MutableMetric mutable = factory.newForField(field, (Metric) annotation,
registry);
if (mutable != null) {
try {
field.set(source, mutable);
hasAtMetric = true;
}
catch (Exception e) {
throw new MetricsException("Error setting field "+ field +
" annotated with "+ annotation, e);
}
}
}
}
MutableMetricsFactory.newForField
MutableMetric newForField(Field field, Metric annotation,
MetricsRegistry registry) {
if (LOG.isDebugEnabled()) {
LOG.debug("field "+ field +" with annotation "+ annotation);
}
MetricsInfo info = getInfo(annotation, field);
MutableMetric metric = newForField(field, annotation);
if (metric != null) {
registry.add(info.name(), metric);
return metric;
}
final Class<?> cls = field.getType();
if (cls == MutableCounterInt.class) {
return registry.newCounter(info, 0);
}
if (cls == MutableCounterLong.class) {
return registry.newCounter(info, 0L);
}
if (cls == MutableGaugeInt.class) {
return registry.newGauge(info, 0);
}
if (cls == MutableGaugeLong.class) {
return registry.newGauge(info, 0L);
}
if (cls == MutableRate.class) {
return registry.newRate(info.name(), info.description(),
annotation.always());
}
if (cls == MutableRates.class) {
return new MutableRates(registry);
}
if (cls == MutableStat.class) {
return registry.newStat(info.name(), info.description(),
annotation.sampleName(), annotation.valueName(),
annotation.always());
}
throw new MetricsException("Unsupported metric field "+ field.getName() +
" of type "+ field.getType().getName());
}
newCounter
Take newCounter as an example.
/**
* Create a mutable integer counter
* @param info metadata of the metric
* @param iVal initial value
* @return a new counter object
*/
public synchronized MutableCounterInt newCounter(MetricsInfo info, int iVal) {
checkMetricName(info.name());
MutableCounterInt ret = new MutableCounterInt(info, iVal);
metricsMap.put(info.name(), ret);
return ret;
}
registerSource
Register method calls registerSource, it instantiate MetricsSourceAdapter, and put it to map sources, and then calls sa.start.
synchronized
void registerSource(String name, String desc, MetricsSource source) {
checkNotNull(config, "config");
MetricsConfig conf = sourceConfigs.get(name);
MetricsSourceAdapter sa = conf != null
? new MetricsSourceAdapter(prefix, name, desc, source,
injectedTags, period, conf)
: new MetricsSourceAdapter(prefix, name, desc, source,
injectedTags, period, config.subset(SOURCE_KEY));
sources.put(name, sa);
sa.start();
LOG.debug("Registered source "+ name);
}
Method start calls startMBeans
startMBeans register it to jmx.
synchronized void startMBeans() {
if (mbeanName != null) {
LOG.warn("MBean "+ name +" already initialized!");
LOG.debug("Stacktrace: ", new Throwable());
return;
}
mbeanName = MBeans.register(prefix, name, this);
LOG.debug("MBean for source "+ name +" registered.");
}
MetricsSourceAdapter(String prefix, String name, String description,
MetricsSource source, Iterable<MetricsTag> injectedTags,
int period, MetricsConfig conf) {
this(prefix, name, description, source, injectedTags,
conf.getFilter(RECORD_FILTER_KEY),
conf.getFilter(METRIC_FILTER_KEY),
period + 1, // hack to avoid most of the "innocuous" races.
conf.getBoolean(START_MBEANS_KEY, true));
}
MetricsSourceBuilder.build
public MetricsSource build() {
if (source instanceof MetricsSource) {
if (hasAtMetric && !hasRegistry) {
throw new MetricsException("Hybrid metrics: registry required.");
}
return (MetricsSource) source;
}
else if (!hasAtMetric) {
throw new MetricsException("No valid @Metric annotation found.");
}
return new MetricsSource() {
@Override
public void getMetrics(MetricsCollector builder, boolean all) {
registry.snapshot(builder.addRecord(registry.info()), all);
}
};
}