Springboot reads metrics from dropwizard.

  springboot

autoconfig

~/.m2/repository/org/springframework/boot/spring-boot-actuator/1.4.3.RELEASE/spring-boot-actuator-1.4.3.RELEASE-sources.jar! /org/springframework/boot/actuate/autoconfigure/MetricsDropwizardAutoConfiguration.java

@Configuration
@ConditionalOnClass(MetricRegistry.class)
@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class)
public class MetricsDropwizardAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MetricRegistry metricRegistry() {
        return new MetricRegistry();
    }

    @Bean
    @ConditionalOnMissingBean({ DropwizardMetricServices.class, CounterService.class,
            GaugeService.class })
    public DropwizardMetricServices dropwizardMetricServices(
            MetricRegistry metricRegistry) {
        return new DropwizardMetricServices(metricRegistry);
    }

    @Bean
    public MetricReaderPublicMetrics dropwizardPublicMetrics(
            MetricRegistry metricRegistry) {
        MetricRegistryMetricReader reader = new MetricRegistryMetricReader(
                metricRegistry);
        return new MetricReaderPublicMetrics(reader);
    }

}

Here, dropwizard’s metrics are packaged as MetricReaderPublicMetrics.

MetricRegistryMetricReader

~/.m2/repository/org/springframework/boot/spring-boot-actuator/1.4.3.RELEASE/spring-boot-actuator-1.4.3.RELEASE-sources.jar! /org/springframework/boot/actuate/metrics/reader/MetricRegistryMetricReader.java

public class MetricRegistryMetricReader implements MetricReader, MetricRegistryListener {

    private static final Log logger = LogFactory.getLog(MetricRegistryMetricReader.class);

    private static final Map<Class<?>, Set<String>> numberKeys = new ConcurrentHashMap<Class<?>, Set<String>>();

    private final Object monitor = new Object();

    private final Map<String, String> names = new ConcurrentHashMap<String, String>();

    private final MultiValueMap<String, String> reverse = new LinkedMultiValueMap<String, String>();

    private final MetricRegistry registry;

    public MetricRegistryMetricReader(MetricRegistry registry) {
        this.registry = registry;
        registry.addListener(this);
    }

    @Override
    public Metric<?> findOne(String metricName) {
        String name = this.names.get(metricName);
        if (name == null) {
            return null;
        }
        com.codahale.metrics.Metric metric = this.registry.getMetrics().get(name);
        if (metric == null) {
            return null;
        }
        if (metric instanceof Counter) {
            Counter counter = (Counter) metric;
            return new Metric<Number>(metricName, counter.getCount());
        }
        if (metric instanceof Gauge) {
            Object value = ((Gauge<?>) metric).getValue();
            if (value instanceof Number) {
                return new Metric<Number>(metricName, (Number) value);
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Ignoring gauge '" + name + "' (" + metric
                        + ") as its value is not a Number");
            }
            return null;
        }
        if (metric instanceof Sampling) {
            if (metricName.contains(".snapshot.")) {
                Number value = getMetric(((Sampling) metric).getSnapshot(), metricName);
                if (metric instanceof Timer) {
                    // convert back to MILLISEC
                    value = TimeUnit.MILLISECONDS.convert(value.longValue(),
                            TimeUnit.NANOSECONDS);
                }
                return new Metric<Number>(metricName, value);
            }
        }
        return new Metric<Number>(metricName, getMetric(metric, metricName));
    }

    @Override
    public Iterable<Metric<?>> findAll() {
        return new Iterable<Metric<?>>() {
            @Override
            public Iterator<Metric<?>> iterator() {
                Set<Metric<?>> metrics = new HashSet<Metric<?>>();
                for (String name : MetricRegistryMetricReader.this.names.keySet()) {
                    Metric<?> metric = findOne(name);
                    if (metric != null) {
                        metrics.add(metric);
                    }
                }
                return metrics.iterator();
            }
        };
    }

    @Override
    public long count() {
        return this.names.size();
    }

    @Override
    public void onGaugeAdded(String name, Gauge<?> gauge) {
        this.names.put(name, name);
        synchronized (this.monitor) {
            this.reverse.add(name, name);
        }
    }

