Extract metric writers into a MetricExporters composite
Adds a separate exporter per MetricWriter and allows individual configuration of exporters for fine-grained control of schedules and patterns etc.
This commit is contained in:
parent
0ce72105b9
commit
2f2750e713
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
|
@ -30,15 +31,15 @@ import org.springframework.boot.actuate.metrics.buffer.CounterBuffers;
|
|||
import org.springframework.boot.actuate.metrics.buffer.GaugeBuffers;
|
||||
import org.springframework.boot.actuate.metrics.export.Exporter;
|
||||
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
|
||||
import org.springframework.boot.actuate.metrics.export.MetricExportProperties;
|
||||
import org.springframework.boot.actuate.metrics.export.MetricExporters;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.actuate.metrics.repository.InMemoryMetricRepository;
|
||||
import org.springframework.boot.actuate.metrics.repository.MetricRepository;
|
||||
import org.springframework.boot.actuate.metrics.writer.CompositeMetricWriter;
|
||||
import org.springframework.boot.actuate.metrics.writer.DefaultCounterService;
|
||||
import org.springframework.boot.actuate.metrics.writer.DefaultGaugeService;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.JavaVersion;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range;
|
||||
|
|
@ -50,7 +51,6 @@ import org.springframework.context.annotation.Configuration;
|
|||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ import com.codahale.metrics.MetricRegistry;
|
|||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(MetricsProperties.class)
|
||||
@EnableConfigurationProperties(MetricExportProperties.class)
|
||||
public class MetricRepositoryAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
|
|
@ -173,10 +173,10 @@ public class MetricRepositoryAutoConfiguration {
|
|||
static class DefaultMetricsExporterConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private List<MetricWriter> writers;
|
||||
private Map<String, MetricWriter> writers = Collections.emptyMap();
|
||||
|
||||
@Autowired
|
||||
private MetricsProperties metrics;
|
||||
private MetricExportProperties metrics;
|
||||
|
||||
@Autowired(required = false)
|
||||
@Qualifier("actuatorMetricRepository")
|
||||
|
|
@ -184,27 +184,19 @@ public class MetricRepositoryAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(MetricWriter.class)
|
||||
public MetricCopyExporter metricWritersMetricExporter(MetricReader reader) {
|
||||
List<MetricWriter> writers = new ArrayList<MetricWriter>(this.writers);
|
||||
public MetricExporters metricWritersMetricExporter(MetricReader reader) {
|
||||
Map<String, MetricWriter> writers = new HashMap<String, MetricWriter>(
|
||||
this.writers);
|
||||
if (this.actuatorMetricRepository != null
|
||||
&& writers.contains(this.actuatorMetricRepository)) {
|
||||
writers.remove(this.actuatorMetricRepository);
|
||||
}
|
||||
MetricCopyExporter exporter = new MetricCopyExporter(reader,
|
||||
new CompositeMetricWriter(writers)) {
|
||||
@Scheduled(fixedDelayString = "${spring.metrics.export.delayMillis:5000}")
|
||||
@Override
|
||||
public void export() {
|
||||
super.export();
|
||||
&& writers.containsValue(this.actuatorMetricRepository)) {
|
||||
for (String name : this.writers.keySet()) {
|
||||
if (writers.get(name).equals(this.actuatorMetricRepository)) {
|
||||
writers.remove(name);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.metrics.getExport().getIncludes() != null
|
||||
|| this.metrics.getExport().getExcludes() != null) {
|
||||
exporter.setIncludes(this.metrics.getExport().getIncludes());
|
||||
exporter.setExcludes(this.metrics.getExport().getExcludes());
|
||||
}
|
||||
return exporter;
|
||||
MetricExporters exporters = new MetricExporters(reader, writers, this.metrics);
|
||||
return exporters;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,28 +14,112 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.autoconfigure;
|
||||
package org.springframework.boot.actuate.metrics.export;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties("spring.metrics")
|
||||
public class MetricsProperties {
|
||||
@ConfigurationProperties("spring.metrics.export")
|
||||
public class MetricExportProperties {
|
||||
|
||||
/**
|
||||
* Flag to disable all metric exports (assuming any MetricWriters are available).
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
private Export export = new Export();
|
||||
|
||||
public Export getExport() {
|
||||
private Map<String, Export> writers = new LinkedHashMap<String, Export>();
|
||||
|
||||
/**
|
||||
* Default values for trigger configuration for all writers. Can also be set by
|
||||
* including a writer with <code>name="*"</code>.
|
||||
*
|
||||
* @return the default trigger configuration
|
||||
*/
|
||||
public Export getDefault() {
|
||||
return this.export;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for triggers on individual named writers. Each value can individually
|
||||
* specify a name pattern explicitly, or else the map key will be used if the name is
|
||||
* not set.
|
||||
*
|
||||
* @return the writers
|
||||
*/
|
||||
public Map<String, Export> getWriters() {
|
||||
return this.writers;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void setDefaults() {
|
||||
Export defaults = null;
|
||||
for (Entry<String, Export> entry : this.writers.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Export value = entry.getValue();
|
||||
if (value.getNames() == null || value.getNames().length == 0) {
|
||||
value.setNames(new String[] { key });
|
||||
}
|
||||
if (Arrays.asList(value.getNames()).contains("*")) {
|
||||
defaults = value;
|
||||
}
|
||||
}
|
||||
if (defaults == null) {
|
||||
this.export.setNames(new String[] { "*" });
|
||||
this.writers.put("*", this.export);
|
||||
defaults = this.export;
|
||||
}
|
||||
if (defaults.isIgnoreTimestamps() == null) {
|
||||
defaults.setIgnoreTimestamps(false);
|
||||
}
|
||||
if (defaults.isSendLatest() == null) {
|
||||
defaults.setSendLatest(true);
|
||||
}
|
||||
if (defaults.getDelayMillis() == null) {
|
||||
defaults.setDelayMillis(5000);
|
||||
}
|
||||
for (Export value : this.writers.values()) {
|
||||
if (value.isIgnoreTimestamps() == null) {
|
||||
value.setIgnoreTimestamps(false);
|
||||
}
|
||||
if (value.isSendLatest() == null) {
|
||||
value.setSendLatest(true);
|
||||
}
|
||||
if (value.getDelayMillis() == null) {
|
||||
value.setDelayMillis(5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public static class Export {
|
||||
/**
|
||||
* Names (or patterns) for bean names that this configuration applies to.
|
||||
*/
|
||||
private String[] names;
|
||||
/**
|
||||
* Delay in milliseconds between export ticks. Metrics are exported to external
|
||||
* sources on a schedule with this delay.
|
||||
*/
|
||||
private long delayMillis;
|
||||
private Long delayMillis;
|
||||
|
||||
/**
|
||||
* Flag to enable metric export (assuming a MetricWriter is available).
|
||||
|
|
@ -46,12 +130,26 @@ public class MetricsProperties {
|
|||
* Flag to switch off any available optimizations based on not exporting unchanged
|
||||
* metric values.
|
||||
*/
|
||||
private boolean ignoreTimestamps = false;
|
||||
private Boolean sendLatest;
|
||||
|
||||
/**
|
||||
* Flag to ignore timestamps completely. If true, send all metrics all the time,
|
||||
* including ones that haven't changed since startup.
|
||||
*/
|
||||
private Boolean ignoreTimestamps;
|
||||
|
||||
private String[] includes;
|
||||
|
||||
private String[] excludes;
|
||||
|
||||
public String[] getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
public void setNames(String[] names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
public String[] getIncludes() {
|
||||
return this.includes;
|
||||
}
|
||||
|
|
@ -60,14 +158,14 @@ public class MetricsProperties {
|
|||
this.includes = includes;
|
||||
}
|
||||
|
||||
public String[] getExcludes() {
|
||||
return this.excludes;
|
||||
}
|
||||
|
||||
public void setExcludes(String[] excludes) {
|
||||
this.excludes = excludes;
|
||||
}
|
||||
|
||||
public String[] getExcludes() {
|
||||
return this.excludes;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
|
@ -76,7 +174,7 @@ public class MetricsProperties {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public long getDelayMillis() {
|
||||
public Long getDelayMillis() {
|
||||
return this.delayMillis;
|
||||
}
|
||||
|
||||
|
|
@ -84,12 +182,34 @@ public class MetricsProperties {
|
|||
this.delayMillis = delayMillis;
|
||||
}
|
||||
|
||||
public boolean isIgnoreTimestamps() {
|
||||
public Boolean isIgnoreTimestamps() {
|
||||
return this.ignoreTimestamps;
|
||||
}
|
||||
|
||||
public void setIgnoreTimestamps(boolean ignoreTimestamps) {
|
||||
this.ignoreTimestamps = ignoreTimestamps;
|
||||
}
|
||||
|
||||
public Boolean isSendLatest() {
|
||||
return this.sendLatest;
|
||||
}
|
||||
|
||||
public void setSendLatest(boolean sendLatest) {
|
||||
this.sendLatest = sendLatest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a matching trigger configuration.
|
||||
* @param name the bean name to match
|
||||
* @return a matching configuration if there is one
|
||||
*/
|
||||
public Export findExport(String name) {
|
||||
for (Export value : this.writers.values()) {
|
||||
if (PatternMatchUtils.simpleMatch(value.getNames(), name)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.actuate.metrics.export;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.export.MetricExportProperties.Export;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.config.IntervalTask;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class MetricExporters implements SchedulingConfigurer {
|
||||
|
||||
private MetricExportProperties export;
|
||||
|
||||
private Map<String, MetricWriter> writers;
|
||||
|
||||
private Map<String, Exporter> exporters = new HashMap<String, Exporter>();
|
||||
|
||||
private MetricReader reader;
|
||||
|
||||
public MetricExporters(MetricReader reader, Map<String, MetricWriter> writers,
|
||||
MetricExportProperties export) {
|
||||
this.reader = reader;
|
||||
this.export = export;
|
||||
this.writers = writers;
|
||||
}
|
||||
|
||||
public Map<String, Exporter> getExporters() {
|
||||
return this.exporters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
|
||||
for (Entry<String, MetricWriter> entry : this.writers.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Export trigger = this.export.findExport(name);
|
||||
|
||||
if (trigger != null) {
|
||||
|
||||
MetricWriter writer = entry.getValue();
|
||||
final MetricCopyExporter exporter = new MetricCopyExporter(this.reader,
|
||||
writer);
|
||||
if (trigger.getIncludes() != null || trigger.getExcludes() != null) {
|
||||
exporter.setIncludes(trigger.getIncludes());
|
||||
exporter.setExcludes(trigger.getExcludes());
|
||||
}
|
||||
exporter.setIgnoreTimestamps(trigger.isIgnoreTimestamps());
|
||||
exporter.setSendLatest(trigger.isSendLatest());
|
||||
|
||||
this.exporters.put(name, exporter);
|
||||
|
||||
taskRegistrar.addFixedDelayTask(new IntervalTask(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
exporter.export();
|
||||
}
|
||||
}, trigger.getDelayMillis(), trigger.getDelayMillis()));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ import org.springframework.boot.actuate.metrics.buffer.BufferCounterService;
|
|||
import org.springframework.boot.actuate.metrics.buffer.BufferGaugeService;
|
||||
import org.springframework.boot.actuate.metrics.dropwizard.DropwizardMetricServices;
|
||||
import org.springframework.boot.actuate.metrics.export.MetricCopyExporter;
|
||||
import org.springframework.boot.actuate.metrics.export.MetricExporters;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
import org.springframework.boot.actuate.metrics.reader.PrefixMetricReader;
|
||||
import org.springframework.boot.actuate.metrics.writer.MetricWriter;
|
||||
|
|
@ -86,7 +87,7 @@ public class MetricRepositoryAutoConfigurationTests {
|
|||
MessageChannelConfiguration.class, MetricsChannelAutoConfiguration.class,
|
||||
MetricRepositoryAutoConfiguration.class,
|
||||
PropertyPlaceholderAutoConfiguration.class);
|
||||
MetricCopyExporter exporter = this.context.getBean(MetricCopyExporter.class);
|
||||
MetricExporters exporter = this.context.getBean(MetricExporters.class);
|
||||
assertNotNull(exporter);
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +99,9 @@ public class MetricRepositoryAutoConfigurationTests {
|
|||
GaugeService gaugeService = this.context.getBean(GaugeService.class);
|
||||
assertNotNull(gaugeService);
|
||||
gaugeService.submit("foo", 2.7);
|
||||
MetricCopyExporter exporter = this.context.getBean(MetricCopyExporter.class);
|
||||
MetricExporters exporters = this.context.getBean(MetricExporters.class);
|
||||
MetricCopyExporter exporter = (MetricCopyExporter) exporters.getExporters().get(
|
||||
"writer");
|
||||
exporter.setIgnoreTimestamps(true);
|
||||
exporter.export();
|
||||
MetricWriter writer = this.context.getBean("writer", MetricWriter.class);
|
||||
|
|
|
|||
Loading…
Reference in New Issue