Polish Elasticsearch health indicator

- Nest the configuration class in HealthIndicatorAutoConfiguration,
   bringing it into line with the other health indicator configuration
   classes
 - Include the statistics from the response in the health’s details
 - Map YELLOW to UP rather than UNKNOWN as it indicates that the cluster
   is running but that “the primary shard is allocated but replicas are
   not” [1]. The details can be used to determine the precise state of
   the cluster.
 - Add a property to configure the time that the health indicator will
   wait to receive a response from the cluster
 - Document the configuration properties
 - Update the tests to cover the updated functionality

See gh-2399

[1] http://www.elastic.co/guide/en/elasticsearch/reference/1.x/cluster-health.html
This commit is contained in:
Andy Wilkinson
2015-04-09 10:08:20 +01:00
parent 58c8c2ccdc
commit 03b109a2c8
12 changed files with 403 additions and 209 deletions

View File

@@ -1,55 +0,0 @@
/*
* Copyright 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 org.springframework.boot.actuate.autoconfigure;
import org.elasticsearch.client.Client;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicatorProperties;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} for
* {@link org.springframework.boot.actuate.health.ElasticsearchHealthIndicator}.
*
* @author Binwei Yang
* @since 1.2.2
*/
@Configuration
@AutoConfigureBefore({EndpointAutoConfiguration.class})
@AutoConfigureAfter({HealthIndicatorAutoConfiguration.class})
@ConditionalOnProperty(prefix = "management.health.elasticsearch", name = "enabled", matchIfMissing = true)
public class ElasticsearchHealthIndicatorConfiguration {
@Bean
@ConditionalOnBean(Client.class)
@ConditionalOnMissingBean(name = "elasticsearchHealthIndicator")
public HealthIndicator elasticsearchHealthIndicator() {
return new ElasticsearchHealthIndicator();
}
@Bean
public ElasticsearchHealthIndicatorProperties elasticsearchHealthIndicatorProperties() {
return new ElasticsearchHealthIndicatorProperties();
}
}

View File

@@ -23,6 +23,7 @@ import javax.jms.ConnectionFactory;
import javax.sql.DataSource;
import org.apache.solr.client.solrj.SolrServer;
import org.elasticsearch.client.Client;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
@@ -31,6 +32,8 @@ import org.springframework.boot.actuate.health.CompositeHealthIndicator;
import org.springframework.boot.actuate.health.DataSourceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicator;
import org.springframework.boot.actuate.health.DiskSpaceHealthIndicatorProperties;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicator;
import org.springframework.boot.actuate.health.ElasticsearchHealthIndicatorProperties;
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.actuate.health.JmsHealthIndicator;
@@ -47,6 +50,7 @@ import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadata;
import org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvider;
@@ -79,7 +83,8 @@ import org.springframework.mail.javamail.JavaMailSenderImpl;
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MongoAutoConfiguration.class,
MongoDataAutoConfiguration.class, RedisAutoConfiguration.class,
RabbitAutoConfiguration.class, SolrAutoConfiguration.class,
MailSenderAutoConfiguration.class, JmsAutoConfiguration.class })
MailSenderAutoConfiguration.class, JmsAutoConfiguration.class,
ElasticsearchAutoConfiguration.class })
@EnableConfigurationProperties({ HealthIndicatorAutoConfigurationProperties.class })
public class HealthIndicatorAutoConfiguration {
@@ -306,4 +311,30 @@ public class HealthIndicatorAutoConfiguration {
}
@Configuration
@ConditionalOnBean(Client.class)
@ConditionalOnProperty(prefix = "management.health.elasticsearch", name = "enabled", matchIfMissing = true)
@EnableConfigurationProperties(ElasticsearchHealthIndicatorProperties.class)
public static class ElasticsearchHealthIndicatorConfiguration extends
CompositeHealthIndicatorConfiguration<ElasticsearchHealthIndicator, Client> {
@Autowired
private Map<String, Client> clients;
@Autowired
private ElasticsearchHealthIndicatorProperties properties;
@Bean
@ConditionalOnMissingBean(name = "elasticsearchHealthIndicator")
public HealthIndicator elasticsearchHealthIndicator() {
return createHealthIndicator(this.clients);
}
@Override
protected ElasticsearchHealthIndicator createHealthIndicator(Client client) {
return new ElasticsearchHealthIndicator(client, this.properties);
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* 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.
@@ -16,47 +16,58 @@
package org.springframework.boot.actuate.health;
import java.util.List;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Simple implementation of a {@link HealthIndicator} returning health status information for
* ElasticSearch cluster.
* {@link HealthIndicator} for an Elasticsearch cluster.
*
* @author Binwei Yang
* @since 1.2.2
* @author Andy Wilkinson
* @since 1.3.0
*/
public class ElasticsearchHealthIndicator extends ApplicationHealthIndicator {
@Autowired
private Client client;
public class ElasticsearchHealthIndicator extends AbstractHealthIndicator {
@Autowired
private ElasticsearchHealthIndicatorProperties properties;
private final Client client;
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
try {
ClusterHealthResponse response = client.admin().cluster().health(Requests.clusterHealthRequest(
properties.getIndexNamesAsArray()
)).actionGet(100);
private final ElasticsearchHealthIndicatorProperties properties;
switch (response.getStatus()) {
case GREEN:
builder.up();
break;
case RED:
builder.down();
break;
case YELLOW:
default:
builder.unknown();
break;
}
builder.withDetail("clusterHealth", response);
} catch (Exception handled) {
builder.unknown().withDetail("exception", handled);
}
}
public ElasticsearchHealthIndicator(Client client,
ElasticsearchHealthIndicatorProperties properties) {
this.client = client;
this.properties = properties;
}
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
List<String> indices = this.properties.getIndices();
ClusterHealthResponse response = this.client
.admin()
.cluster()
.health(Requests.clusterHealthRequest(indices.isEmpty() ? null : indices
.toArray(new String[indices.size()])))
.actionGet(this.properties.getResponseTimeout());
switch (response.getStatus()) {
case GREEN:
case YELLOW:
builder.up();
break;
case RED:
default:
builder.down();
break;
}
builder.withDetail("clusterName", response.getClusterName());
builder.withDetail("numberOfNodes", response.getNumberOfNodes());
builder.withDetail("numberOfDataNodes", response.getNumberOfDataNodes());
builder.withDetail("activePrimaryShards", response.getActivePrimaryShards());
builder.withDetail("activeShards", response.getActiveShards());
builder.withDetail("relocatingShards", response.getRelocatingShards());
builder.withDetail("initializingShards", response.getInitializingShards());
builder.withDetail("unassignedShards", response.getUnassignedShards());
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014 the original author or authors.
* 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.
@@ -16,37 +16,41 @@
package org.springframework.boot.actuate.health;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* External configuration properties for {@link ElasticsearchHealthIndicator}
*
* @author Binwei Yang
* @since 1.2.2
* @author Andy Wilkinson
* @since 1.3.0
*/
@ConfigurationProperties("management.health.elasticsearch")
public class ElasticsearchHealthIndicatorProperties {
public static final String ALL = "_all";
/**
* Comma-separated index names
*/
private List<String> indices = new ArrayList<String>();
/**
* comma separated index names. the default includes all indices.
*/
private String indices = ALL;
/**
* The time, in milliseconds, to wait for a response from the cluster
*/
private long responseTimeout = 100L;
public String getIndices() {
return indices;
}
public List<String> getIndices() {
return this.indices;
}
public void setIndices(String indices) {
this.indices = indices;
}
public long getResponseTimeout() {
return this.responseTimeout;
}
public void setResponseTimeout(long responseTimeout) {
this.responseTimeout = responseTimeout;
}
String[] getIndexNamesAsArray() {
if (null == indices) {
return new String[]{ALL};
} else {
return indices.split(",");
}
}
}

View File

@@ -12,5 +12,4 @@ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration,\
org.springframework.boot.actuate.autoconfigure.ElasticsearchHealthIndicatorConfiguration
org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration