Skip to content

Commit b9db474

Browse files
author
Dave Syer
committed
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
1 parent de0f0ec commit b9db474

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package org.springframework.boot.actuate.metrics.export;
1818

19+
import java.io.Closeable;
20+
import java.io.Flushable;
21+
import java.io.IOException;
1922
import java.util.ArrayList;
2023
import java.util.Collection;
2124
import java.util.Collections;
@@ -36,7 +39,7 @@
3639
* @author Dave Syer
3740
* @since 1.3.0
3841
*/
39-
public abstract class AbstractMetricExporter implements Exporter {
42+
public abstract class AbstractMetricExporter implements Exporter, Closeable, Flushable {
4043

4144
private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class);
4245

@@ -143,6 +146,13 @@ private void flushQuietly() {
143146
}
144147
}
145148

149+
@Override
150+
public void close() throws IOException {
151+
export();
152+
flushQuietly();
153+
}
154+
155+
@Override
146156
public void flush() {
147157
}
148158

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricExporters.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
package org.springframework.boot.actuate.metrics.export;
1818

19+
import java.io.Closeable;
20+
import java.io.IOException;
1921
import java.util.HashMap;
22+
import java.util.HashSet;
2023
import java.util.Map;
2124
import java.util.Map.Entry;
25+
import java.util.Set;
2226

2327
import org.springframework.boot.actuate.metrics.reader.MetricReader;
2428
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
@@ -32,7 +36,7 @@
3236
* @author Dave Syer
3337
* @since 1.3.0
3438
*/
35-
public class MetricExporters implements SchedulingConfigurer {
39+
public class MetricExporters implements SchedulingConfigurer, Closeable {
3640

3741
private MetricReader reader;
3842

@@ -42,6 +46,8 @@ public class MetricExporters implements SchedulingConfigurer {
4246

4347
private final Map<String, Exporter> exporters = new HashMap<String, Exporter>();
4448

49+
private final Set<String> closeables = new HashSet<String>();
50+
4551
public MetricExporters(MetricExportProperties properties) {
4652
this.properties = properties;
4753
}
@@ -78,6 +84,7 @@ public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
7884
if (trigger != null) {
7985
MetricCopyExporter exporter = getExporter(writer, trigger);
8086
this.exporters.put(name, exporter);
87+
this.closeables.add(name);
8188
ExportRunner runner = new ExportRunner(exporter);
8289
IntervalTask task = new IntervalTask(runner, trigger.getDelayMillis(),
8390
trigger.getDelayMillis());
@@ -99,6 +106,16 @@ public Map<String, Exporter> getExporters() {
99106
return this.exporters;
100107
}
101108

109+
@Override
110+
public void close() throws IOException {
111+
for (String name : this.closeables) {
112+
Exporter exporter = this.exporters.get(name);
113+
if (exporter instanceof Closeable) {
114+
((Closeable) exporter).close();
115+
}
116+
}
117+
}
118+
102119
private static class ExportRunner implements Runnable {
103120

104121
private final Exporter exporter;

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/MetricExportAutoConfigurationTests.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ public void after() {
6868
}
6969
}
7070

71+
@Test
72+
public void metricsFlushAutomatically() throws Exception {
73+
this.context = new AnnotationConfigApplicationContext(WriterConfig.class,
74+
MetricRepositoryAutoConfiguration.class,
75+
MetricExportAutoConfiguration.class,
76+
PropertyPlaceholderAutoConfiguration.class);
77+
GaugeService gaugeService = this.context.getBean(GaugeService.class);
78+
assertNotNull(gaugeService);
79+
gaugeService.submit("foo", 2.7);
80+
MetricExporters flusher = this.context.getBean(MetricExporters.class);
81+
flusher.close(); // this will be called by Spring on shutdown
82+
MetricWriter writer = this.context.getBean("writer", MetricWriter.class);
83+
Mockito.verify(writer, Mockito.atLeastOnce()).set(Matchers.any(Metric.class));
84+
}
85+
7186
@Test
7287
public void defaultExporterWhenMessageChannelAvailable() throws Exception {
7388
this.context = new AnnotationConfigApplicationContext(

0 commit comments

Comments
 (0)