diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java index bb7188c626..4992c6c76a 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/MetricRepositoryAutoConfiguration.java @@ -134,6 +134,7 @@ public class MetricRepositoryAutoConfiguration { } @Bean + @Primary @ConditionalOnMissingBean public BufferMetricReader metricReader(CounterBuffers counters, GaugeBuffers gauges) { @@ -181,7 +182,7 @@ public class MetricRepositoryAutoConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnBean(MetricWriter.class) - public MetricCopyExporter messageChannelMetricExporter(MetricReader reader) { + public MetricCopyExporter metricWritersMetricExporter(MetricReader reader) { List writers = new ArrayList(this.writers); if (this.actuatorMetricRepository != null && writers.contains(this.actuatorMetricRepository)) { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java index e030657b29..2a265ae511 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/AbstractMetricExporter.java @@ -22,6 +22,8 @@ import java.util.Collections; import java.util.Date; import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.util.StringUtils; @@ -34,6 +36,8 @@ import org.springframework.util.StringUtils; */ public abstract class AbstractMetricExporter implements Exporter { + private static final Log logger = LogFactory.getLog(AbstractMetricExporter.class); + private volatile AtomicBoolean processing = new AtomicBoolean(false); private Date earliestTimestamp = new Date(); @@ -86,11 +90,25 @@ public abstract class AbstractMetricExporter implements Exporter { } } } + catch (Exception e) { + logger.warn("Could not write to MetricWriter: " + e.getClass() + ": " + + e.getMessage()); + } finally { + try { + flush(); + } + catch (Exception e) { + logger.warn("Could not flush MetricWriter: " + e.getClass() + ": " + + e.getMessage()); + } this.processing.set(false); } } + public void flush() { + } + /** * Generate a group of metrics to iterate over in the form of a set of Strings (e.g. * prefixes). If the metrics to be exported partition into groups identified by a diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java index b218eef9f4..bbb3aec207 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/export/MetricCopyExporter.java @@ -22,6 +22,7 @@ import java.util.Iterator; import org.springframework.boot.actuate.metrics.Metric; import org.springframework.boot.actuate.metrics.reader.MetricReader; import org.springframework.boot.actuate.metrics.writer.MetricWriter; +import org.springframework.boot.actuate.metrics.writer.WriterUtils; import org.springframework.util.PatternMatchUtils; /** @@ -79,6 +80,11 @@ public class MetricCopyExporter extends AbstractMetricExporter { } } + @Override + public void flush() { + WriterUtils.flush(this.writer); + } + private class PatternMatchingIterator implements Iterator> { private Metric buffer = null; diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java new file mode 100644 index 0000000000..9b52ad54a1 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/DefaultOpenTsdbNamingStrategy.java @@ -0,0 +1,68 @@ +/* + * 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.opentsdb; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.springframework.util.ObjectUtils; + +/** + * A naming strategy that just passes through the metric name, together with tags from a + * set of static values. Open TSDB requires at least one tag, so one is always added for + * you: the {@value #PREFIX_KEY} key is added with a unique value "spring.X" where X is an + * object hash code ID for this (the naming stategy). In most cases this will be unique + * enough to allow aggregation of the underlying metrics in Open TSDB, but normally it is + * best to provide your own tags, including a prefix if you know one (overwriting the + * default). + * + * @author Dave Syer + */ +public class DefaultOpenTsdbNamingStrategy implements OpenTsdbNamingStrategy { + + public static final String PREFIX_KEY = "prefix"; + + /** + * Tags to apply to every metric. Open TSDB requires at least one tag, so a "prefix" + * tag is added for you by default. + */ + private Map tags = new LinkedHashMap(); + + private Map cache = new HashMap(); + + public DefaultOpenTsdbNamingStrategy() { + this.tags.put(PREFIX_KEY, + "spring." + ObjectUtils.getIdentityHexString(this)); + } + + public void setTags(Map staticTags) { + this.tags.putAll(staticTags); + } + + @Override + public OpenTsdbName getName(String name) { + if (this.cache.containsKey(name)) { + return this.cache.get(name); + } + OpenTsdbName value = new OpenTsdbName(name); + value.setTags(this.tags); + this.cache.put(name, value); + return value; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java new file mode 100644 index 0000000000..8e660f8300 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbData.java @@ -0,0 +1,82 @@ +/* + * 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.opentsdb; + +import java.util.Map; + +/** + * @author Dave Syer + */ +public class OpenTsdbData { + + private OpenTsdbName name; + + private Long timestamp; + + private Number value; + + protected OpenTsdbData() { + this.name = new OpenTsdbName(); + } + + public OpenTsdbData(String metric, Number value) { + this(metric, value, System.currentTimeMillis()); + } + + public OpenTsdbData(String metric, Number value, Long timestamp) { + this(new OpenTsdbName(metric), value, timestamp); + } + + public OpenTsdbData(OpenTsdbName name, Number value, Long timestamp) { + this.name = name; + this.value = value; + this.timestamp = timestamp; + } + + public String getMetric() { + return this.name.getMetric(); + } + + public void setMetric(String metric) { + this.name.setMetric(metric); + } + + public Long getTimestamp() { + return this.timestamp; + } + + public void setTimestamp(Long timestamp) { + this.timestamp = timestamp; + } + + public Number getValue() { + return this.value; + } + + public void setValue(Number value) { + this.value = value; + } + + public Map getTags() { + return this.name.getTags(); + } + + public void setTags(Map tags) { + this.name.setTags(tags); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbHttpMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbHttpMetricWriter.java new file mode 100644 index 0000000000..bd29f8a6e9 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbHttpMetricWriter.java @@ -0,0 +1,143 @@ +/* + * 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.opentsdb; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.actuate.metrics.Metric; +import org.springframework.boot.actuate.metrics.writer.Delta; +import org.springframework.boot.actuate.metrics.writer.MetricWriter; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.web.client.RestTemplate; + +/** + * A {@link MetricWriter} for the Open TSDB database (version 2.0), writing metrics to the + * HTTP endpoint provided by the server. Data are buffered according to the + * {@link #setBufferSize(int) bufferSize} property, and only flushed automatically when + * the buffer size is reached. Users should either manually {@link #flush()} after writing + * a batch of data if that makes sense, or consider adding a {@link Scheduled + * @Scheduled} task to flush periodically. + * + * @author Dave Syer + */ +public class OpenTsdbHttpMetricWriter implements MetricWriter { + + private static final Log logger = LogFactory.getLog(OpenTsdbHttpMetricWriter.class); + + private RestTemplate restTemplate = new RestTemplate(); + + /** + * URL for POSTing data. Defaults to http://localhost:4242/api/put. + */ + private String url = "http://localhost:4242/api/put"; + + /** + * Buffer size to fill before posting data to server. + */ + private int bufferSize = 64; + + /** + * The media type to use to serialize and accept responses from the server. Defaults + * to "application/json". + */ + private MediaType mediaType = MediaType.APPLICATION_JSON; + + private List buffer = new ArrayList(this.bufferSize); + + private OpenTsdbNamingStrategy namingStrategy = new DefaultOpenTsdbNamingStrategy(); + + public RestTemplate getRestTemplate() { + return this.restTemplate; + } + + public void setRestTemplate(RestTemplate restTemplate) { + this.restTemplate = restTemplate; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setBufferSize(int bufferSize) { + this.bufferSize = bufferSize; + } + + public void setMediaType(MediaType mediaType) { + this.mediaType = mediaType; + } + + public void setNamingStrategy(OpenTsdbNamingStrategy namingStrategy) { + this.namingStrategy = namingStrategy; + } + + @Override + public void increment(Delta delta) { + throw new UnsupportedOperationException("Counters not supported via increment"); + } + + @Override + public void set(Metric value) { + OpenTsdbData data = new OpenTsdbData( + this.namingStrategy.getName(value.getName()), value.getValue(), value + .getTimestamp().getTime()); + this.buffer.add(data); + if (this.buffer.size() >= this.bufferSize) { + flush(); + } + } + + /** + * Flush the buffer without waiting for it to fill any further. + */ + public void flush() { + if (this.buffer.isEmpty()) { + return; + } + List temp = new ArrayList(); + synchronized (this.buffer) { + temp.addAll(this.buffer); + this.buffer.clear(); + } + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Arrays.asList(this.mediaType)); + headers.setContentType(this.mediaType); + HttpEntity> request = new HttpEntity>(temp, + headers); + @SuppressWarnings("rawtypes") + ResponseEntity response = this.restTemplate.postForEntity(this.url, request, + Map.class); + if (!response.getStatusCode().is2xxSuccessful()) { + logger.warn("Cannot write metrics (discarded " + temp.size() + " values): " + + response.getBody()); + } + } + + @Override + public void reset(String metricName) { + set(new Metric(metricName, 0L)); + } + +} \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java new file mode 100644 index 0000000000..228c58651b --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbName.java @@ -0,0 +1,58 @@ +/* + * 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.opentsdb; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author Dave Syer + */ +public class OpenTsdbName { + + private String metric; + + private Map tags = new LinkedHashMap(); + + protected OpenTsdbName() { + } + + public OpenTsdbName(String metric) { + this.metric = metric; + } + + public String getMetric() { + return this.metric; + } + + public void setMetric(String metric) { + this.metric = metric; + } + + public Map getTags() { + return this.tags; + } + + public void setTags(Map tags) { + this.tags.putAll(tags); + } + + public void tag(String name, String value) { + this.tags.put(name, value); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java new file mode 100644 index 0000000000..c3bebf7eb4 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/OpenTsdbNamingStrategy.java @@ -0,0 +1,26 @@ +/* + * 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.opentsdb; + +/** + * @author Dave Syer + */ +public interface OpenTsdbNamingStrategy { + + OpenTsdbName getName(String metricName); + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java new file mode 100644 index 0000000000..defed376b0 --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/opentsdb/package-info.java @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * Metrics integration with OpenTSDB. + */ +package org.springframework.boot.actuate.metrics.opentsdb; + diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java index a34b02a3f0..b84115fd2e 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/CompositeMetricWriter.java @@ -18,6 +18,7 @@ package org.springframework.boot.actuate.metrics.writer; import java.util.ArrayList; import java.util.Collections; +import java.util.Iterator; import java.util.List; import org.springframework.boot.actuate.metrics.Metric; @@ -28,7 +29,7 @@ import org.springframework.boot.actuate.metrics.Metric; * * @author Dave Syer */ -public class CompositeMetricWriter implements MetricWriter { +public class CompositeMetricWriter implements MetricWriter, Iterable { private final List writers = new ArrayList(); @@ -40,6 +41,11 @@ public class CompositeMetricWriter implements MetricWriter { this.writers.addAll(writers); } + @Override + public Iterator iterator() { + return this.writers.iterator(); + } + @Override public void increment(Delta delta) { for (MetricWriter writer : this.writers) { diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/WriterUtils.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/WriterUtils.java new file mode 100644 index 0000000000..8ae662387c --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/writer/WriterUtils.java @@ -0,0 +1,59 @@ +/* + * 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.writer; + +import java.io.Flushable; +import java.lang.reflect.Method; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.boot.actuate.metrics.export.MetricCopyExporter; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; + +/** + * @author Dave Syer + */ +public class WriterUtils { + + private static final Log logger = LogFactory.getLog(MetricCopyExporter.class); + + public static void flush(MetricWriter writer) { + if (writer instanceof CompositeMetricWriter) { + for (MetricWriter element : (CompositeMetricWriter) writer) { + flush(element); + } + } + try { + if (ClassUtils.isPresent("java.io.Flushable", null)) { + if (writer instanceof Flushable) { + ((Flushable) writer).flush(); + return; + } + } + Method method = ReflectionUtils.findMethod(writer.getClass(), "flush"); + if (method != null) { + ReflectionUtils.invokeMethod(method, writer); + } + } + catch (Exception e) { + logger.warn("Could not flush MetricWriter: " + e.getClass() + ": " + + e.getMessage()); + } + } + +} diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index f0b9e0c15a..69d1dc09e5 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -957,6 +957,38 @@ MetricWriter metricWriter() { +[[production-ready-metric-writers-export-to-open-tdsb]] +==== Example: Export to Open TSDB +If you provide a `@Bean` of type `OpenTsdbHttpMetricWriter` the metrics are exported to +http://opentsdb.net/[Open TSDB] for aggregation. The `OpenTsdbHttpMetricWriter` has a +`url` property that you need to set to the Open TSDB "/put" endpoint, e.g. +`http://localhost:4242/api/put`). It also has a `namingStrategy` that you can customize +or configure to make the metrics match the data structure you need on the server. By +default it just passes through the metric name as an Open TSDB metric name and adds a tag +"prefix" with value "spring.X" where "X" is the object hash of the default naming +strategy. Thus, after running the application and generating some metrics (e.g. by pinging +the home page) you can inspect the metrics in the TDB UI (http://localhost:4242 by +default). Example: + +---- +curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root +[ + { + "metric": "counter.status.200.root", + "tags": { + "prefix": "spring.b968a76" + }, + "aggregateTags": [], + "dps": { + "1430492872": 2, + "1430492875": 6 + } + } +] +---- + + + [[production-ready-metric-writers-export-to-statsd]] ==== Example: Export to Statsd If you provide a `@Bean` of type `StatsdMetricWriter` the metrics are exported to a @@ -975,7 +1007,7 @@ private int port; @Bean MetricWriter metricWriter() { - return new StatsdMetricWriter(prefix, host, port); + return new StatsdMetricWriter(prefix, host, port); } ---- @@ -991,7 +1023,7 @@ tool that understands JMX (e.g. JConsole or JVisualVM). Example: ---- @Bean MetricWriter metricWriter(MBeanExporter exporter) { - return new JmxMetricWriter(exporter); + return new JmxMetricWriter(exporter); } ---- diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index 0c7e18ef66..7edc05fae0 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -52,6 +52,7 @@ spring-boot-sample-jta-bitronix spring-boot-sample-jta-jndi spring-boot-sample-liquibase + spring-boot-sample-metrics-opentsdb spring-boot-sample-metrics-redis spring-boot-sample-parent-context spring-boot-sample-profile diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc new file mode 100644 index 0000000000..f6d5b1a838 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/README.adoc @@ -0,0 +1,29 @@ +Spring Boot sample with Open TSDB export for metrics. + +Start opentsdb, e.g. with [Docker Compose]() + +[source,indent=0] +---- +$ docker-compose up +---- + +Run the app and ping the home page (http://localhost:8080) a few times. Go and look at +the result in the TDB UI, e.g. + +[source,indent=0] +---- + $ curl localhost:4242/api/query?start=1h-ago&m=max:counter.status.200.root + [ + { + "metric": "counter.status.200.root", + "tags": { + "prefix": "spring.b968a76" + }, + "aggregateTags": [], + "dps": { + "1430492872": 2, + "1430492875": 6 + } + } + ] +---- \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml new file mode 100644 index 0000000000..c9d453bd7c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/docker-compose.yml @@ -0,0 +1,4 @@ +opentsdb: + image: lancope/opentsdb + ports: + - "4242:4242" diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml new file mode 100644 index 0000000000..eb20ebffc5 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 1.3.0.BUILD-SNAPSHOT + + spring-boot-sample-metrics-opentsdb + spring-boot-sample-metrics-opentsdb + Spring Boot Actuator Sample + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldService.java new file mode 100644 index 0000000000..e4bcc96cae --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/HelloWorldService.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2013 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 sample.metrics.opentsdb; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false) +public class HelloWorldService { + + private String name = "World"; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHelloMessage() { + return "Hello " + this.name; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java new file mode 100644 index 0000000000..d4f36f55e9 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleController.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012-2013 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 sample.metrics.opentsdb; + +import java.util.Collections; +import java.util.Map; + +import org.hibernate.validator.constraints.NotBlank; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Description; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@Description("A controller for handling requests for hello messages") +public class SampleController { + + @Autowired + private HelloWorldService helloWorldService; + + @RequestMapping(value = "/", method = RequestMethod.GET) + @ResponseBody + public Map hello() { + return Collections.singletonMap("message", + this.helloWorldService.getHelloMessage()); + } + + protected static class Message { + + @NotBlank(message = "Message value cannot be empty") + private String value; + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java new file mode 100644 index 0000000000..81376fd64e --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplication.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2013 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 sample.metrics.opentsdb; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.actuate.metrics.opentsdb.DefaultOpenTsdbNamingStrategy; +import org.springframework.boot.actuate.metrics.opentsdb.OpenTsdbHttpMetricWriter; +import org.springframework.boot.actuate.metrics.opentsdb.OpenTsdbNamingStrategy; +import org.springframework.boot.actuate.metrics.writer.MetricWriter; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; + +@SpringBootApplication +public class SampleOpenTsdbExportApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleOpenTsdbExportApplication.class, args); + } + + @Bean + @ConfigurationProperties("metrics.export") + public MetricWriter openTsdbMetricWriter() { + OpenTsdbHttpMetricWriter writer = new OpenTsdbHttpMetricWriter(); + writer.setNamingStrategy(namingStrategy()); + return writer; + } + + @Bean + @ConfigurationProperties("metrics.names") + public OpenTsdbNamingStrategy namingStrategy() { + return new DefaultOpenTsdbNamingStrategy(); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties new file mode 100644 index 0000000000..80dfb9df52 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/main/resources/application.properties @@ -0,0 +1 @@ +service.name: Phil \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java new file mode 100644 index 0000000000..8a2f70cdb1 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-metrics-opentsdb/src/test/java/sample/metrics/opentsdb/SampleOpenTsdbExportApplicationTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2014 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 sample.metrics.opentsdb; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.IntegrationTest; +import org.springframework.boot.test.SpringApplicationConfiguration; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; + +/** + * Basic integration tests for {@link SampleOpenTsdbExportApplication}. + * + * @author Dave Syer + */ +@RunWith(SpringJUnit4ClassRunner.class) +@SpringApplicationConfiguration(classes = SampleOpenTsdbExportApplication.class) +@WebAppConfiguration +@IntegrationTest("server.port=0") +@DirtiesContext +public class SampleOpenTsdbExportApplicationTests { + + @Test + public void contextLoads() { + + } + +}