At the moment Spring Boot does not have an Activator integration for InfluxDB and the TICK stack. It is being tracked as an enhancement as Issue 5688 and is currently scheduled for Spring Boot 1.5.0.
Until there is an out-of-the-box implementation, this article explains what you need to do to get a Spring Boot application talking to InfluxDB database, using version 1.4.1.RELEASE
of Boot.
The Writer is adapted from the commits referenced in that issue tracker, but the tests and the property-based configuration has been omitted for clarity.
Assumptions
You need a Spring Boot application with the Actuator (spring-boot-starter-actuator
) enabled, and, at the very least a working copy of InfluxDB, although running Chronograf is pretty much required for looking at the data in any meaningful way.
I’ve only included code and import statements for the bits that are key to the example. You’ll need to fill in the rest.
This approach will not work if you’re using the Dropwizard metrics capability within Spring Boot. That is automatically enabled if the Dropwizard metrics dependency io.dropwizard.metrics:metrics-core
has been added to your build.
Dependencies
The only new dependency is to add the Java InfluxDB client. For Gradle add the following:
compile 'org.influxdb:influxdb-java:2.4'
Spring Bean Configuration
There are two pieces of Spring configuration needed to make this work.
MetricsWriter
To get Spring to write metrics to Influx, you need to add an ExportMetricsWriter bean to your application config class, like in this example:
import org.influxdb.InfluxDB;
import org.influxdb.InfluxDBFactory;
import org.springframework.boot.actuate.autoconfigure.ExportMetricWriter;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
...
@Bean
@ExportMetricWriter
GaugeWriter influxMetricsWriter() {
InfluxDB influxDB = InfluxDBFactory.connect( "http://localhost:8086",
"root",
"root");
String dbName = "myMetricsDB"; // the name of the datastore you choose
influxDB.createDatabase( dbName);
InfluxDBMetricWriter.Builder builder = new InfluxDBMetricWriter.Builder( influxDB);
builder.databaseName( dbName);
builder.batchActions( 500); // number of points for batch before data is sent to Influx
return builder.build();
}
Clearly in a production implementation, the database URL, username, password and datastore name would be made configurable.
Getting all JVM metrics
The configuration above will get bespoke metrics, but not the default JVM values, which can be very useful. In order to copy the PublicMetrics
data to the writer, a second bean needs to be declared:
@Bean
public MetricsEndpointMetricReader metricsEndpointMetricReader(MetricsEndpoint metricsEndpoint) {
return new MetricsEndpointMetricReader(metricsEndpoint);
}
Writer class Implementation
This is adapted from the implementation in the issue tracker, with the only fix being to send the data with null
as the InfluxDB Retention Policy, as the original value caused certain value names to cause the data to be rejected by Influx.
This also uses Lombok to reduce the boilerplate Java in the example. The Lombok @Builder
annotation wan’t used as it is a different Builder pattern from the one generated by Lombok.
import java.util.concurrent.TimeUnit;
import org.influxdb.InfluxDB;
import org.influxdb.dto.Point;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.writer.GaugeWriter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
public class InfluxDBMetricWriter implements GaugeWriter {
private static final String DEFAULT_DATABASE_NAME = "metrics";
private static final int DEFAULT_BATCH_ACTIONS = 500;
private static final int DEFAULT_FLUSH_DURATION = 30;
private final InfluxDB influxDB;
private final String databaseName;
private InfluxDBMetricWriter(Builder builder) {
this.influxDB = builder.influxDB;
this.databaseName = builder.databaseName;
this.influxDB.createDatabase( this.databaseName);
this.influxDB.enableBatch( builder.batchActions, builder.flushDuration,
builder.flushDurationTimeUnit);
this.influxDB.setLogLevel( builder.logLevel);
}
@Override
public void set(Metric<?> value) {
Point point = Point.measurement( value.getName())
.time( value.getTimestamp().getTime(), TimeUnit.MILLISECONDS)
.addField( "value", value.getValue())
.build();
this.influxDB.write( this.databaseName, null, point);
}
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor
public static class Builder {
@NonNull private final InfluxDB influxDB;
@Setter private String databaseName = DEFAULT_DATABASE_NAME;
@Setter private int batchActions = DEFAULT_BATCH_ACTIONS;
private int flushDuration = DEFAULT_FLUSH_DURATION;
private TimeUnit flushDurationTimeUnit = TimeUnit.SECONDS;
@Setter private InfluxDB.LogLevel logLevel = InfluxDB.LogLevel.BASIC;
public Builder flushDuration( int flushDuration, TimeUnit flushDurationTimeUnit) {
this.flushDuration = flushDuration;
this.flushDurationTimeUnit = flushDurationTimeUnit;
return this;
}
public InfluxDBMetricWriter build() {
return new InfluxDBMetricWriter(this);
}
}
}