diff --git a/cf-java-logging-support-log4j2/pom.xml b/cf-java-logging-support-log4j2/pom.xml index b792c832..7f0892fa 100644 --- a/cf-java-logging-support-log4j2/pom.xml +++ b/cf-java-logging-support-log4j2/pom.xml @@ -48,6 +48,25 @@ + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + add-jmh-sources + generate-test-sources + + add-test-source + + + + src/jmh/java + + + + + org.apache.maven.plugins maven-compiler-plugin @@ -72,6 +91,7 @@ org.apache.maven.plugins maven-surefire-plugin + ${surefire.plugin.version} true @@ -81,4 +101,38 @@ + + + + benchmark + + + + org.codehaus.mojo + exec-maven-plugin + ${exec.plugin.version} + + + run-benchmarks + verify + + exec + + + ${java.home}/bin/java + test + + -classpath + + com.sap.hcp.cf.log4j2.benchmark.BenchmarkRunner + + + + + + + + + + diff --git a/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/benchmark/BenchmarkRunner.java b/cf-java-logging-support-log4j2/src/jmh/java/com/sap/hcp/cf/log4j2/benchmark/BenchmarkRunner.java similarity index 100% rename from cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/benchmark/BenchmarkRunner.java rename to cf-java-logging-support-log4j2/src/jmh/java/com/sap/hcp/cf/log4j2/benchmark/BenchmarkRunner.java diff --git a/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java b/cf-java-logging-support-log4j2/src/jmh/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java similarity index 93% rename from cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java rename to cf-java-logging-support-log4j2/src/jmh/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java index e42c4b0e..3fb44c8e 100644 --- a/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java +++ b/cf-java-logging-support-log4j2/src/jmh/java/com/sap/hcp/cf/log4j2/benchmark/EncodingBenchmarks.java @@ -15,8 +15,11 @@ import static com.sap.hcp.cf.logging.common.request.RequestRecordBuilder.requestRecord; +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) -@Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) +@Fork(1) public class EncodingBenchmarks { public static Logger LOG = LoggerFactory.getLogger(EncodingBenchmarks.class); diff --git a/cf-java-logging-support-logback/pom.xml b/cf-java-logging-support-logback/pom.xml index 7a6cc712..4ecf7d02 100644 --- a/cf-java-logging-support-logback/pom.xml +++ b/cf-java-logging-support-logback/pom.xml @@ -35,9 +35,29 @@ + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + add-jmh-sources + generate-test-sources + + add-test-source + + + + src/jmh/java + + + + + org.apache.maven.plugins maven-surefire-plugin + ${surefire.plugin.version} true @@ -47,4 +67,38 @@ + + + + benchmark + + + + org.codehaus.mojo + exec-maven-plugin + ${exec.plugin.version} + + + run-benchmarks + verify + + exec + + + ${java.home}/bin/java + test + + -classpath + + com.sap.hcp.cf.logback.benchmark.BenchmarkRunner + + + + + + + + + + diff --git a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/benchmark/BenchmarkRunner.java b/cf-java-logging-support-logback/src/jmh/java/com/sap/hcp/cf/logback/benchmark/BenchmarkRunner.java similarity index 100% rename from cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/benchmark/BenchmarkRunner.java rename to cf-java-logging-support-logback/src/jmh/java/com/sap/hcp/cf/logback/benchmark/BenchmarkRunner.java diff --git a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java b/cf-java-logging-support-logback/src/jmh/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java similarity index 96% rename from cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java rename to cf-java-logging-support-logback/src/jmh/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java index 4f5f502b..7bbd2f3e 100644 --- a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java +++ b/cf-java-logging-support-logback/src/jmh/java/com/sap/hcp/cf/logback/benchmark/EncodingBenchmarks.java @@ -15,8 +15,11 @@ import static com.sap.hcp.cf.logging.common.request.RequestRecordBuilder.requestRecord; +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) @Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) +@Fork(1) public class EncodingBenchmarks { public static Logger LOG = LoggerFactory.getLogger(EncodingBenchmarks.class); diff --git a/cf-java-logging-support-servlet/pom.xml b/cf-java-logging-support-servlet/pom.xml index f1817148..299a1278 100644 --- a/cf-java-logging-support-servlet/pom.xml +++ b/cf-java-logging-support-servlet/pom.xml @@ -15,7 +15,6 @@ 3.5.0 - 3.3.0 3.1.0 @@ -74,4 +73,67 @@ + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${build-helper-maven-plugin.version} + + + add-jmh-sources + generate-test-sources + + add-test-source + + + + src/jmh/java + + + + + + + + + + + benchmark + + + + org.codehaus.mojo + exec-maven-plugin + ${exec.plugin.version} + + + run-benchmarks + verify + + exec + + + ${java.home}/bin/java + test + + -classpath + + com.sap.hcp.cf.logging.servlet.filter.benchmark.BenchmarkRunner + + + + + + + + + + + diff --git a/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/BenchmarkRunner.java b/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/BenchmarkRunner.java new file mode 100644 index 00000000..cf6f0026 --- /dev/null +++ b/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/BenchmarkRunner.java @@ -0,0 +1,15 @@ +package com.sap.hcp.cf.logging.servlet.filter.benchmark; + +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +public class BenchmarkRunner { + + public static void main(String[] args) throws RunnerException { + Options options = + new OptionsBuilder().include(RequestUriMatcherBenchmarks.class.getSimpleName()).forks(1).build(); + new Runner(options).run(); + } +} diff --git a/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/RequestUriMatcherBenchmarks.java b/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/RequestUriMatcherBenchmarks.java new file mode 100644 index 00000000..8b3d0939 --- /dev/null +++ b/cf-java-logging-support-servlet/src/jmh/java/com/sap/hcp/cf/logging/servlet/filter/benchmark/RequestUriMatcherBenchmarks.java @@ -0,0 +1,86 @@ +package com.sap.hcp.cf.logging.servlet.filter.benchmark; + +import com.sap.hcp.cf.logging.servlet.filter.RequestUriMatcher; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) +@Fork(1) +public class RequestUriMatcherBenchmarks { + + @State(Scope.Benchmark) + public static class BenchmarkState { + + // --- matchers --- + public final RequestUriMatcher noPatterns = new RequestUriMatcher(null); + public final RequestUriMatcher exactSingle = new RequestUriMatcher("/health"); + public final RequestUriMatcher wildcardDouble = new RequestUriMatcher("/actuator/**"); + public final RequestUriMatcher wildcardSingle = new RequestUriMatcher("/api/*/status"); + public final RequestUriMatcher multiPattern = + new RequestUriMatcher("/health, /metrics, /actuator/**, /readyz, /livez"); + + // --- URIs --- + public final String uriHealth = "/health"; + public final String uriApiOrders = "/api/orders"; + public final String uriActuatorDeep = "/actuator/health/liveness"; + public final String uriApiStatus = "/api/orders/status"; + public final String uriNoMatch = "/api/v1/orders/123/items"; + } + + /** Baseline: no patterns configured — fastest possible path. */ + @Benchmark + public boolean noPatterns(BenchmarkState s) { + return s.noPatterns.matches(s.uriApiOrders); + } + + /** Single exact pattern, URI matches. */ + @Benchmark + public boolean exactMatch(BenchmarkState s) { + return s.exactSingle.matches(s.uriHealth); + } + + /** Single exact pattern, URI does not match. */ + @Benchmark + public boolean exactNoMatch(BenchmarkState s) { + return s.exactSingle.matches(s.uriApiOrders); + } + + /** Double-wildcard pattern (/actuator/**), deep URI matches. */ + @Benchmark + public boolean doubleWildcardMatch(BenchmarkState s) { + return s.wildcardDouble.matches(s.uriActuatorDeep); + } + + /** Double-wildcard pattern (/actuator/**), URI does not match. */ + @Benchmark + public boolean doubleWildcardNoMatch(BenchmarkState s) { + return s.wildcardDouble.matches(s.uriNoMatch); + } + + /** Single-segment wildcard ({@code /api/*/status}), URI matches. */ + @Benchmark + public boolean singleWildcardMatch(BenchmarkState s) { + return s.wildcardSingle.matches(s.uriApiStatus); + } + + /** + * Realistic actuator-exclusion scenario: five patterns, URI matches the third one (/actuator/**) — worst-case + * traversal through the list. + */ + @Benchmark + public boolean multiPatternMatch(BenchmarkState s) { + return s.multiPattern.matches(s.uriActuatorDeep); + } + + /** + * Realistic actuator-exclusion scenario: five patterns, URI matches none — full list traversal. + */ + @Benchmark + public boolean multiPatternNoMatch(BenchmarkState s) { + return s.multiPattern.matches(s.uriNoMatch); + } +} diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilter.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilter.java index 8c6edb49..63e8e1ad 100644 --- a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilter.java +++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilter.java @@ -23,11 +23,13 @@ public class GenerateRequestLogFilter extends AbstractLoggingFilter { public static final String WRAP_RESPONSE_INIT_PARAM = "wrapResponse"; public static final String WRAP_REQUEST_INIT_PARAM = "wrapRequest"; + public static final String EXCLUDE_PATTERNS_INIT_PARAM = "excludePatterns"; private final RequestRecordFactory requestRecordFactory; private boolean wrapResponse = true; private boolean wrapRequest = true; + private RequestUriMatcher excludeMatcher = new RequestUriMatcher(null); public GenerateRequestLogFilter() { this(new RequestRecordFactory(new LogOptionalFieldsSettings(GenerateRequestLogFilter.class.getName()))); @@ -47,6 +49,7 @@ public void init(FilterConfig filterConfig) throws ServletException { if ("false".equalsIgnoreCase(value)) { wrapRequest = false; } + excludeMatcher = new RequestUriMatcher(filterConfig.getInitParameter(EXCLUDE_PATTERNS_INIT_PARAM)); } @Override @@ -75,7 +78,7 @@ protected void doFilterRequest(HttpServletRequest request, HttpServletResponse r try { doFilter(chain, request, response); } finally { - if (!request.isAsyncStarted()) { + if (!request.isAsyncStarted() && !excludeMatcher.matches(request.getRequestURI())) { logger.logRequest(); } diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcher.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcher.java new file mode 100644 index 00000000..93da8d1e --- /dev/null +++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcher.java @@ -0,0 +1,99 @@ +package com.sap.hcp.cf.logging.servlet.filter; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Matches request URIs against a list of Ant-style path patterns. + *

+ * Supported wildcards: + *

+ */ +public class RequestUriMatcher { + + private final List patterns; + + /** + * Creates a matcher from a comma-separated list of Ant-style path patterns. + * + * @param commaSeparatedPatterns + * comma-separated pattern string, may be {@code null} or blank + */ + public RequestUriMatcher(String commaSeparatedPatterns) { + if (commaSeparatedPatterns == null || commaSeparatedPatterns.isBlank()) { + this.patterns = Collections.emptyList(); + } else { + this.patterns = Arrays.stream(commaSeparatedPatterns.split(",")).map(String::trim).filter(s -> !s.isEmpty()) + .toList(); + } + } + + /** + * Recursively matches {@code pattern} starting at {@code pi} (pattern index) against {@code uri} starting at + * {@code ui} (uri index). No heap allocation occurs during matching. + */ + private static boolean matches(String pattern, int pi, String uri, int ui) { + while (pi < pattern.length()) { + char pc = pattern.charAt(pi); + if (pc == '*') { + boolean doubleWildcard = pi + 1 < pattern.length() && pattern.charAt(pi + 1) == '*'; + if (doubleWildcard) { + // ** — skip the ** and try matching the rest from every position in uri + pi += 2; + if (pi == pattern.length()) { + return true; // ** at end matches everything remaining + } + for (int i = ui; i <= uri.length(); i++) { + if (matches(pattern, pi, uri, i)) { + return true; + } + } + return false; + } else { + // * — advance past * and try matching the rest from every position + // within the current segment (no slash crossing) + pi++; + if (pi == pattern.length()) { + return uri.indexOf('/', ui) == -1; // * at end matches rest of segment + } + for (int i = ui; i <= uri.length(); i++) { + if (i > ui && uri.charAt(i - 1) == '/') { + return false; // * cannot cross a slash + } + if (matches(pattern, pi, uri, i)) { + return true; + } + } + return false; + } + } else { + // literal character — must match exactly + if (ui >= uri.length() || uri.charAt(ui) != pc) { + return false; + } + pi++; + ui++; + } + } + return ui == uri.length(); + } + + /** + * Returns {@code true} if the given URI matches any of the configured patterns. + */ + public boolean matches(String uri) { + if (uri == null) { + return false; + } + for (String pattern: patterns) { + if (matches(pattern, 0, uri, 0)) { + return true; + } + } + return false; + } +} diff --git a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilterTest.java b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilterTest.java index 7645ed91..acc4aef3 100644 --- a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilterTest.java +++ b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/GenerateRequestLogFilterTest.java @@ -119,10 +119,55 @@ public void directlyForwardsRequestResponseWhenLogIsDisabled(ConsoleOutput conso assertThat(console.getAllEvents()).isEmpty(); ((LoggerContext) LoggerFactory.getILoggerFactory()).getLogger(RequestLogger.class).setLevel(Level.INFO); + } + + @Test + public void doesNotWriteRequestLogForExcludedUri(ConsoleOutput console) throws Exception { + when(request.getRequestURI()).thenReturn("/health"); + GenerateRequestLogFilter filter = new GenerateRequestLogFilter(requestRecordFactory); + filter.init(new ExcludePatternsConfig("/health,/metrics")); + + filter.doFilter(request, response, chain); + + assertThat(console.getAllEvents()).isEmpty(); + } + + @Test + public void writesRequestLogForNonExcludedUri(ConsoleOutput console) throws Exception { + when(request.getRequestURI()).thenReturn("/api/orders"); + GenerateRequestLogFilter filter = new GenerateRequestLogFilter(requestRecordFactory); + filter.init(new ExcludePatternsConfig("/health,/metrics")); + + filter.doFilter(request, response, chain); + + assertThat(console.getAllEvents()).isNotEmpty(); + } + + @Test + public void stillEnrichesContextForExcludedUri(ConsoleOutput console) throws Exception { + when(request.getRequestURI()).thenReturn("/actuator/health"); + GenerateRequestLogFilter filter = new GenerateRequestLogFilter(requestRecordFactory); + filter.init(new ExcludePatternsConfig("/actuator/**")); + + filter.doFilter(request, response, chain); + + verify(request).setAttribute(eq(MDC.class.getName()), anyMap()); + assertThat(console.getAllEvents()).isEmpty(); + } + + @Test + public void doesNotWriteRequestLogForWildcardExcludedUri(ConsoleOutput console) throws Exception { + when(request.getRequestURI()).thenReturn("/actuator/health/liveness"); + GenerateRequestLogFilter filter = new GenerateRequestLogFilter(requestRecordFactory); + filter.init(new ExcludePatternsConfig("/actuator/**")); + + filter.doFilter(request, response, chain); + assertThat(console.getAllEvents()).isEmpty(); } private static class NoRequestWrappingConfig implements FilterConfig { + @Override public String getFilterName() { return "no-request-wrapping"; @@ -143,4 +188,36 @@ public Enumeration getInitParameterNames() { return enumeration(List.of("wrapRequest")); } } + + private static class ExcludePatternsConfig implements FilterConfig { + + private final String excludePatterns; + + ExcludePatternsConfig(String excludePatterns) { + this.excludePatterns = excludePatterns; + } + + @Override + public String getFilterName() { + return "exclude-patterns"; + } + + @Override + public ServletContext getServletContext() { + return null; + } + + @Override + public String getInitParameter(String name) { + if (GenerateRequestLogFilter.EXCLUDE_PATTERNS_INIT_PARAM.equals(name)) { + return excludePatterns; + } + return null; + } + + @Override + public Enumeration getInitParameterNames() { + return enumeration(List.of(GenerateRequestLogFilter.EXCLUDE_PATTERNS_INIT_PARAM)); + } + } } diff --git a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcherTest.java b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcherTest.java new file mode 100644 index 00000000..c7cfcb79 --- /dev/null +++ b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestUriMatcherTest.java @@ -0,0 +1,99 @@ +package com.sap.hcp.cf.logging.servlet.filter; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; + +class RequestUriMatcherTest { + + @Test + void doesNotMatchWhenNoPatternsConfigured() { + RequestUriMatcher matcher = new RequestUriMatcher(null); + assertThat(matcher.matches("/health")).isFalse(); + } + + @Test + void doesNotMatchWhenPatternStringIsBlank() { + RequestUriMatcher matcher = new RequestUriMatcher(" "); + assertThat(matcher.matches("/health")).isFalse(); + } + + @Test + void doesNotMatchNullUri() { + RequestUriMatcher matcher = new RequestUriMatcher("/health"); + assertThat(matcher.matches(null)).isFalse(); + } + + @ParameterizedTest + @ValueSource(strings = { "/health", "/metrics", "/actuator" }) + void matchesExactPattern(String uri) { + RequestUriMatcher matcher = new RequestUriMatcher("/health,/metrics,/actuator"); + assertThat(matcher.matches(uri)).isTrue(); + } + + @Test + void doesNotMatchDifferentPath() { + RequestUriMatcher matcher = new RequestUriMatcher("/health"); + assertThat(matcher.matches("/api/orders")).isFalse(); + } + + @Test + void matchesSingleWildcardWithinSegment() { + RequestUriMatcher matcher = new RequestUriMatcher("/api/*/status"); + assertThat(matcher.matches("/api/orders/status")).isTrue(); + } + + @Test + void singleWildcardDoesNotMatchAcrossSegments() { + RequestUriMatcher matcher = new RequestUriMatcher("/api/*/status"); + assertThat(matcher.matches("/api/orders/items/status")).isFalse(); + } + + @ParameterizedTest + @CsvSource({ "/actuator/**, /actuator/health", "/actuator/**, /actuator/health/liveness", + "/actuator/**, /actuator/", "/api/**, /api/v1/orders/123" }) + void matchesDoubleWildcardAcrossSegments(String pattern, String uri) { + RequestUriMatcher matcher = new RequestUriMatcher(pattern.trim()); + assertThat(matcher.matches(uri.trim())).isTrue(); + } + + @Test + void doubleWildcardDoesNotMatchSiblingPath() { + RequestUriMatcher matcher = new RequestUriMatcher("/actuator/**"); + assertThat(matcher.matches("/api/orders")).isFalse(); + } + + @Test + void matchesFirstOfMultiplePatterns() { + RequestUriMatcher matcher = new RequestUriMatcher("/health, /actuator/**, /metrics"); + assertThat(matcher.matches("/health")).isTrue(); + } + + @Test + void matchesMiddleOfMultiplePatterns() { + RequestUriMatcher matcher = new RequestUriMatcher("/health, /actuator/**, /metrics"); + assertThat(matcher.matches("/actuator/health")).isTrue(); + } + + @Test + void matchesLastOfMultiplePatterns() { + RequestUriMatcher matcher = new RequestUriMatcher("/health, /actuator/**, /metrics"); + assertThat(matcher.matches("/metrics")).isTrue(); + } + + @Test + void doesNotMatchWhenNoneOfMultiplePatternsApply() { + RequestUriMatcher matcher = new RequestUriMatcher("/health, /actuator/**, /metrics"); + assertThat(matcher.matches("/api/orders")).isFalse(); + } + + @Test + void ignoresWhitespaceAroundPatterns() { + RequestUriMatcher matcher = new RequestUriMatcher(" /health , /metrics "); + assertThat(matcher.matches("/health")).isTrue(); + assertThat(matcher.matches("/metrics")).isTrue(); + } +} diff --git a/pom.xml b/pom.xml index a3917191..17515a86 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,7 @@ 3.5.0 3.5.3 3.6.3 + 3.3.0 3.12.0 3.2.8 0.10.0