diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/DefaultMetricsTagProvider.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/DefaultMetricsTagProvider.java deleted file mode 100644 index ea88aefe..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/DefaultMetricsTagProvider.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.HandlerMapping; - -/** - * @author Jon Schneider - */ -public class DefaultMetricsTagProvider implements MetricsTagProvider { - @Override - public Map clientHttpRequestTags(HttpRequest request, - ClientHttpResponse response) { - String urlTemplate = RestTemplateUrlTemplateHolder.getRestTemplateUrlTemplate(); - if (urlTemplate == null) { - urlTemplate = "none"; - } - - String status; - try { - status = (response == null) ? "CLIENT_ERROR" : ((Integer) response - .getRawStatusCode()).toString(); - } - catch (IOException e) { - status = "IO_ERROR"; - } - - String host = request.getURI().getHost(); - if( host == null ) { - host = "none"; - } - - String strippedUrlTemplate = urlTemplate.replaceAll("^https?://[^/]+/", ""); - - Map tags = new HashMap<>(); - tags.put("method", request.getMethod().name()); - tags.put("uri", sanitizeUrlTemplate(strippedUrlTemplate)); - tags.put("status", status); - tags.put("clientName", host); - - return Collections.unmodifiableMap(tags); - } - - @Override - public Map httpRequestTags(HttpServletRequest request, - HttpServletResponse response, Object handler, String caller) { - Map tags = new HashMap<>(); - - tags.put("method", request.getMethod()); - tags.put("status", ((Integer) response.getStatus()).toString()); - - String uri = (String) request - .getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE); - if (uri == null) { - uri = request.getPathInfo(); - } - if (!StringUtils.hasText(uri)) { - uri = "/"; - } - uri = sanitizeUrlTemplate(uri.substring(1)); - tags.put("uri", uri.isEmpty() ? "root" : uri); - - Object exception = request.getAttribute("exception"); - if (exception != null) { - tags.put("exception", exception.getClass().getSimpleName()); - } - - if (caller != null) { - tags.put("caller", caller); - } - - return tags; - } - - /** - * As is, the urlTemplate is not suitable for use with Atlas, as all interactions with - * Atlas take place via query parameters - */ - protected String sanitizeUrlTemplate(String urlTemplate) { - String sanitized = urlTemplate - .replaceAll("\\{(\\w+):.+}(?=/|$)", "-$1-") // extract path variable names from regex expressions - .replaceAll("/", "_") - .replaceAll("[{}]", "-"); - if (!StringUtils.hasText(sanitized)) { - sanitized = "none"; - } - return sanitized; - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsClientHttpRequestInterceptor.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsClientHttpRequestInterceptor.java deleted file mode 100644 index 5c43c4eb..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsClientHttpRequestInterceptor.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.io.IOException; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache; -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpRequestExecution; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.http.client.ClientHttpResponse; - -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.tag.SmallTagMap; -import com.netflix.servo.tag.Tags; - -/** - * Intercepts RestTemplate requests and records metrics about execution time and results. - * - * @author Jon Schneider - */ -public class MetricsClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { - /** - * The interceptor writes to a Servo MonitorRegistry, which we get away with for now - * because our Spectator implementation is underpinned by a ServoRegistry. When Spring - * Boot (Actuator) provides a more general purpose abstraction for dimensional metrics - * systems, this can be moved there and rewritten against that abstraction. - */ - private final ServoMonitorCache servoMonitorCache; - - private final Collection tagProviders; - - private final String metricName; - - public MetricsClientHttpRequestInterceptor( - Collection tagProviders, - ServoMonitorCache servoMonitorCache, String metricName) { - this.tagProviders = tagProviders; - this.servoMonitorCache = servoMonitorCache; - this.metricName = metricName; - } - - @Override - public ClientHttpResponse intercept(HttpRequest request, byte[] body, - ClientHttpRequestExecution execution) throws IOException { - long startTime = System.nanoTime(); - - ClientHttpResponse response = null; - try { - response = execution.execute(request, body); - return response; - } - finally { - SmallTagMap.Builder builder = SmallTagMap.builder(); - for (MetricsTagProvider tagProvider : tagProviders) { - for (Map.Entry tag : tagProvider - .clientHttpRequestTags(request, response).entrySet()) { - builder.add(Tags.newTag(tag.getKey(), tag.getValue())); - } - } - - MonitorConfig.Builder monitorConfigBuilder = MonitorConfig - .builder(metricName); - monitorConfigBuilder.withTags(builder); - - servoMonitorCache.getTimer(monitorConfigBuilder.build()) - .record(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); - } - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptor.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptor.java deleted file mode 100644 index 5db99822..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptor.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.TimeUnit; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache; -import org.springframework.web.context.request.RequestContextHolder; -import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.tag.SmallTagMap; -import com.netflix.servo.tag.Tags; - -import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST; - -/** - * Intercepts incoming HTTP requests and records metrics about execution time and results. - * - * @author Jon Schneider - */ -public class MetricsHandlerInterceptor extends HandlerInterceptorAdapter { - @Value("${netflix.metrics.rest.metricName:rest}") - String metricName; - - @Value("${netflix.metrics.rest.callerHeader:#{null}}") - String callerHeader; - - @Autowired - MonitorRegistry registry; - - @Autowired - ServoMonitorCache servoMonitorCache; - - @Autowired - Collection tagProviders; - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, - Object handler) throws Exception { - RequestContextHolder.getRequestAttributes().setAttribute("requestStartTime", - System.nanoTime(), SCOPE_REQUEST); - return super.preHandle(request, response, handler); - } - - @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, - Object handler, Exception ex) throws Exception { - RequestContextHolder.getRequestAttributes().setAttribute("exception", ex, - SCOPE_REQUEST); - Long startTime = (Long) RequestContextHolder.getRequestAttributes().getAttribute( - "requestStartTime", SCOPE_REQUEST); - if (startTime != null) - recordMetric(request, response, handler, startTime); - super.afterCompletion(request, response, handler, ex); - } - - protected void recordMetric(HttpServletRequest request, HttpServletResponse response, - Object handler, Long startTime) { - String caller = null; - if (callerHeader != null) { - caller = request.getHeader(callerHeader); - } - - SmallTagMap.Builder builder = SmallTagMap.builder(); - for (MetricsTagProvider tagProvider : tagProviders) { - Map tags = tagProvider.httpRequestTags(request, response, - handler, caller); - for (Map.Entry tag : tags.entrySet()) { - builder.add(Tags.newTag(tag.getKey(), tag.getValue())); - } - } - - MonitorConfig.Builder monitorConfigBuilder = MonitorConfig.builder(metricName); - monitorConfigBuilder.withTags(builder); - - servoMonitorCache.getTimer(monitorConfigBuilder.build()).record( - System.nanoTime() - startTime, TimeUnit.NANOSECONDS); - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsInterceptorConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsInterceptorConfiguration.java deleted file mode 100644 index 4352a5c1..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsInterceptorConfiguration.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.util.ArrayList; -import java.util.Collection; - -import javax.servlet.http.HttpServletRequest; - -import org.aspectj.lang.JoinPoint; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; -import org.springframework.cloud.netflix.metrics.servo.ServoMonitorCache; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestInterceptor; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.Monitors; - -/** - * @author Jon Schneider - */ -@Configuration -@ConditionalOnProperty(value = "spring.cloud.netflix.metrics.enabled", havingValue = "true", matchIfMissing = true) -@ConditionalOnClass({ Monitors.class, MetricReader.class }) -public class MetricsInterceptorConfiguration { - - @Configuration - @ConditionalOnWebApplication - @ConditionalOnClass(WebMvcConfigurerAdapter.class) - static class MetricsWebResourceConfiguration extends WebMvcConfigurerAdapter { - @Bean - MetricsHandlerInterceptor servoMonitoringWebResourceInterceptor() { - return new MetricsHandlerInterceptor(); - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(servoMonitoringWebResourceInterceptor()); - } - } - - @Configuration - @ConditionalOnClass({ RestTemplate.class, JoinPoint.class }) - @ConditionalOnProperty(value = "spring.aop.enabled", havingValue = "true", matchIfMissing = true) - static class MetricsRestTemplateAspectConfiguration { - - @Bean - RestTemplateUrlTemplateCapturingAspect restTemplateUrlTemplateCapturingAspect() { - return new RestTemplateUrlTemplateCapturingAspect(); - } - - } - - @Configuration - @ConditionalOnClass({ RestTemplate.class, HttpServletRequest.class }) // HttpServletRequest implicitly required by MetricsTagProvider - static class MetricsRestTemplateConfiguration { - - @Value("${netflix.metrics.restClient.metricName:restclient}") - String metricName; - - @Bean - MetricsClientHttpRequestInterceptor spectatorLoggingClientHttpRequestInterceptor( - Collection tagProviders, - ServoMonitorCache servoMonitorCache) { - return new MetricsClientHttpRequestInterceptor(tagProviders, - servoMonitorCache, this.metricName); - } - - @Bean - BeanPostProcessor spectatorRestTemplateInterceptorPostProcessor() { - return new MetricsInterceptorPostProcessor(); - } - - private static class MetricsInterceptorPostProcessor - implements BeanPostProcessor, ApplicationContextAware { - private ApplicationContext context; - private MetricsClientHttpRequestInterceptor interceptor; - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) { - return bean; - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) { - if (bean instanceof RestTemplate) { - if (this.interceptor == null) { - this.interceptor = this.context - .getBean(MetricsClientHttpRequestInterceptor.class); - } - RestTemplate restTemplate = (RestTemplate) bean; - // create a new list as the old one may be unmodifiable (ie Arrays.asList()) - ArrayList interceptors = new ArrayList<>(); - interceptors.add(interceptor); - interceptors.addAll(restTemplate.getInterceptors()); - restTemplate.setInterceptors(interceptors); - } - return bean; - } - - @Override - public void setApplicationContext(ApplicationContext context) - throws BeansException { - this.context = context; - } - } - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProvider.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProvider.java deleted file mode 100644 index 34a66eae..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpResponse; - -/** - * @author Jon Schneider - */ -public interface MetricsTagProvider { - /** - * @param request RestTemplate client HTTP request - * @param response may be null in the event of a client error - * @return a map of tags added to every client HTTP request metric - */ - Map clientHttpRequestTags(HttpRequest request, - ClientHttpResponse response); - - /** - * @param request HTTP request - * @param response HTTP response - * @param handler the request method that is responsible for handling the request - * @return a map of tags added to every Spring MVC HTTP request metric - */ - Map httpRequestTags(HttpServletRequest request, - HttpServletResponse response, Object handler, String caller); -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProviderAdapter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProviderAdapter.java deleted file mode 100644 index 3ec1b9f1..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/MetricsTagProviderAdapter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.util.Collections; -import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.http.HttpRequest; -import org.springframework.http.client.ClientHttpResponse; - -/** - * @author Jon Schneider - */ -public class MetricsTagProviderAdapter implements MetricsTagProvider { - @Override - public Map clientHttpRequestTags(HttpRequest request, - ClientHttpResponse response) { - return Collections.emptyMap(); - } - - @Override - public Map httpRequestTags(HttpServletRequest request, - HttpServletResponse response, Object handler, String caller) { - return Collections.emptyMap(); - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateCapturingAspect.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateCapturingAspect.java deleted file mode 100644 index 18abe558..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateCapturingAspect.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; - -/** - * Captures the still-templated URI because currently the ClientHttpRequestInterceptor - * currently only gives us the means to retrieve the substituted URI. - * - * @author Jon Schneider - */ -@Aspect -public class RestTemplateUrlTemplateCapturingAspect { - @Around("execution(* org.springframework.web.client.RestOperations+.*(String, ..))") - public Object captureUrlTemplate(ProceedingJoinPoint joinPoint) throws Throwable { - try { - String urlTemplate = (String) joinPoint.getArgs()[0]; - RestTemplateUrlTemplateHolder.setRestTemplateUrlTemplate(urlTemplate); - return joinPoint.proceed(); - } - finally { - RestTemplateUrlTemplateHolder.clear(); - } - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateHolder.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateHolder.java deleted file mode 100644 index 28bfea1d..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/RestTemplateUrlTemplateHolder.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import org.springframework.core.NamedThreadLocal; - -/** - * Holding area for the still-templated URI because currently the - * ClientHttpRequestInterceptor only gives us the means to retrieve the substituted URI. - * - * @author Jon Schneider - */ -public class RestTemplateUrlTemplateHolder { - private static final ThreadLocal restTemplateUrlTemplateHolder = new NamedThreadLocal( - "Rest Template URL Template"); - - public static String getRestTemplateUrlTemplate() { - return restTemplateUrlTemplateHolder.get(); - } - - public static void setRestTemplateUrlTemplate(String urlTemplate) { - restTemplateUrlTemplateHolder.set(urlTemplate); - } - - public static void clear() { - restTemplateUrlTemplateHolder.remove(); - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java deleted file mode 100644 index f521ef32..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/ServoEnvironmentPostProcessor.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics; - -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; -import org.springframework.util.ClassUtils; - -/** - * @author Dave Syer - */ -public class ServoEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private static final Log log = LogFactory.getLog(ServoEnvironmentPostProcessor.class); - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - if (ClassUtils.isPresent("com.netflix.servo.monitor.Monitors", null)) { - // Make spring AOP default to target class so RestTemplates can be customized - log.debug("Setting 'spring.aop.proxyTargetClass=true' to make spring AOP default to target class so RestTemplates can be customized"); - addDefaultProperty(environment, "spring.aop.proxyTargetClass", "true"); - } - } - - private void addDefaultProperty(ConfigurableEnvironment environment, String name, - String value) { - MutablePropertySources sources = environment.getPropertySources(); - Map map = null; - if (sources.contains("defaultProperties")) { - PropertySource source = sources.get("defaultProperties"); - if (source instanceof MapPropertySource) { - map = ((MapPropertySource) source).getSource(); - } - } - else { - map = new LinkedHashMap<>(); - sources.addLast(new MapPropertySource("defaultProperties", map)); - } - if (map != null) { - map.put(name, value); - } - } - -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java deleted file mode 100644 index a2f8e4cb..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import java.util.Collection; -import java.util.Map; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.actuate.metrics.export.Exporter; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.cloud.netflix.metrics.servo.ServoMetricsAutoConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.client.RestTemplate; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.publish.MonitorRegistryMetricPoller; -import com.netflix.servo.tag.BasicTagList; - -/** - * Configures the Atlas metrics backend, also configuring Spectator to collect metrics if necessary. - * - * @author Jon Schneider - */ -@Configuration -@ConditionalOnClass(AtlasMetricObserver.class) -@Import(ServoMetricsAutoConfiguration.class) -public class AtlasConfiguration { - @Autowired(required = false) - private Collection tagProviders; - - @Autowired(required = false) - @Qualifier("atlasRestTemplate") - private RestTemplate restTemplate = new RestTemplate(); - - @Bean - public AtlasMetricObserverConfigBean atlasObserverConfig() { - return new AtlasMetricObserverConfigBean(); - } - - @Bean - @ConditionalOnMissingBean - public AtlasMetricObserver atlasObserver(AtlasMetricObserverConfigBean atlasObserverConfig) { - BasicTagList tags = (BasicTagList) BasicTagList.EMPTY; - if (tagProviders != null) { - for (AtlasTagProvider tagProvider : tagProviders) { - for (Map.Entry tag : tagProvider.defaultTags().entrySet()) { - if (tag.getValue() != null) - tags = tags.copy(tag.getKey(), tag.getValue()); - } - } - } - return new AtlasMetricObserver(atlasObserverConfig, restTemplate, tags); - } - - @Bean - @ConditionalOnMissingBean - public Exporter exporter(AtlasMetricObserver observer, MonitorRegistry monitorRegistry) { - return new AtlasExporter(observer, new MonitorRegistryMetricPoller(monitorRegistry)); - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporter.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporter.java deleted file mode 100644 index 95b2dca5..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import org.springframework.boot.actuate.metrics.export.Exporter; - -import com.netflix.servo.publish.BasicMetricFilter; -import com.netflix.servo.publish.MetricPoller; - -/** - * @author Jon Schneider - */ -public class AtlasExporter implements Exporter { - private AtlasMetricObserver observer; - private MetricPoller poller; - - public AtlasExporter(AtlasMetricObserver observer, MetricPoller poller) { - this.observer = observer; - this.poller = poller; - } - - @Override - public void export() { - observer.update(poller.poll(BasicMetricFilter.MATCH_ALL)); - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserver.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserver.java deleted file mode 100644 index 540785d5..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserver.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.web.client.HttpClientErrorException; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.dataformat.smile.SmileFactory; -import com.netflix.servo.Metric; -import com.netflix.servo.annotations.DataSourceType; -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.publish.MetricObserver; -import com.netflix.servo.tag.BasicTag; -import com.netflix.servo.tag.Tag; -import com.netflix.servo.tag.TagList; - -/** - * Observer that forwards metrics to atlas. In addition to being a MetricObserver, it also - * supports a push model that sends metrics as soon as possible (asynchronously). - * - * @author Jon Schneider - */ -public class AtlasMetricObserver implements MetricObserver { - private static final Log logger = LogFactory.getLog(AtlasMetricObserver.class); - private static final SmileFactory smileFactory = new SmileFactory(); - private static final Tag atlasRateTag = new BasicTag("atlas.dstype", "rate"); - private static final Tag atlasCounterTag = new BasicTag("atlas.dstype", "counter"); - private static final Tag atlasGaugeTag = new BasicTag("atlas.dstype", "gauge"); - private static final Pattern validAtlasTag = Pattern.compile("[\\.\\-\\w]+"); - - private AtlasMetricObserverConfigBean config; - private RestTemplate restTemplate; - private TagList commonTags; - private String uri; - - public AtlasMetricObserver(AtlasMetricObserverConfigBean config, - RestTemplate restTemplate, TagList commonTags) { - this.config = config; - this.commonTags = commonTags; - this.restTemplate = restTemplate; - this.uri = normalizeAtlasUri(config.getUri()); - - if (!validTags(commonTags)) { - throw new IllegalArgumentException( - "One or more atlas tags contain invalid characters, must match [\\.\\-\\w]+"); - } - } - - @Override - public String getName() { - return "atlas"; - } - - protected static boolean validTags(TagList tags) { - for (Tag tag : tags) { - if (!validAtlasTag.matcher(tag.getKey()).matches()) { - logger.debug("Invalid tag key " + tag.getKey()); - return false; - } - - if (!validAtlasTag.matcher(tag.getValue()).matches()) { - logger.debug("Invalid tag value " + tag.getValue()); - return false; - } - } - - return true; - } - - static String normalizeAtlasUri(String uri) { - if (uri != null) { - Matcher matcher = Pattern.compile("(.+?)(/api/v1/publish)?/?").matcher(uri); - if (matcher.matches()) - return matcher.group(1) + "/api/v1/publish"; - else - throw new IllegalStateException("netflix.atlas.uri is not a valid uri"); - } - throw new IllegalStateException("netflix.atlas.uri was not found in your properties and is required to communicate with Atlas"); - } - - @Override - public void update(List rawMetrics) { - if (!config.isEnabled()) { - logger.debug("Atlas metric observer disabled. Not sending metrics."); - return; - } - - if (rawMetrics.isEmpty()) { - logger.debug("Metrics list is empty, no data being sent to server."); - return; - } - - List metrics = sanitizeTags(addTypeTagsAsNecessary(rawMetrics)); - - for (int i = 0; i < metrics.size(); i += config.getBatchSize()) { - List batch = metrics.subList(i, - Math.min(metrics.size(), config.getBatchSize() + i)); - logger.debug("Sending a metrics batch of size " + batch.size()); - sendMetricsBatch(batch); - } - } - - enum PublishMetricsBatchStatus { - NothingToDo, Success, PartialSuccess, Failure - } - - PublishMetricsBatchStatus sendMetricsBatch(List metrics) { - try { - ByteArrayOutputStream output = new ByteArrayOutputStream(); - JsonGenerator gen = smileFactory.createGenerator(output, JsonEncoding.UTF8); - - gen.writeStartObject(); - - writeCommonTags(gen); - if (writeMetrics(gen, metrics) == 0) - return PublishMetricsBatchStatus.NothingToDo; // short circuit this batch if no valid/numeric metrics existed - - gen.writeEndObject(); - gen.flush(); - - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.valueOf("application/x-jackson-smile")); - HttpEntity entity = new HttpEntity<>(output.toByteArray(), headers); - try { - ResponseEntity response = restTemplate.exchange(uri, HttpMethod.POST, entity, Map.class); - if(response.getStatusCode() == HttpStatus.ACCEPTED) { - // partial success processing the metrics batch - List messages = (List) response.getBody().get("message"); - if(messages != null) { - for (String message : messages) { - logger.error("Failed to write metric to atlas: " + message); - } - } - return PublishMetricsBatchStatus.PartialSuccess; - } - } - catch (HttpClientErrorException e) { - logger.error("Failed to write metrics to atlas: " + e.getResponseBodyAsString()); - return PublishMetricsBatchStatus.Failure; - } - catch (RestClientException e) { - logger.error("Failed to write metrics to atlas", e); - return PublishMetricsBatchStatus.Failure; - } - } - catch (IOException e) { - return PublishMetricsBatchStatus.Failure; - } - - return PublishMetricsBatchStatus.Success; - } - - private void writeCommonTags(JsonGenerator gen) throws IOException { - gen.writeObjectFieldStart("tags"); - for (Tag tag : commonTags) - gen.writeStringField(tag.getKey(), tag.getValue()); - gen.writeEndObject(); - } - - private int writeMetrics(JsonGenerator gen, List metrics) throws IOException { - int totalMetricsInBatch = 0; - gen.writeArrayFieldStart("metrics"); - - for (Metric m : metrics) { - if (!validTags(m.getConfig().getTags())) - continue; - - if (!Number.class.isAssignableFrom(m.getValue().getClass())) - continue; - - gen.writeStartObject(); - - gen.writeObjectFieldStart("tags"); - gen.writeStringField("name", m.getConfig().getName()); - for (Tag tag : m.getConfig().getTags()) - gen.writeStringField(tag.getKey(), tag.getValue()); - gen.writeEndObject(); - - gen.writeNumberField("start", m.getTimestamp()); - gen.writeNumberField("value", m.getNumberValue().doubleValue()); - - gen.writeEndObject(); - - totalMetricsInBatch++; - } - - gen.writeEndArray(); - return totalMetricsInBatch; - } - - static List sanitizeTags(List metrics) { - List sanitized = new ArrayList<>(metrics.size()); - for (Metric m : metrics) { - MonitorConfig.Builder config = MonitorConfig.builder(toValidCharset(m.getConfig().getName())); - for (Tag tag : m.getConfig().getTags()) { - config.withTag(toValidCharset(tag.getKey()), toValidCharset(tag.getValue())); - } - config.withPublishingPolicy(m.getConfig().getPublishingPolicy()); - sanitized.add(new Metric(config.build(), m.getTimestamp(), m.getValue())); - } - return sanitized; - } - - private static String toValidCharset(String name) { - return name.replaceAll("[^\\.\\-\\w]", "_"); - } - - static List addTypeTagsAsNecessary(List metrics) { - List typedMetrics = new ArrayList<>(metrics.size()); - for (Metric m : metrics) { - String value = m.getConfig().getTags().getValue(DataSourceType.KEY); - Metric transformed; - - // Atlas will not normalize metrics tagged with atlas.dstype=gauge. Since - // these metric types are pre-normalized, we do not want Atlas to touch the - // value - if (DataSourceType.GAUGE.name().equals(value) - || DataSourceType.RATE.name().equals(value) - || DataSourceType.NORMALIZED.name().equals(value)) { - transformed = new Metric(m.getConfig().withAdditionalTag(atlasGaugeTag), - m.getTimestamp(), m.getValue()); - } - - // atlas.dstype=counter means you're sending the absolute value of the counter - // (a monotonically increasing value), and Atlas will keep the previous value - // and convert it to a rate per second when the metric is received - else if (DataSourceType.COUNTER.name().equals(value)) { - transformed = new Metric( - m.getConfig().withAdditionalTag(atlasCounterTag), - m.getTimestamp(), m.getValue()); - } - - // Atlas will normalize the value to a minute boundary based on its timestamp - else { - transformed = new Metric(m.getConfig().withAdditionalTag(atlasRateTag), - m.getTimestamp(), m.getValue()); - } - - typedMetrics.add(transformed); - } - return typedMetrics; - } -} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverConfigBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverConfigBean.java deleted file mode 100644 index 9c04eac4..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverConfigBean.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * @author Jon Schneider - */ -@ConfigurationProperties("netflix.atlas") -public class AtlasMetricObserverConfigBean { - private String uri; - private boolean enabled = true; - private Integer batchSize = 10000; - - public boolean isEnabled() { - return enabled; - } - - public int getBatchSize() { - return batchSize; - } - - public String getUri() { - return uri; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public void setBatchSize(Integer batchSize) { - this.batchSize = batchSize; - } - - public void setUri(String uri) { - this.uri = uri; - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasTagProvider.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasTagProvider.java deleted file mode 100644 index e05e49c5..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/AtlasTagProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import java.util.Map; - -/** - * Provide implementations of this interface in your application context to add a set of static tags to every metric - * sent to Atlas. - * - * @author Jon Schneider - */ -public interface AtlasTagProvider { - Map defaultTags(); -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java deleted file mode 100644 index 476e636e..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/atlas/EnableAtlas.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.Import; - -/** - * Annotation for clients to enable Atlas metrics publishing. - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@Inherited -@Import(AtlasConfiguration.class) -public @interface EnableAtlas { -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/DimensionalServoMetricNaming.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/DimensionalServoMetricNaming.java deleted file mode 100644 index 27c3e09e..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/DimensionalServoMetricNaming.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.util.StringUtils; - -import com.netflix.servo.monitor.Monitor; -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.tag.Tag; - -/** - * @author Jon Schneider - */ -public class DimensionalServoMetricNaming implements ServoMetricNaming { - @Override - public String asHierarchicalName(Monitor monitor) { - MonitorConfig config = monitor.getConfig(); - List tags = new ArrayList<>(config.getTags().size()); - for (Tag t : config.getTags()) { - tags.add(t.getKey() + "=" + t.getValue()); - } - return config.getName() + "(" + StringUtils.collectionToCommaDelimitedString(tags) + ")"; - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/HierarchicalServoMetricNaming.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/HierarchicalServoMetricNaming.java deleted file mode 100644 index 2f8b4ab5..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/HierarchicalServoMetricNaming.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import com.netflix.servo.annotations.DataSourceType; -import com.netflix.servo.monitor.Monitor; -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.tag.Tag; -import com.netflix.servo.tag.TagList; - -/** - * @author Spencer Gibb - */ -public class HierarchicalServoMetricNaming implements ServoMetricNaming { - private static final String JMX_DOMAIN_KEY = "JmxDomain"; - public static final String SERVO = "servo."; - - @Override - public String asHierarchicalName(Monitor monitor) { - MonitorConfig config = monitor.getConfig(); - TagList tags = config.getTags(); - - Tag domainTag = tags.getTag(JMX_DOMAIN_KEY); - String name; - if (domainTag != null) { // jmx metric - name = handleJmxMetric(config, tags); - } else { - name = handleMetric(config, tags); - } - return name.toLowerCase(); - } - - private String handleMetric(MonitorConfig config, TagList tags) { - String type = cleanValue(tags.getTag(DataSourceType.KEY), false); - String instanceName = cleanValue(tags.getTag("instance"), false); - String name = cleanupIllegalCharacters(config.getName(), true); - String statistic = cleanValue(tags.getTag("statistic"), false); - - StringBuilder nameBuilder = new StringBuilder(); - if (type != null) { - nameBuilder.append(type).append("."); - } - nameBuilder.append(SERVO); - if (instanceName != null) { - nameBuilder.append(instanceName).append("."); - } - if (name != null) { - nameBuilder.append(name).append("."); - } - if (statistic != null) { - nameBuilder.append(statistic).append("."); - } - // remove trailing "." - nameBuilder.deleteCharAt(nameBuilder.lastIndexOf(".")); - return nameBuilder.toString(); - } - - private String handleJmxMetric(MonitorConfig config, TagList tags) { - String domain = cleanValue(tags.getTag(JMX_DOMAIN_KEY), true); - String type = cleanValue(tags.getTag("Jmx.type"), false); - String instanceName = cleanValue(tags.getTag("Jmx.instance"), false); - String name = cleanValue(tags.getTag("Jmx.name"), true); - String fieldName = cleanupIllegalCharacters(config.getName(), false); - - StringBuilder nameBuilder = new StringBuilder(); - nameBuilder.append(domain).append("."); - if (type != null) { - nameBuilder.append(type).append("."); - } - nameBuilder.append(SERVO); - if (instanceName != null) { - nameBuilder.append(instanceName).append("."); - } - if (name != null) { - nameBuilder.append(name).append("."); - } - if (fieldName != null) { - nameBuilder.append(fieldName).append("."); - } - // remove trailing "." - nameBuilder.deleteCharAt(nameBuilder.lastIndexOf(".")); - return nameBuilder.toString(); - } - - private String cleanValue(Tag tag, boolean allowPeriodsInName) { - if (tag == null) { - return null; - } - - return cleanupIllegalCharacters(tag.getValue(), allowPeriodsInName); - } - - private String cleanupIllegalCharacters(String s, boolean allowPeriodsInName) { - if (!allowPeriodsInName) { - s = s.replace(".", "_"); - } - return s.replace(" ", "_"); - } -} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricNaming.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricNaming.java deleted file mode 100644 index 4fc240a1..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricNaming.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import com.netflix.servo.monitor.Monitor; - -/** - * @author Spencer Gibb - */ -public interface ServoMetricNaming { - /** - * @param monitor a monitor representing a single statistic (not a CompositeMonitor) - * @return a hierarchical name representing a single statistic for a servo monitor; - * note that this method will be called once for each statistic on a composite servo - * Monitor like a Timer. - */ - String asHierarchicalName(Monitor monitor); -} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricReader.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricReader.java deleted file mode 100644 index d1acebd7..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricReader.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import java.util.ArrayList; -import java.util.Collection; - -import org.springframework.boot.actuate.metrics.Metric; -import org.springframework.boot.actuate.metrics.reader.MetricReader; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.CompositeMonitor; -import com.netflix.servo.monitor.Monitor; - -/** - * @author Jon Schneider - */ -public class ServoMetricReader implements MetricReader { - MonitorRegistry monitorRegistry; - ServoMetricNaming metricNaming; - - public ServoMetricReader(MonitorRegistry monitorRegistry, - ServoMetricNaming metricNaming) { - this.monitorRegistry = monitorRegistry; - this.metricNaming = metricNaming; - } - - @Override - public Metric findOne(String s) { - throw new UnsupportedOperationException( - "cannot construct a tag-based Servo id from a hierarchical name"); - } - - @Override - public Iterable> findAll() { - Collection> metrics = new ArrayList<>(); - for (Monitor monitor : monitorRegistry.getRegisteredMonitors()) { - addToMetrics(monitor, metrics); - } - return metrics; - } - - private void addToMetrics(Monitor monitor, Collection> metrics) { - if (monitor instanceof CompositeMonitor) { - for (Monitor nestedMonitor : ((CompositeMonitor) monitor).getMonitors()) { - addToMetrics(nestedMonitor, metrics); - } - } - else if (monitor.getValue() instanceof Number) { - // Servo does support non-numeric values, but there is no such concept in - // Spring Boot - metrics.add(new Metric<>(metricNaming.asHierarchicalName(monitor), - (Number) monitor.getValue())); - } - } - - @Override - public long count() { - long count = 0; - for (Monitor monitor : monitorRegistry.getRegisteredMonitors()) { - count += countMetrics(monitor); - } - return count; - } - - private static long countMetrics(Monitor monitor) { - if (monitor instanceof CompositeMonitor) { - long count = 0; - for (Monitor nestedMonitor : ((CompositeMonitor) monitor).getMonitors()) { - count += countMetrics(nestedMonitor); - } - return count; - } - return 1; - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricServices.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricServices.java deleted file mode 100644 index 2a163c10..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricServices.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.BasicCounter; -import com.netflix.servo.monitor.BasicDistributionSummary; -import com.netflix.servo.monitor.BasicTimer; -import com.netflix.servo.monitor.DoubleGauge; -import com.netflix.servo.monitor.LongGauge; -import com.netflix.servo.monitor.MonitorConfig; - -/** - * Provides a CounterService and GaugeService implementation - * backed by Servo. - * - * @author Jon Schneider - */ -public class ServoMetricServices implements CounterService, GaugeService { - private final MonitorRegistry registry; - - private final ConcurrentMap counters = new ConcurrentHashMap<>(); - private final ConcurrentMap longGauges = new ConcurrentHashMap<>(); - private final ConcurrentMap doubleGauges = new ConcurrentHashMap<>(); - private final ConcurrentMap distributionSummaries = new ConcurrentHashMap<>(); - private final ConcurrentMap timers = new ConcurrentHashMap<>(); - - public ServoMetricServices(MonitorRegistry registry) { - this.registry = registry; - } - - protected static String stripMetricName(String metricName) { - return metricName.replaceFirst("^(timer|histogram|meter)\\.", ""); - } - - @Override - public void increment(String name) { - incrementInternal(name, 1L); - } - - @Override - public void decrement(String name) { - incrementInternal(name, -1L); - } - - private void incrementInternal(String name, long value) { - String strippedName = stripMetricName(name); - - if (name.startsWith("status.")) { - // drop this metric since we are capturing it already with - // ServoHandlerInterceptor, - // and we are able to glean more information like exceptionType from that - // mechanism than what - // boot provides us - } - else if (name.startsWith("meter.")) { - BasicCounter counter = counters.get(strippedName); - if (counter == null) { - counter = new BasicCounter(MonitorConfig.builder(strippedName).build()); - counters.put(strippedName, counter); - registry.register(counter); - } - counter.increment(value); - } - else { - LongGauge gauge = longGauges.get(strippedName); - if (gauge == null) { - gauge = new LongGauge(MonitorConfig.builder(strippedName).build()); - longGauges.put(strippedName, gauge); - registry.register(gauge); - } - gauge.set(value); - } - } - - @Override - public void reset(String name) { - String strippedName = stripMetricName(name); - BasicCounter counter = counters.remove(strippedName); - if (counter != null) - registry.unregister(counter); - - LongGauge gauge = longGauges.remove(strippedName); - if (gauge != null) - registry.unregister(gauge); - - BasicDistributionSummary distributionSummary = distributionSummaries - .remove(strippedName); - if (distributionSummary != null) - registry.unregister(distributionSummary); - } - - @Override - public void submit(String name, double dValue) { - long value = ((Double) dValue).longValue(); - String strippedName = stripMetricName(name); - if (name.startsWith("histogram.")) { - BasicDistributionSummary distributionSummary = distributionSummaries - .get(strippedName); - if (distributionSummary == null) { - distributionSummary = new BasicDistributionSummary(MonitorConfig.builder( - strippedName).build()); - distributionSummaries.put(strippedName, distributionSummary); - registry.register(distributionSummary); - } - distributionSummary.record(value); - } - else if (name.startsWith("timer.")) { - BasicTimer timer = timers.get(strippedName); - if (timer == null) { - timer = new BasicTimer(MonitorConfig.builder(strippedName).build()); - timers.put(strippedName, timer); - registry.register(timer); - } - timer.record(value, TimeUnit.MILLISECONDS); - } - else { - DoubleGauge gauge = doubleGauges.get(strippedName); - if (gauge == null) { - gauge = new DoubleGauge(MonitorConfig.builder(strippedName).build()); - doubleGauges.put(strippedName, gauge); - registry.register(gauge); - } - gauge.set(dValue); - } - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsAutoConfiguration.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsAutoConfiguration.java deleted file mode 100644 index 7a12286f..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsAutoConfiguration.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.metrics.servo; - -import org.springframework.boot.actuate.autoconfigure.metrics.ExportMetricReader; -import org.springframework.boot.actuate.autoconfigure.metrics.MetricRepositoryAutoConfiguration; -import org.springframework.boot.actuate.endpoint.MetricReaderPublicMetrics; -import org.springframework.boot.actuate.metrics.CounterService; -import org.springframework.boot.actuate.metrics.GaugeService; -import org.springframework.boot.actuate.metrics.reader.MetricReader; -import org.springframework.boot.autoconfigure.AutoConfigureBefore; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.cloud.netflix.metrics.DefaultMetricsTagProvider; -import org.springframework.cloud.netflix.metrics.MetricsInterceptorConfiguration; -import org.springframework.cloud.netflix.metrics.MetricsTagProvider; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -import com.netflix.servo.DefaultMonitorRegistry; -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.Monitors; - -/** - * Auto configuration to configure Servo support. - * - * @author Dave Syer - * @author Christian Dupuis - * @author Jon Schneider - */ -@Configuration -@ConditionalOnClass({ Monitors.class, MetricReader.class }) -@ConditionalOnMissingClass("com.netflix.spectator.api.Registry") -@AutoConfigureBefore(MetricRepositoryAutoConfiguration.class) -@Import(MetricsInterceptorConfiguration.class) -@ConditionalOnProperty(name = "spring.metrics.servo.enabled", matchIfMissing = true) -public class ServoMetricsAutoConfiguration { - @Bean - @ConditionalOnMissingBean - public ServoMetricsConfigBean servoMetricsConfig() { - return new ServoMetricsConfigBean(); - } - - @Bean - @ConditionalOnMissingBean - public ServoMetricNaming servoMetricNaming() { - return new HierarchicalServoMetricNaming(); - } - - @Bean - @ConditionalOnMissingBean - public MonitorRegistry monitorRegistry(ServoMetricsConfigBean servoMetricsConfig) { - System.setProperty( - DefaultMonitorRegistry.class.getCanonicalName() + ".registryClass", - servoMetricsConfig.getRegistryClass()); - return DefaultMonitorRegistry.getInstance(); - } - - @Bean - public ServoMonitorCache monitorCache(MonitorRegistry monitorRegistry, ServoMetricsConfigBean servoMetricsConfig) { - return new ServoMonitorCache(monitorRegistry, servoMetricsConfig); - } - - @Bean - @ExportMetricReader - public ServoMetricReader servoMetricReader(MonitorRegistry monitorRegistry, - ServoMetricNaming servoMetricNaming) { - ServoMetricReader reader = new ServoMetricReader(monitorRegistry, - servoMetricNaming); - return reader; - } - - @Bean - public MetricReaderPublicMetrics servoPublicMetrics(ServoMetricReader reader) { - return new MetricReaderPublicMetrics(reader); - } - - @Bean - @ConditionalOnMissingBean({ CounterService.class, GaugeService.class }) - public ServoMetricServices servoMetricServices(MonitorRegistry monitorRegistry) { - return new ServoMetricServices(monitorRegistry); - } - - @Configuration - @ConditionalOnClass(name = "javax.servlet.http.HttpServletRequest") - protected static class MetricsTagConfiguration { - @Bean - public MetricsTagProvider defaultMetricsTagProvider() { - return new DefaultMetricsTagProvider(); - } - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsConfigBean.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsConfigBean.java deleted file mode 100644 index cbae9f50..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMetricsConfigBean.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import org.springframework.boot.context.properties.ConfigurationProperties; - -/** - * Configuration properties to configure Servo support. - * - * @author Jon Schneider - */ -@ConfigurationProperties("netflix.metrics.servo") -public class ServoMetricsConfigBean { - - /** - * Enable the Netflix Servo metrics services. If this flag is off Servo can still be - * used by Netflix OSS components, but the Spring Boot metrics collection will be done - * with the default services. - */ - boolean enabled = true; - /** - * Fully qualified class name for monitor registry used by Servo. - */ - String registryClass = "com.netflix.servo.BasicMonitorRegistry"; - - /** - * When the `ServoMonitorCache` reaches this size, a warning is logged. - * This will be useful if you are using string concatenation in RestTemplate urls. - */ - int cacheWarningThreshold = 1000; - - public boolean getEnabled() { - return this.enabled; - } - - public void isEnabled(boolean enabled) { - this.enabled = enabled; - } - - public String getRegistryClass() { - return this.registryClass; - } - - public void setRegistryClass(String registryClass) { - this.registryClass = registryClass; - } - - public int getCacheWarningThreshold() { - return cacheWarningThreshold; - } - - public void setCacheWarningThreshold(int cacheWarningThreshold) { - this.cacheWarningThreshold = cacheWarningThreshold; - } -} diff --git a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMonitorCache.java b/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMonitorCache.java deleted file mode 100644 index 79fb2c65..00000000 --- a/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/metrics/servo/ServoMonitorCache.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.servo; - -import java.util.HashMap; -import java.util.Map; - -import com.netflix.servo.MonitorRegistry; -import com.netflix.servo.monitor.BasicTimer; -import com.netflix.servo.monitor.MonitorConfig; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Servo does not provide a mechanism to retrieve an existing monitor by name + tags. - * - * @author Jon Schneider - */ -public class ServoMonitorCache { - - private static final Log log = LogFactory.getLog(ServoMonitorCache.class); - - private final Map timerCache = new HashMap<>(); - private final MonitorRegistry monitorRegistry; - private final ServoMetricsConfigBean config; - - public ServoMonitorCache(MonitorRegistry monitorRegistry, ServoMetricsConfigBean config) { - this.monitorRegistry = monitorRegistry; - this.config = config; - } - - /** - * @param config contains the name and tags that uniquely identify a timer - * @return an already registered timer if it exists, otherwise create/register one and - * return it. - */ - public synchronized BasicTimer getTimer(MonitorConfig config) { - BasicTimer t = this.timerCache.get(config); - if (t != null) - return t; - - t = new BasicTimer(config); - this.timerCache.put(config, t); - - if (this.timerCache.size() > this.config.getCacheWarningThreshold()) { - log.warn("timerCache is above the warning threshold of " + this.config.getCacheWarningThreshold() + " with size " + this.timerCache.size() + "."); - } - - this.monitorRegistry.register(t); - return t; - } -} diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java deleted file mode 100644 index f351f42a..00000000 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasExporterTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013-2015 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.metrics.atlas; - -import java.util.Properties; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.test.web.client.match.MockRestRequestMatchers; -import org.springframework.test.web.client.response.MockRestResponseCreators; -import org.springframework.web.client.RestTemplate; - -import com.netflix.servo.monitor.DynamicCounter; - -/** - * @author Jon Schneider - */ -@SpringBootTest(classes = AtlasExporterConfiguration.class) -@RunWith(SpringJUnit4ClassRunner.class) -public class AtlasExporterTests { - @Autowired - private RestTemplate restTemplate; - - @Autowired - private AtlasExporter atlasExporter; - - @Test - public void exportMetricsAtPeriodicIntervals() { - MockRestServiceServer mockServer = MockRestServiceServer - .createServer(this.restTemplate); - - mockServer.expect(MockRestRequestMatchers.requestTo("/atlas/api/v1/publish")) - .andExpect(MockRestRequestMatchers.method(HttpMethod.POST)) - .andRespond(MockRestResponseCreators.withSuccess("{\"status\" : \"OK\"}", - MediaType.APPLICATION_JSON)); - - DynamicCounter.increment("counterThatWillBeSentToAtlas"); - this.atlasExporter.export(); - - mockServer.verify(); - } -} - -@EnableAutoConfiguration -@Configuration -@EnableAtlas -class AtlasExporterConfiguration { - - @Qualifier("atlasRestTemplate") - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } - - @Bean - public static PropertySourcesPlaceholderConfigurer properties() throws Exception { - final PropertySourcesPlaceholderConfigurer config = new PropertySourcesPlaceholderConfigurer(); - Properties properties = new Properties(); - properties.setProperty("netflix.atlas.uri", "atlas"); - config.setProperties(properties); - return config; - } -} \ No newline at end of file diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverTests.java deleted file mode 100644 index 65c0bed2..00000000 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/atlas/AtlasMetricObserverTests.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright 2013-2017 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.metrics.atlas; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import org.junit.Test; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.web.client.MockRestServiceServer; -import org.springframework.test.web.client.match.MockRestRequestMatchers; -import org.springframework.test.web.client.response.MockRestResponseCreators; -import org.springframework.web.client.RestTemplate; - -import com.netflix.servo.Metric; -import com.netflix.servo.annotations.DataSourceType; -import com.netflix.servo.monitor.MonitorConfig; -import com.netflix.servo.tag.BasicTagList; -import com.netflix.servo.tag.Tag; - -import static com.netflix.servo.annotations.DataSourceType.COUNTER; -import static com.netflix.servo.annotations.DataSourceType.GAUGE; -import static com.netflix.servo.annotations.DataSourceType.INFORMATIONAL; -import static com.netflix.servo.annotations.DataSourceType.KEY; -import static com.netflix.servo.annotations.DataSourceType.NORMALIZED; -import static com.netflix.servo.annotations.DataSourceType.RATE; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.core.Is.is; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -/** - * @author Jon Schneider - */ -public class AtlasMetricObserverTests { - @Test - public void normalizeAtlasUri() { - String normalized = "http://localhost:7001/api/v1/publish"; - assertThat(AtlasMetricObserver.normalizeAtlasUri("http://localhost:7001"), is(equalTo(normalized))); - assertThat(AtlasMetricObserver.normalizeAtlasUri("http://localhost:7001/"), is(equalTo(normalized))); - assertThat(AtlasMetricObserver.normalizeAtlasUri("http://localhost:7001/api/v1/publish"), is(equalTo(normalized))); - assertThat(AtlasMetricObserver.normalizeAtlasUri("http://localhost:7001/api/v1/publish/"), is(equalTo(normalized))); - } - - @Test(expected = IllegalStateException.class) - public void emptyAtlasUriThrowsException() { - AtlasMetricObserver.normalizeAtlasUri(""); - } - - @Test(expected = IllegalStateException.class) - public void missingAtlasUriThrowsException() { - AtlasMetricObserver.normalizeAtlasUri(null); - } - - @Test - public void checkValidityOfTags() { - assertTrue(AtlasMetricObserver.validTags(BasicTagList.of("foo", "bar"))); - assertFalse(AtlasMetricObserver.validTags(BasicTagList.of("{foo}", "bar"))); - assertFalse(AtlasMetricObserver.validTags(BasicTagList.of("foo", "{bar}"))); - } - - @Test - public void assignTypesToMetrics() { - assertHasAtlasType("counter", metricWithType("foo", COUNTER)); - - assertHasAtlasType("gauge", metricWithType("foo", GAUGE)); - assertHasAtlasType("gauge", metricWithType("foo", NORMALIZED)); - assertHasAtlasType("gauge", metricWithType("foo", RATE)); - - assertHasAtlasType("rate", metricWithType("foo", INFORMATIONAL)); - assertHasAtlasType("rate", new Metric(new MonitorConfig.Builder("foo").build(), - System.currentTimeMillis(), "bar")); - - // already has type - Metric m = new Metric(new MonitorConfig.Builder("foo") - .withTag(KEY, COUNTER.name()).withTag("atlas.dstype", "counter").build(), - System.currentTimeMillis(), "bar"); - assertHasAtlasType("counter", m); - assertThat(m.getConfig().getTags().size(), is(equalTo(2))); - } - - private void assertHasAtlasType(String atlasType, Metric m) { - assertThat(AtlasMetricObserver.addTypeTagsAsNecessary(Collections.singletonList(m)) - .get(0).getConfig().getTags().getValue("atlas.dstype"), is(equalTo(atlasType))); - } - - private Metric metricWithType(String key, DataSourceType type) { - return new Metric(new MonitorConfig.Builder(key).withTag(KEY, type.name()) - .build(), System.currentTimeMillis(), 1); - } - - @Test - public void metricsSentInBatches() { - RestTemplate restTemplate = new RestTemplate(); - - AtlasMetricObserverConfigBean config = new AtlasMetricObserverConfigBean(); - config.setBatchSize(2); - config.setUri("atlas"); - - AtlasMetricObserver obs = new AtlasMetricObserver(config, restTemplate, - BasicTagList.EMPTY); - - // batch size is divisible by metric size - MockRestServiceServer mockServer = MockRestServiceServer - .createServer(restTemplate); - expectTotalBatches(mockServer, 2); - obs.update(generateMetrics(4)); - mockServer.verify(); - - // batch size is not divisible by metric size - mockServer = MockRestServiceServer.createServer(restTemplate); - expectTotalBatches(mockServer, 3); - obs.update(generateMetrics(5)); - mockServer.verify(); - - // metric size is less than batch size - mockServer = MockRestServiceServer.createServer(restTemplate); - expectTotalBatches(mockServer, 1); - obs.update(generateMetrics(1)); - mockServer.verify(); - - // no metrics to send - mockServer = MockRestServiceServer.createServer(restTemplate); - expectTotalBatches(mockServer, 0); - obs.update(Collections. emptyList()); - mockServer.verify(); - - // a single non-numeric metric does not result in a post - mockServer = MockRestServiceServer.createServer(restTemplate); - expectTotalBatches(mockServer, 0); - obs.update(Collections.singletonList(new Metric(new MonitorConfig.Builder("foo") - .build(), 0, "nonumber"))); - mockServer.verify(); - } - - /** - * If ALL of the metrics in a batch fail, Atlas will return a 400 with a String body indicating why. - */ - @Test - public void failingMetricsBatch() { - RestTemplate restTemplate = new RestTemplate(); - - AtlasMetricObserverConfigBean config = new AtlasMetricObserverConfigBean(); - config.setBatchSize(1); - config.setUri("atlas"); - - MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); - mockServer - .expect(MockRestRequestMatchers.requestTo("/atlas/api/v1/publish")) - .andExpect(MockRestRequestMatchers.method(HttpMethod.POST)) - .andRespond(MockRestResponseCreators.withBadRequest().body("foo0 is bad for some reason")); - - AtlasMetricObserver obs = new AtlasMetricObserver(config, restTemplate, BasicTagList.EMPTY); - - assertThat(obs.sendMetricsBatch(generateMetrics(1)), - is(equalTo(AtlasMetricObserver.PublishMetricsBatchStatus.Failure))); - } - - /** - * If SOME metrics in a batch fail, Atlas will return a 202 with a JSON body with a message for each - * failing metric. - */ - @Test - public void partialSuccessMetricsBatch() { - RestTemplate restTemplate = new RestTemplate(); - - AtlasMetricObserverConfigBean config = new AtlasMetricObserverConfigBean(); - config.setBatchSize(2); - config.setUri("atlas"); - - MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate); - mockServer - .expect(MockRestRequestMatchers.requestTo("/atlas/api/v1/publish")) - .andExpect(MockRestRequestMatchers.method(HttpMethod.POST)) - .andRespond( - MockRestResponseCreators.withStatus(HttpStatus.ACCEPTED) - .body("{\"message\" : [\"foo1 is bad for some reason\"]}") - .contentType(MediaType.APPLICATION_JSON)); - - AtlasMetricObserver obs = new AtlasMetricObserver(config, restTemplate, BasicTagList.EMPTY); - - assertThat(obs.sendMetricsBatch(generateMetrics(2)), - is(equalTo(AtlasMetricObserver.PublishMetricsBatchStatus.PartialSuccess))); - } - - @Test - public void sanitizeMetrics() { - String mixtureOfValidAndInvalidChars = "a_1.2-Z/ A"; - - Metric m = new Metric(new MonitorConfig.Builder(mixtureOfValidAndInvalidChars) - .withTag(mixtureOfValidAndInvalidChars, mixtureOfValidAndInvalidChars).build(), 0, 1); - - Metric sanitizedMetric = AtlasMetricObserver.sanitizeTags(Collections.singletonList(m)).get(0); - - String valid = "a_1.2-Z__A"; - assertThat(sanitizedMetric.getConfig().getName(), is(equalTo(valid))); - - Tag tag = sanitizedMetric.getConfig().getTags().iterator().next(); - assertThat(tag.getKey(), is(equalTo(valid))); - assertThat(tag.getValue(), is(equalTo(valid))); - } - - private List generateMetrics(int numberOfMetrics) { - List metrics = new ArrayList<>(); - for (int i = 0; i < numberOfMetrics; i++) - metrics.add(metricWithType("foo" + i, DataSourceType.GAUGE)); - return metrics; - } - - private void expectTotalBatches(MockRestServiceServer mockServer, - int totalBatchesExpected) { - for (int i = 0; i < totalBatchesExpected; i++) { - mockServer - .expect(MockRestRequestMatchers.requestTo("/atlas/api/v1/publish")) - .andExpect(MockRestRequestMatchers.method(HttpMethod.POST)) - .andRespond( - MockRestResponseCreators.withSuccess("{\"status\" : \"OK\"}", - MediaType.APPLICATION_JSON)); - } - } -} diff --git a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/.jdk8 b/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/.jdk8 deleted file mode 100644 index e69de29b..00000000 diff --git a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/pom.xml b/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/pom.xml deleted file mode 100644 index ee2f5619..00000000 --- a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/pom.xml +++ /dev/null @@ -1,30 +0,0 @@ - - 4.0.0 - - org.springframework.cloud - spring-cloud-starter-netflix - 2.0.0.BUILD-SNAPSHOT - - spring-cloud-starter-netflix-spectator - Spring Cloud Starter Netflix Spectator - Spring Cloud Starter Netflix Spectator - https://projects.spring.io/spring-cloud - - Pivotal Software, Inc. - https://www.spring.io - - - ${basedir}/../../.. - - - - org.springframework.cloud - spring-cloud-starter - - - org.springframework.cloud - spring-cloud-netflix-spectator - - - \ No newline at end of file diff --git a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/src/main/resources/META-INF/spring.provides b/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/src/main/resources/META-INF/spring.provides deleted file mode 100644 index b33a3efa..00000000 --- a/spring-cloud-starter-netflix/spring-cloud-starter-netflix-spectator/src/main/resources/META-INF/spring.provides +++ /dev/null @@ -1 +0,0 @@ -provides: spring-cloud-starter, spring-cloud-netflix-spectator \ No newline at end of file