Sending System Metrics to Graphite with Spring-Boot

5.5k views Asked by At

Spring-Boot actuator exposes many useful metrics at /metrics such as uptime, memory usage, GC count.

Only a subset of these are sent to Graphite when using the Dropwizard Metrics integration. In specific, only the counters and gauges

Is there any way to get these other metrics to be published to graphite?

The documentation suggests that it should be possible:

Users of the Dropwizard ‘Metrics’ library will find that Spring Boot metrics are automatically published to com.codahale.metrics.MetricRegistry

2

There are 2 answers

3
Nitin Arora On

System Metrics created by Spring boot are not reported automatically because MetricsRegistry does not know anything about those Metrics.

You should register those metrics manually when your application boots up.

@Autowired
private SystemPublicMetrics systemPublicMetrics;

private void registerSystemMetrics(MetricRegistry metricRegistry) {
    systemPublicMetrics.metrics().forEach(m -> {
        Gauge<Long> metricGauge = () -> m.getValue().longValue();
        metricRegistry.register(m.getName(), metricGauge);   
    });
}

I have defined Gauge, not all the system metrics should be added as gauge. e.g. the Counter should be used to capture count values.

If you don't want to use Spring boot. Use can include metrics-jvm out of the box to capture JVM level metrics.

0
Sergey Shcherbakov On

Here's a solution that does update DropWizard metrics on Spring metrics change. It also does that without turning @EnableScheduling on:

@EnableMetrics
@Configuration
public class ConsoleMetricsConfig extends MetricsConfigurerAdapter {

    @Autowired
    private SystemPublicMetrics systemPublicMetrics;

    @Override
    public void configureReporters(MetricRegistry metricRegistry) {

        metricRegistry.register("jvm.memory", new MemoryUsageGaugeSet());
        metricRegistry.register("jvm.thread-states", new ThreadStatesGaugeSet());
        metricRegistry.register("jvm.garbage-collector", new GarbageCollectorMetricSet());

        metricRegistry.register("spring.boot", (MetricSet) () -> {
            final Map<String, Metric> gauges = new HashMap<String, Metric>();

            for (final org.springframework.boot.actuate.metrics.Metric<?> springMetric : 
                    systemPublicMetrics.metrics()) {

                gauges.put(springMetric.getName(), (Gauge<Object>) () -> {

                    return systemPublicMetrics.metrics().stream()
                        .filter(m -> StringUtils.equals(m.getName(), springMetric.getName()))
                        .map(m -> m.getValue())
                        .findFirst()
                        .orElse(null);

                });
            }
            return Collections.unmodifiableMap(gauges);
        });

        registerReporter(ConsoleReporter
            .forRegistry(metricRegistry)
            .convertRatesTo(TimeUnit.SECONDS)
            .convertDurationsTo(TimeUnit.MILLISECONDS)
            .build())
            .start(intervalSecs, TimeUnit.SECONDS);

    }
}

It uses the com.ryantenney.metrics library for enabling additional Spring annotations support and DropWizard reporters:

    <dependency>
        <groupId>com.ryantenney.metrics</groupId>
        <artifactId>metrics-spring</artifactId>
        <version>3.1.3</version>
    </dependency>

But it is actually not necessary in this particular case.