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 index 226984b1..ea88aefe 100644 --- 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 @@ -99,7 +99,10 @@ public class DefaultMetricsTagProvider implements MetricsTagProvider { * Atlas take place via query parameters */ protected String sanitizeUrlTemplate(String urlTemplate) { - String sanitized = urlTemplate.replaceAll("/", "_").replaceAll("[{}]", "-"); + String sanitized = urlTemplate + .replaceAll("\\{(\\w+):.+}(?=/|$)", "-$1-") // extract path variable names from regex expressions + .replaceAll("/", "_") + .replaceAll("[{}]", "-"); if (!StringUtils.hasText(sanitized)) { sanitized = "none"; } diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptorIntegrationTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptorIntegrationTests.java index dd7cf2c0..25565f22 100644 --- a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptorIntegrationTests.java +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/MetricsHandlerInterceptorIntegrationTests.java @@ -112,6 +112,12 @@ public class MetricsHandlerInterceptorIntegrationTests { mvc.perform(get("/test/some/error/10")).andExpect(status().is4xxClientError()); assertTimer("test_some_error_-id-", null, 422); } + + @Test + public void metricsGatheredOnRequestMappingWithRegex() throws Exception { + mvc.perform(get("/test/some/regex/.aa")).andExpect(status().isOk()); + assertTimer("test_some_regex_-id-", null, 200); + } protected void assertTimer(String uriTag, String exceptionType, Integer status) { MonitorConfig.Builder builder = new MonitorConfig.Builder("metricName") @@ -161,7 +167,12 @@ class MetricsTestController { public String testSomeUnhandledError(@PathVariable Long id) { throw new RuntimeException("Boom on $id!"); } - + + @RequestMapping("/regex/{id:\\.[a-z]+}") + public String testSomeRegexRequest(@PathVariable String id) { + return id; + } + @ExceptionHandler(value = IllegalStateException.class) @ResponseStatus(code = HttpStatus.UNPROCESSABLE_ENTITY) ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) { diff --git a/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/servo/DefaultMetricsTagProviderTests.java b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/servo/DefaultMetricsTagProviderTests.java new file mode 100644 index 00000000..652bd784 --- /dev/null +++ b/spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/metrics/servo/DefaultMetricsTagProviderTests.java @@ -0,0 +1,58 @@ +/* + * 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 static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.springframework.web.servlet.HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Test; +import org.springframework.cloud.netflix.metrics.DefaultMetricsTagProvider; +import org.springframework.cloud.netflix.metrics.MetricsTagProvider; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +public class DefaultMetricsTagProviderTests { + MetricsTagProvider provider = new DefaultMetricsTagProvider(); + HttpServletResponse response = new MockHttpServletResponse(); + + @Test + public void encodeSlashes() { + HttpServletRequest request = new MockHttpServletRequest("GET", "/test/some"); + request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, "/test/some"); + assertUriTagIsEqualTo(request, "test_some"); + } + + @Test + public void encodePathVariables() { + HttpServletRequest request = new MockHttpServletRequest("GET", "/test/some/1"); + request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, "/test/some/{id}"); + assertUriTagIsEqualTo(request, "test_some_-id-"); + } + + @Test + public void encodeRegexBasedUri() { + HttpServletRequest request = new MockHttpServletRequest("GET", "/test/some/regex/a{}"); + request.setAttribute(BEST_MATCHING_PATTERN_ATTRIBUTE, "/test/some/regex/{id:.+{}}"); + assertUriTagIsEqualTo(request, "test_some_regex_-id-"); + } + + private void assertUriTagIsEqualTo(HttpServletRequest request, String expectedTag) { + assertThat(provider.httpRequestTags(request, response, null, null).get("uri"), is(equalTo(expectedTag))); + } +}