Commit 9c2679d4 by Dave Syer

Add metrics from Eureka client discovery

parent fcf2811e
...@@ -16,13 +16,16 @@ ...@@ -16,13 +16,16 @@
package org.springframework.cloud.netflix.eureka; package org.springframework.cloud.netflix.eureka;
import javax.annotation.PreDestroy; import javax.annotation.PreDestroy;
import javax.management.MBeanServer;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.netflix.servo.ServoMetricReader;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -34,6 +37,7 @@ import org.springframework.context.event.ContextRefreshedEvent; ...@@ -34,6 +37,7 @@ import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import com.netflix.appinfo.ApplicationInfoManager; import com.netflix.appinfo.ApplicationInfoManager;
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.InstanceInfo.InstanceStatus; import com.netflix.appinfo.InstanceInfo.InstanceStatus;
import com.netflix.discovery.DiscoveryClient; import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.DiscoveryManager; import com.netflix.discovery.DiscoveryManager;
...@@ -118,4 +122,10 @@ public class EurekaClientConfiguration implements ...@@ -118,4 +122,10 @@ public class EurekaClientConfiguration implements
return DiscoveryManager.getInstance().getDiscoveryClient(); return DiscoveryManager.getInstance().getDiscoveryClient();
} }
@Bean
@ConditionalOnMissingBean
public EurekaHealthIndicator eurekaHealthIndicator(MBeanServer server, EurekaInstanceConfig config) {
return new EurekaHealthIndicator(discoveryClient(), new ServoMetricReader(server), config);
}
} }
/*
* 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;
}
}
/*
* 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;
}
}
/*
* 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);
}
}
/*
* 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;
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\ org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration,\
org.springframework.cloud.netflix.feign.FeignAutoConfiguration org.springframework.cloud.netflix.feign.FeignAutoConfiguration,\
\ No newline at end of file org.springframework.cloud.netflix.servo.ServoMetricsAutoConfiguration
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment