Add metric flusher to export remaining metrics on shutdown

Before this change the app context closes and metrics that have not
yet been exported ccan be orphaned. The design of this feature is simple:
use Closeable where possible, so that it will be called automatically
by Spring on shutdown.

Fixes gh-5771
pull/5808/merge
Dave Syer 9 years ago
parent de0f0ecce4
commit b9db4742ac

@ -16,6 +16,9 @@
package org.springframework.boot.actuate.metrics.export; package org.springframework.boot.actuate.metrics.export;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -36,7 +39,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
public abstract class AbstractMetricExporter implements Exporter { public abstract class AbstractMetricExporter implements Exporter, Closeable, Flushable {
private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class); private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class);
@ -143,6 +146,13 @@ public abstract class AbstractMetricExporter implements Exporter {
} }
} }
@Override
public void close() throws IOException {
export();
flushQuietly();
}
@Override
public void flush() { public void flush() {
} }

@ -16,9 +16,13 @@
package org.springframework.boot.actuate.metrics.export; package org.springframework.boot.actuate.metrics.export;
import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set;
import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.reader.MetricReader;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter; import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
@ -32,7 +36,7 @@ import org.springframework.scheduling.config.ScheduledTaskRegistrar;
* @author Dave Syer * @author Dave Syer
* @since 1.3.0 * @since 1.3.0
*/ */
public class MetricExporters implements SchedulingConfigurer { public class MetricExporters implements SchedulingConfigurer, Closeable {
private MetricReader reader; private MetricReader reader;
@ -42,6 +46,8 @@ public class MetricExporters implements SchedulingConfigurer {
private final Map<String, Exporter> exporters = new HashMap<String, Exporter>(); private final Map<String, Exporter> exporters = new HashMap<String, Exporter>();
private final Set<String> closeables = new HashSet<String>();
public MetricExporters(MetricExportProperties properties) { public MetricExporters(MetricExportProperties properties) {
this.properties = properties; this.properties = properties;
} }
@ -78,6 +84,7 @@ public class MetricExporters implements SchedulingConfigurer {
if (trigger != null) { if (trigger != null) {
MetricCopyExporter exporter = getExporter(writer, trigger); MetricCopyExporter exporter = getExporter(writer, trigger);
this.exporters.put(name, exporter); this.exporters.put(name, exporter);
this.closeables.add(name);
ExportRunner runner = new ExportRunner(exporter); ExportRunner runner = new ExportRunner(exporter);
IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(), IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(),
trigger.getDelayMillis()); trigger.getDelayMillis());
@ -99,6 +106,16 @@ public class MetricExporters implements SchedulingConfigurer {
return this.exporters; return this.exporters;
} }
@Override
public void close() throws IOException {
for (String name : this.closeables) {
Exporter exporter = this.exporters.get(name);
if (exporter instanceof Closeable) {
((Closeable) exporter).close();
}
}
}
private static class ExportRunner implements Runnable { private static class ExportRunner implements Runnable {
private final Exporter exporter; private final Exporter exporter;

@ -68,6 +68,21 @@ public class MetricExportAutoConfigurationTests {
} }
} }
@Test
public void metricsFlushAutomatically() throws Exception {
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
MetricRepositoryAutoConfiguration.class,
MetricExportAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
GaugeService gaugeService = this.context.getBean(GaugeService.class);
assertNotNull(gaugeService);
gaugeService.submit("foo", 2.7);
MetricExporters flusher = this.context.getBean(MetricExporters.class);
flusher.close(); // this will be called by Spring on shutdown
MetricWriter writer = this.context.getBean("writer", MetricWriter.class);
Mockito.verify(writer, Mockito.atLeastOnce()).set(Matchers.any(Metric.class));
}
@Test @Test
public void defaultExporterWhenMessageChannelAvailable() throws Exception { public void defaultExporterWhenMessageChannelAvailable() throws Exception {
this.context = new AnnotationConfigApplicationContext( this.context = new AnnotationConfigApplicationContext(

Loading…
Cancel
Save