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
This commit is contained in:
Dave Syer 2016-04-22 09:09:05 +01:00
parent de0f0ecce4
commit b9db4742ac
3 changed files with 44 additions and 2 deletions

View File

@ -16,6 +16,9 @@
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.Collection;
import java.util.Collections;
@ -36,7 +39,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer
* @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);
@ -143,6 +146,13 @@ public abstract class AbstractMetricExporter implements Exporter {
}
}
@Override
public void close() throws IOException {
export();
flushQuietly();
}
@Override
public void flush() {
}

View File

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

View File

@ -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
public void defaultExporterWhenMessageChannelAvailable() throws Exception {
this.context = new AnnotationConfigApplicationContext(