Commit fcf2811e by Dave Syer

Add Hystrix data to /metrics

parent 64258624
package org.springframework.cloud.netflix.hystrix; package org.springframework.cloud.netflix.hystrix;
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect; import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.metrics.GaugeService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware; import org.springframework.context.annotation.ImportAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Collection; import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsPoller;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsPoller.MetricsAsJsonPollerListener;
/** /**
* Created by sgibb on 6/19/14. * Created by sgibb on 6/19/14.
...@@ -19,37 +33,140 @@ import java.util.Collection; ...@@ -19,37 +33,140 @@ import java.util.Collection;
@Configuration @Configuration
public class HystrixConfiguration implements ImportAware { public class HystrixConfiguration implements ImportAware {
private AnnotationAttributes enableHystrix; private AnnotationAttributes enableHystrix;
@Bean @Bean
HystrixCommandAspect hystrixCommandAspect() { HystrixCommandAspect hystrixCommandAspect() {
return new HystrixCommandAspect(); return new HystrixCommandAspect();
} }
@Bean @Bean
//TODO: add enable/disable // TODO: add enable/disable
public HystrixStreamEndpoint hystrixStreamEndpoint() { // TODO: make it @ConditionalOnWebApp (need a nested class)
return new HystrixStreamEndpoint(); public HystrixStreamEndpoint hystrixStreamEndpoint() {
} return new HystrixStreamEndpoint();
}
@Override
public void setImportMetadata(AnnotationMetadata importMetadata) { @Override
this.enableHystrix = AnnotationAttributes.fromMap( public void setImportMetadata(AnnotationMetadata importMetadata) {
importMetadata.getAnnotationAttributes(EnableHystrix.class.getName(), false)); this.enableHystrix = AnnotationAttributes.fromMap(importMetadata
Assert.notNull(this.enableHystrix, .getAnnotationAttributes(EnableHystrix.class.getName(), false));
"@EnableHystrix is not present on importing class " + importMetadata.getClassName()); Assert.notNull(
} this.enableHystrix,
"@EnableHystrix is not present on importing class "
@Autowired(required=false) + importMetadata.getClassName());
void setConfigurers(Collection<HystrixConfigurer> configurers) { }
if (CollectionUtils.isEmpty(configurers)) {
return; @Autowired(required = false)
} void setConfigurers(Collection<HystrixConfigurer> configurers) {
if (configurers.size() > 1) { if (CollectionUtils.isEmpty(configurers)) {
throw new IllegalStateException("Only one TransactionManagementConfigurer may exist"); return;
} }
//TODO: create CircuitBreakerConfigurer API if (configurers.size() > 1) {
// CircuitBreakerConfigurer configurer = configurers.iterator().next(); throw new IllegalStateException(
//this.txManager = configurer.annotationDrivenTransactionManager(); "Only one TransactionManagementConfigurer may exist");
} }
// TODO: create CircuitBreakerConfigurer API
// CircuitBreakerConfigurer configurer = configurers.iterator().next();
// this.txManager = configurer.annotationDrivenTransactionManager();
}
@Configuration
@ConditionalOnClass(GaugeService.class)
protected static class HystrixMetricsPollerConfiguration implements SmartLifecycle {
private static Log logger = LogFactory
.getLog(HystrixMetricsPollerConfiguration.class);
@Autowired
private GaugeService gauges;
private ObjectMapper mapper = new ObjectMapper();
private HystrixMetricsPoller poller;
private Set<String> reserved = new HashSet<String>(Arrays.asList("group", "name",
"type", "currentTime"));
@Override
public void start() {
MetricsAsJsonPollerListener listener = new MetricsAsJsonPollerListener() {
@Override
public void handleJsonMetric(String json) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> map = mapper.readValue(json, Map.class);
if (map != null && map.containsKey("type")) {
addMetrics(map, "hystrix.");
}
}
catch (IOException e) {
// ignore
}
}
};
poller = new HystrixMetricsPoller(listener, 2000);
// start polling and it will write directly to the listener
poller.start();
logger.info("Starting poller");
}
private void addMetrics(Map<String, Object> map, String root) {
StringBuilder prefixBuilder = new StringBuilder(root);
if (map.containsKey("type")) {
prefixBuilder.append((String) map.get("type"));
if (map.containsKey("group")) {
prefixBuilder.append(".").append(map.get("group"));
}
prefixBuilder.append(".").append(map.get("name"));
}
String prefix = prefixBuilder.toString();
for (String key : map.keySet()) {
Object value = map.get(key);
if (!reserved.contains(key)) {
if (value instanceof Number) {
String name = prefix + "." + key;
gauges.submit(name, ((Number) value).doubleValue());
}
else if (value instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> sub = (Map<String, Object>) value;
addMetrics(sub, prefix);
}
}
}
}
@Override
public void stop() {
if (poller != null) {
poller.shutdown();
}
}
@Override
public boolean isRunning() {
return poller!=null ? poller.isRunning() : false;
}
@Override
public int getPhase() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public void stop(Runnable callback) {
if (poller != null) {
poller.shutdown();
}
callback.run();
}
}
} }
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