Commit 4390b795 by Christian Dupuis

Retrieve servo metrics directly instead of grabbing them from JMX

fixes #16
parent c4a53a52
......@@ -16,10 +16,8 @@
package org.springframework.cloud.netflix.eureka;
import javax.annotation.PostConstruct;
import javax.management.MBeanServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
......@@ -70,11 +68,8 @@ public class EurekaClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnBean(MBeanServer.class)
@ConditionalOnExpression("${spring.jmx.enabled:true}")
public EurekaHealthIndicator eurekaHealthIndicator(MBeanServer server,
EurekaInstanceConfig config) {
return new EurekaHealthIndicator(discoveryClient, new ServoMetricReader(server),
public EurekaHealthIndicator eurekaHealthIndicator(EurekaInstanceConfig config) {
return new EurekaHealthIndicator(discoveryClient, new ServoMetricReader(),
config);
}
......
......@@ -13,72 +13,107 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.servo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import java.util.concurrent.TimeUnit;
import org.springframework.boot.actuate.metrics.Metric;
import org.springframework.boot.actuate.metrics.reader.MetricReader;
import com.netflix.servo.monitor.MonitorConfig;
import com.netflix.servo.publish.BaseMetricObserver;
import com.netflix.servo.publish.BasicMetricFilter;
import com.netflix.servo.publish.MetricObserver;
import com.netflix.servo.publish.MonitorRegistryMetricPoller;
import com.netflix.servo.publish.PollRunnable;
import com.netflix.servo.publish.PollScheduler;
/**
* @author Dave Syer
* {@link MetricReader} implementation that registers a {@link MetricObserver} with the
* Netflix Servo library and exposes Servo metrics to the <code>/metric</code> endpoint.
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class ServoMetricReader implements MetricReader {
private final MBeanServer server;
private static final Object monitor = new Object();
public ServoMetricReader(MBeanServer server) {
this.server = server;
private final Map<String, Metric<?>> metrics = new HashMap<String, Metric<?>>();
public ServoMetricReader() {
List<MetricObserver> observers = new ArrayList<MetricObserver>();
observers.add(new ServoMetricObserver(this.metrics));
PollRunnable task = new PollRunnable(new MonitorRegistryMetricPoller(),
BasicMetricFilter.MATCH_ALL, true, observers);
if (!PollScheduler.getInstance().isStarted()) {
PollScheduler.getInstance().start();
}
// TODO Make poll interval configurable
PollScheduler.getInstance().addPoller(task, 5, TimeUnit.SECONDS);
}
@Override
public Metric<?> findOne(String metricName) {
return getServoMetrics().get(metricName);
synchronized (monitor) {
return this.metrics.get(metricName);
}
}
@Override
public Iterable<Metric<?>> findAll() {
return getServoMetrics().values();
synchronized (monitor) {
return Collections.unmodifiableCollection(this.metrics.values());
}
}
@Override
public long count() {
return getServoMetrics().size();
synchronized (monitor) {
return this.metrics.size();
}
}
private Map<String,Metric<?>> getServoMetrics() {
Map<String, Metric<?>> metrics = getServoMetrics("COUNTER");
metrics.putAll(getServoMetrics("GAUGE"));
return metrics;
/**
* {@link MetricObserver} to convert Servo metrics into Spring Boot {@link Metric} instances.
*/
private static final class ServoMetricObserver extends BaseMetricObserver {
private final Map<String, Metric<?>> metrics;
public ServoMetricObserver(Map<String, Metric<?>> metrics) {
super("spring-boot");
this.metrics = 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));
@Override
public void updateImpl(List<com.netflix.servo.Metric> servoMetrics) {
Map<String, Metric<?>> newMetrics = new HashMap<String, Metric<?>>();
for (com.netflix.servo.Metric servoMetric : servoMetrics) {
MonitorConfig config = servoMetric.getConfig();
String type = config.getTags().getValue("type");
String key = new StringBuilder(type).append(".servo.").append(config.getName())
.toString().toLowerCase();
if (servoMetric.hasNumberValue()) {
newMetrics.put(key, new Metric<Number>(key, servoMetric.getNumberValue(),
new Date(servoMetric.getTimestamp())));
}
}
synchronized (monitor) {
this.metrics.clear();
this.metrics.putAll(newMetrics);
}
catch (Exception e) {
// Really?
}
return metrics;
}
}
......@@ -13,9 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.netflix.servo;
import javax.management.MBeanServer;
package org.springframework.cloud.netflix.servo;
import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration;
......@@ -24,30 +23,28 @@ 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.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.servo.monitor.Monitors;
/**
* @author Dave Syer
* Auto configuration to configure Servo support.
*
* @author Dave Syer
* @author Christian Dupuis
*/
@Configuration
@ConditionalOnClass({ Monitors.class, MetricReader.class })
@ConditionalOnBean(MetricReader.class)
@AutoConfigureBefore(EndpointAutoConfiguration.class)
@AutoConfigureAfter({MetricRepositoryAutoConfiguration.class, JmxAutoConfiguration.class})
@ConditionalOnExpression("${spring.jmx.enabled:true}")
@AutoConfigureAfter({MetricRepositoryAutoConfiguration.class})
public class ServoMetricsAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public ServoPublicMetrics servoPublicMetrics(MetricReader reader, MBeanServer server) {
return new ServoPublicMetrics(reader, server);
public ServoPublicMetrics servoPublicMetrics(MetricReader reader) {
return new ServoPublicMetrics(reader);
}
}
......@@ -13,27 +13,29 @@
* 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.PublicMetrics;
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
* {@link PublicMetrics} implementation for Servo metrics.
*
* @author Dave Syer
* @author Christian Dupuis
*/
public class ServoPublicMetrics extends VanillaPublicMetrics {
private final ServoMetricReader servo;
public ServoPublicMetrics(MetricReader reader, MBeanServer server) {
public ServoPublicMetrics(MetricReader reader) {
super(reader);
this.servo = new ServoMetricReader(server);
this.servo = new ServoMetricReader();
}
@Override
......
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