diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java
index 1fdb41b3..8bc06f97 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfiguration.java
@@ -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);
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricReader.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricReader.java
index bcf81e8d..29cbcd1d 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricReader.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricReader.java
@@ -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;
+
/**
+ * {@link MetricReader} implementation that registers a {@link MetricObserver} with the
+ * Netflix Servo library and exposes Servo metrics to the /metric 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> metrics = new HashMap>();
+
+ public ServoMetricReader() {
+ List observers = new ArrayList();
+ 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> 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> getServoMetrics() {
- Map> metrics = getServoMetrics("COUNTER");
- metrics.putAll(getServoMetrics("GAUGE"));
- return metrics;
- }
-
- private Map> getServoMetrics(String type) {
- Map> metrics = new HashMap>();
- try {
- ObjectName name = new ObjectName("com.netflix.servo:type="+type+",*");
- Set 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(key, value));
+
+ /**
+ * {@link MetricObserver} to convert Servo metrics into Spring Boot {@link Metric} instances.
+ */
+ private static final class ServoMetricObserver extends BaseMetricObserver {
+
+ private final Map> metrics;
+
+ public ServoMetricObserver(Map> metrics) {
+ super("spring-boot");
+ this.metrics = metrics;
+ }
+
+ @Override
+ public void updateImpl(List servoMetrics) {
+ Map> newMetrics = new HashMap>();
+ 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(key, servoMetric.getNumberValue(),
+ new Date(servoMetric.getTimestamp())));
}
}
+
+ synchronized (monitor) {
+ this.metrics.clear();
+ this.metrics.putAll(newMetrics);
+ }
}
- catch (Exception e) {
- // Really?
- }
- return metrics;
}
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricsAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricsAutoConfiguration.java
index eae0df05..e58195a5 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricsAutoConfiguration.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoMetricsAutoConfiguration.java
@@ -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;
/**
+ * 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);
}
-
}
diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoPublicMetrics.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoPublicMetrics.java
index 75259297..245bb97d 100644
--- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoPublicMetrics.java
+++ b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/servo/ServoPublicMetrics.java
@@ -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;
/**
+ * {@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