Add metrics from Eureka client discovery
This commit is contained in:
@@ -16,13 +16,16 @@
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import javax.annotation.PreDestroy;
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.netflix.servo.ServoMetricReader;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.SmartLifecycle;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -34,6 +37,7 @@ import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import com.netflix.appinfo.ApplicationInfoManager;
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.discovery.DiscoveryClient;
|
||||
import com.netflix.discovery.DiscoveryManager;
|
||||
@@ -118,4 +122,10 @@ public class EurekaClientConfiguration implements
|
||||
return DiscoveryManager.getInstance().getDiscoveryClient();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EurekaHealthIndicator eurekaHealthIndicator(MBeanServer server, EurekaInstanceConfig config) {
|
||||
return new EurekaHealthIndicator(discoveryClient(), new ServoMetricReader(server), config);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.eureka;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.Health.Builder;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.DiscoveryClient;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class EurekaHealthIndicator implements HealthIndicator {
|
||||
|
||||
private EurekaInstanceConfig instanceConfig;
|
||||
|
||||
private MetricReader metrics;
|
||||
|
||||
private int failCount = 0;
|
||||
|
||||
private DiscoveryClient discovery;
|
||||
|
||||
public EurekaHealthIndicator(DiscoveryClient discovery, MetricReader metrics,
|
||||
EurekaInstanceConfig instanceConfig) {
|
||||
super();
|
||||
this.discovery = discovery;
|
||||
this.metrics = metrics;
|
||||
this.instanceConfig = instanceConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Health health() {
|
||||
Builder builder = Health.unknown();
|
||||
Status status = getStatus(builder);
|
||||
return builder.status(status).withDetail("applications", getApplications())
|
||||
.build();
|
||||
}
|
||||
|
||||
private Status getStatus(Builder builder) {
|
||||
Status status = new Status(discovery.getInstanceRemoteStatus().toString(),
|
||||
"Remote status from Eureka server");
|
||||
@SuppressWarnings("unchecked")
|
||||
Metric<Number> value = (Metric<Number>) metrics
|
||||
.findOne("counter.servo.DiscoveryClient_Failed");
|
||||
if (value != null) {
|
||||
int renewalPeriod = instanceConfig.getLeaseRenewalIntervalInSeconds();
|
||||
int latest = value.getValue().intValue();
|
||||
builder.withDetail("failCount", latest);
|
||||
builder.withDetail("renewalPeriod", renewalPeriod);
|
||||
if (failCount < latest) {
|
||||
status = new Status("UP", "Eureka discovery client is reporting failures");
|
||||
failCount = latest;
|
||||
} else {
|
||||
status = new Status("UP", "No new failures in Eureka discovery client");
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private Map<String, Object> getApplications() {
|
||||
Applications applications = discovery.getApplications();
|
||||
if (applications == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
for (Application application : applications.getRegisteredApplications()) {
|
||||
result.put(application.getName(), application.getInstances().size());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.servo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ServoMetricReader implements MetricReader {
|
||||
|
||||
private final MBeanServer server;
|
||||
|
||||
public ServoMetricReader(MBeanServer server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metric<?> findOne(String metricName) {
|
||||
return getServoMetrics().get(metricName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Metric<?>> findAll() {
|
||||
return getServoMetrics().values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long count() {
|
||||
return getServoMetrics().size();
|
||||
}
|
||||
|
||||
private Map<String,Metric<?>> getServoMetrics() {
|
||||
Map<String, Metric<?>> metrics = getServoMetrics("COUNTER");
|
||||
metrics.putAll(getServoMetrics("GAUGE"));
|
||||
return metrics;
|
||||
}
|
||||
|
||||
private Map<String,Metric<?>> getServoMetrics(String type) {
|
||||
Map<String,Metric<?>> metrics = new HashMap<String, Metric<?>>();
|
||||
try {
|
||||
ObjectName name = new ObjectName("com.netflix.servo:type="+type+",*");
|
||||
Set<ObjectInstance> beans = server.queryMBeans(name, null);
|
||||
for (ObjectInstance bean : beans) {
|
||||
// example: com.netflix.servo:name=DiscoveryClient_Failed,class=DiscoveryClient,type=COUNTER
|
||||
String key = type.toLowerCase() + ".servo."
|
||||
+ bean.getObjectName().getKeyProperty("name");
|
||||
Object attribute = server.getAttribute(bean.getObjectName(), "value");
|
||||
if (attribute instanceof Number) {
|
||||
Number value = (Number) attribute;
|
||||
metrics.put(key, new Metric<Number>(key, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
// Really?
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.servo;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
|
||||
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
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.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.servo.monitor.Monitors;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({Monitors.class,MetricReader.class})
|
||||
@ConditionalOnBean({MBeanServer.class,MetricReader.class})
|
||||
@AutoConfigureBefore(EndpointAutoConfiguration.class)
|
||||
@AutoConfigureAfter(MetricRepositoryAutoConfiguration.class)
|
||||
public class ServoMetricsAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ServoPublicMetrics servoPublicMetrics(MetricReader reader, MBeanServer server) {
|
||||
return new ServoPublicMetrics(reader, server);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-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.cloud.netflix.servo;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
|
||||
import org.springframework.boot.actuate.endpoint.VanillaPublicMetrics;
|
||||
import org.springframework.boot.actuate.metrics.Metric;
|
||||
import org.springframework.boot.actuate.metrics.reader.MetricReader;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ServoPublicMetrics extends VanillaPublicMetrics {
|
||||
|
||||
private final ServoMetricReader servo;
|
||||
|
||||
public ServoPublicMetrics(MetricReader reader, MBeanServer server) {
|
||||
super(reader);
|
||||
this.servo = new ServoMetricReader(server);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Metric<?>> metrics() {
|
||||
Collection<Metric<?>> metrics = super.metrics();
|
||||
if (servo != null) {
|
||||
for (Metric<?> metric : servo.findAll()) {
|
||||
metrics.add(metric);
|
||||
}
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.feign.FeignAutoConfiguration
|
||||
org.springframework.cloud.netflix.feign.FeignAutoConfiguration,\
|
||||
org.springframework.cloud.netflix.servo.ServoMetricsAutoConfiguration
|
||||
Reference in New Issue
Block a user