    @Override
    public void onGaugeRemoved(String name) {
        remove(name);
    }

    @Override
    public void onCounterAdded(String name, Counter counter) {
        this.names.put(name, name);
        synchronized (this.monitor) {
            this.reverse.add(name, name);
        }
    }

    @Override
    public void onCounterRemoved(String name) {
        remove(name);
    }

    @Override
    public void onHistogramAdded(String name, Histogram histogram) {
        for (String key : getNumberKeys(histogram)) {
            String metricName = name + "." + key;
            this.names.put(metricName, name);
            synchronized (this.monitor) {
                this.reverse.add(name, metricName);
            }
        }
        for (String key : getNumberKeys(histogram.getSnapshot())) {
            String metricName = name + ".snapshot." + key;
            this.names.put(metricName, name);
            synchronized (this.monitor) {
                this.reverse.add(name, metricName);
            }
        }
    }

    @Override
    public void onHistogramRemoved(String name) {
        remove(name);
    }

    @Override
    public void onMeterAdded(String name, Meter meter) {
        for (String key : getNumberKeys(meter)) {
            String metricName = name + "." + key;
            this.names.put(metricName, name);
            synchronized (this.monitor) {
                this.reverse.add(name, metricName);
            }
        }
    }

    @Override
    public void onMeterRemoved(String name) {
        remove(name);
    }

    @Override
    public void onTimerAdded(String name, Timer timer) {
        for (String key : getNumberKeys(timer)) {
            String metricName = name + "." + key;
            this.names.put(metricName, name);
            synchronized (this.monitor) {
                this.reverse.add(name, metricName);
            }
        }
        for (String key : getNumberKeys(timer.getSnapshot())) {
            String metricName = name + ".snapshot." + key;
            this.names.put(metricName, name);
            synchronized (this.monitor) {
                this.reverse.add(name, metricName);
            }
        }
    }

    @Override
    public void onTimerRemoved(String name) {
        remove(name);
    }

    private void remove(String name) {
        List<String> keys;
        synchronized (this.monitor) {
            keys = this.reverse.remove(name);
        }
        if (keys != null) {
            for (String key : keys) {
                this.names.remove(name + "." + key);
            }
        }
    }

    private static Set<String> getNumberKeys(Object metric) {
        Set<String> result = numberKeys.get(metric.getClass());
        if (result == null) {
            result = new HashSet<String>();
        }
        if (result.isEmpty()) {
            for (PropertyDescriptor descriptor : BeanUtils
                    .getPropertyDescriptors(metric.getClass())) {
                if (ClassUtils.isAssignable(Number.class, descriptor.getPropertyType())) {
                    result.add(descriptor.getName());
                }
            }
            numberKeys.put(metric.getClass(), result);
        }
        return result;
    }

    private static Number getMetric(Object metric, String metricName) {
        String key = StringUtils.getFilenameExtension(metricName);
        return (Number) new BeanWrapperImpl(metric).getPropertyValue(key);
    }

}

PublicMetricsAutoConfiguration

~/.m2/repository/org/springframework/boot/spring-boot-actuator/1.4.3.RELEASE/spring-boot-actuator-1.4.3.RELEASE-sources.jar! /org/springframework/boot/actuate/autoconfigure/PublicMetricsAutoConfiguration.java

@Configuration
@AutoConfigureBefore(EndpointAutoConfiguration.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, CacheAutoConfiguration.class,
        MetricRepositoryAutoConfiguration.class, CacheStatisticsAutoConfiguration.class,
        IntegrationAutoConfiguration.class })
public class PublicMetricsAutoConfiguration {

    private final List<MetricReader> metricReaders;

    public PublicMetricsAutoConfiguration(
            @ExportMetricReader ObjectProvider<List<MetricReader>> metricReadersProvider) {
        this.metricReaders = metricReadersProvider.getIfAvailable();
    }

    @Bean
    public SystemPublicMetrics systemPublicMetrics() {
        return new SystemPublicMetrics();
    }

    @Bean
    public MetricReaderPublicMetrics metricReaderPublicMetrics() {
        return new MetricReaderPublicMetrics(
                new CompositeMetricReader(this.metricReaders == null ? new MetricReader[0]
                        : this.metricReaders
                                .toArray(new MetricReader[this.metricReaders.size()])));
    }

    @Bean
    @ConditionalOnBean(RichGaugeReader.class)
    public RichGaugeReaderPublicMetrics richGaugePublicMetrics(
            RichGaugeReader richGaugeReader) {
        return new RichGaugeReaderPublicMetrics(richGaugeReader);
    }

    @Configuration
    @ConditionalOnClass(DataSource.class)
    @ConditionalOnBean(DataSource.class)
    static class DataSourceMetricsConfiguration {

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnBean(DataSourcePoolMetadataProvider.class)
        public DataSourcePublicMetrics dataSourcePublicMetrics() {
            return new DataSourcePublicMetrics();
        }

    }

    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class })
    @ConditionalOnWebApplication
    static class TomcatMetricsConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public TomcatPublicMetrics tomcatPublicMetrics() {
            return new TomcatPublicMetrics();
        }

    }

    @Configuration
    @ConditionalOnClass(CacheManager.class)
    @ConditionalOnBean(CacheManager.class)
    static class CacheStatisticsConfiguration {

        @Bean
        @ConditionalOnMissingBean
        @ConditionalOnBean(CacheStatisticsProvider.class)
        public CachePublicMetrics cachePublicMetrics() {
            return new CachePublicMetrics();
        }

    }

    @Configuration
    @ConditionalOnClass(IntegrationMBeanExporter.class)
    @ConditionalOnBean(IntegrationMBeanExporter.class)
    @ConditionalOnJava(JavaVersion.SEVEN)
    @UsesJava7
    static class IntegrationMetricsConfiguration {

        @Bean
        @ConditionalOnMissingBean(name = "springIntegrationPublicMetrics")
        public MetricReaderPublicMetrics springIntegrationPublicMetrics(
                IntegrationMBeanExporter exporter) {
            return new MetricReaderPublicMetrics(
                    new SpringIntegrationMetricReader(exporter));
        }

    }

}

There are both SystemPublicMetrics and MetricReaderPublicMetrics, which use CompositeMetricReader.

MetricsEndpoint

~/.m2/repository/org/springframework/boot/spring-boot-actuator/1.4.3.RELEASE/spring-boot-actuator-1.4.3.RELEASE-sources.jar! /org/springframework/boot/actuate/endpoint/MetricsEndpoint.java

@ConfigurationProperties(prefix = "endpoints.metrics")
public class MetricsEndpoint extends AbstractEndpoint<Map<String, Object>> {

    private final List<PublicMetrics> publicMetrics;

    /**
     * Create a new {@link MetricsEndpoint} instance.
     * @param publicMetrics the metrics to expose
     */
    public MetricsEndpoint(PublicMetrics publicMetrics) {
        this(Collections.singleton(publicMetrics));
    }

    /**
     * Create a new {@link MetricsEndpoint} instance.
     * @param publicMetrics the metrics to expose. The collection will be sorted using the
     * {@link AnnotationAwareOrderComparator}.
     */
    public MetricsEndpoint(Collection<PublicMetrics> publicMetrics) {
        super("metrics");
        Assert.notNull(publicMetrics, "PublicMetrics must not be null");
        this.publicMetrics = new ArrayList<PublicMetrics>(publicMetrics);
        AnnotationAwareOrderComparator.sort(this.publicMetrics);
    }

    public void registerPublicMetrics(PublicMetrics metrics) {
        this.publicMetrics.add(metrics);
        AnnotationAwareOrderComparator.sort(this.publicMetrics);
    }

    public void unregisterPublicMetrics(PublicMetrics metrics) {
        this.publicMetrics.remove(metrics);
    }

    @Override
    public Map<String, Object> invoke() {
        Map<String, Object> result = new LinkedHashMap<String, Object>();
        List<PublicMetrics> metrics = new ArrayList<PublicMetrics>(this.publicMetrics);
        for (PublicMetrics publicMetric : metrics) {
            try {
                for (Metric<?> metric : publicMetric.metrics()) {
                    result.put(metric.getName(), metric.getValue());
                }
            }
            catch (Exception ex) {
                // Could not evaluate metrics
            }
        }
        return result;
    }

}

Public metric is exposed here on the endpoint of /metrics.