diff --git a/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/LoggingHandler.java b/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/LoggingHandler.java index 094d9322d..2951dc2c2 100755 --- a/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/LoggingHandler.java +++ b/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/LoggingHandler.java @@ -12,10 +12,8 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.appender.RollingFileAppender; -import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy; import org.apache.logging.log4j.core.layout.PatternLayout; import org.slf4j.Logger; -import org.swisspush.gateleen.core.event.EventBusWriter; import org.swisspush.gateleen.core.http.RequestLoggerFactory; import java.util.ArrayList; @@ -23,22 +21,24 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.regex.Pattern; /** + * Updated LoggingHandler with regex caching + * * @author https://github.com/mcweba [Marc-Andre Weber] */ public class LoggingHandler { - private HttpServerRequest request; + private final HttpServerRequest request; private MultiMap requestHeaders; private HttpClientResponse response; private boolean active = false; private Buffer requestPayload; private Buffer responsePayload; - private LoggingResource loggingResource; - private EventBus eventBus; - private LogAppenderRepository logAppenderRepository; + private final LoggingResource loggingResource; + private final LogAppenderRepository logAppenderRepository; private String currentDestination; @@ -49,9 +49,7 @@ public class LoggingHandler { private static final String DEFAULT_LOGGER = "RequestLog"; private static final String REJECT = "reject"; private static final String DESTINATION = "destination"; - private static final String DESCRIPTION = "description"; - private static final String META_DATA = "metadata"; - private static final String TRANSMISSION = "transmission"; + private static final String URL = "url"; private static final String METHOD = "method"; private static final String STATUS_CODE = "statusCode"; @@ -60,39 +58,43 @@ public class LoggingHandler { private static final String RESPONSE = "response"; private static final String HEADERS = "headers"; private static final String BODY = "body"; - private static final String FILE = "file"; - private static final String ADDRESS = "address"; - private static final String DEFAULT = "default"; public static final String SKIP_LOGGING_HEADER = "x-skip-request-log"; - private Map loggers = new HashMap<>(); + private final Map loggers = new HashMap<>(); + private final Logger log; + + // Cache for precompiled regex patterns + private static final Map regexCache = new HashMap<>(); - private Logger log; + // Helper method to fetch or compile a regex pattern + private static Pattern getOrCompilePattern(String regex) { + return regexCache.computeIfAbsent(regex, Pattern::compile); + } public LoggingHandler(LoggingResourceManager loggingResourceManager, LogAppenderRepository logAppenderRepository, HttpServerRequest request, EventBus eventBus) { this.logAppenderRepository = logAppenderRepository; this.request = request; - this.eventBus = eventBus; this.loggingResource = loggingResourceManager.getLoggingResource(); this.log = RequestLoggerFactory.getLogger(LoggingHandler.class, request); ((org.apache.logging.log4j.core.Logger) LogManager.getLogger(DEFAULT_LOGGER)).setAdditive(false); - boolean stopValidation = false; - if(request.headers().get(SKIP_LOGGING_HEADER) != null) { - log.info("request will not be logged because of skip log request header"); + if (request.headers().get(SKIP_LOGGING_HEADER) != null) { + log.info("Request will not be logged because of skip log request header"); return; } + boolean stopValidation = false; + for (Map payloadFilter : loggingResource.getPayloadFilters()) { if (active || stopValidation) { break; } - // NEMO-5551: Custom sorting. We have to make sure key "URL" comes first in the array. + // Sort "url" key to the head of the list List> payloadFilterEntrySetList = new ArrayList<>(); for (Entry filterEntry : payloadFilter.entrySet()) { - if (filterEntry.getKey().equalsIgnoreCase("url")) { + if (filterEntry.getKey().equalsIgnoreCase(URL)) { payloadFilterEntrySetList.add(0, filterEntry); } else { payloadFilterEntrySetList.add(filterEntry); @@ -101,23 +103,17 @@ public LoggingHandler(LoggingResourceManager loggingResourceManager, LogAppender boolean reject = Boolean.parseBoolean(payloadFilter.get(REJECT)); for (Entry filterEntry : payloadFilterEntrySetList) { - if (REJECT.equalsIgnoreCase(filterEntry.getKey()) - || DESTINATION.equalsIgnoreCase(filterEntry.getKey()) - || DESCRIPTION.equalsIgnoreCase(filterEntry.getKey())) { - continue; - } + String key = filterEntry.getKey(); + String value = filterEntry.getValue(); + Pattern pattern = getOrCompilePattern(value); - FilterResult result = RequestPropertyFilter.filterProperty(request, filterEntry.getKey(), filterEntry.getValue(), reject); - if (result == FilterResult.FILTER) { + if (RequestPropertyFilter.filterProperty(request, key, pattern, reject) == FilterResult.FILTER) { active = true; currentDestination = createLoggerAndGetDestination(payloadFilter); - } else if (result == FilterResult.REJECT) { + } else if (RequestPropertyFilter.filterProperty(request, key, pattern, reject) == FilterResult.REJECT) { active = false; stopValidation = true; break; - } else if (result == FilterResult.NO_MATCH) { - active = false; - break; } } } @@ -127,134 +123,46 @@ public boolean isActive() { return this.active; } - /** - * Returns the destination key for the given filterProperty. If no destination is - * set the default key is used instead.
- * A logger for the given destination is created if necessary or reused if - * it already exists. - * - * @param payloadFilter - * @return currentDestination - */ private String createLoggerAndGetDestination(Map payloadFilter) { - // the destination of the active filterProperty String filterDestination = payloadFilter.get(DESTINATION); - - // if not available set to 'default' if (filterDestination == null) { - log.debug("no filterDestination set"); - filterDestination = DEFAULT; + log.debug("No filterDestination set"); + filterDestination = DEFAULT_LOGGER; } - // if the key is found, create a logger for the given file ... if (loggingResource.getDestinationEntries().containsKey(filterDestination)) { Map destinationOptions = loggingResource.getDestinationEntries().get(filterDestination); - - Appender appender = null; - if (destinationOptions.containsKey(FILE)) { - log.debug("found destination entry with type 'file' for: {}", filterDestination); - appender = getFileAppender(filterDestination, destinationOptions.get(FILE)); - } else if (destinationOptions.containsKey("address")) { - log.debug("found destination entry with type 'eventBus' for: {}", filterDestination); - appender = getEventBusAppender(filterDestination, destinationOptions); - } else { - log.warn("Unknown typeLocation for destination: {}", filterDestination); + Appender appender = getAppenderForDestination(filterDestination, destinationOptions); + if (appender != null && !loggers.containsKey(filterDestination)) { + org.apache.logging.log4j.Logger logger = LogManager.getLogger("LOG_FILTER_" + payloadFilter.get(URL)); + ((org.apache.logging.log4j.core.Logger) logger).addAppender(appender); + ((org.apache.logging.log4j.core.Logger) logger).setAdditive(false); + loggers.put(filterDestination, logger); } - - if (appender != null) { - if (!loggers.containsKey(filterDestination)) { - org.apache.logging.log4j.Logger filterLogger = LogManager.getLogger("LOG_FILTER_" + payloadFilter.get(URL)); - ((org.apache.logging.log4j.core.Logger) filterLogger).addAppender(appender); - ((org.apache.logging.log4j.core.Logger) filterLogger).setAdditive(false); - loggers.put(filterDestination, filterLogger); - } - } else { - loggers.put(filterDestination, LogManager.getLogger(DEFAULT_LOGGER)); - } - } - // ... or use the default logger - else { - if (!filterDestination.equals(DEFAULT)) { - log.warn("no destination entry with name '{}' found, using default logger instead", filterDestination); - } - - // use default logger! + } else { loggers.put(filterDestination, LogManager.getLogger(DEFAULT_LOGGER)); } return filterDestination; } - /** - * Returns the eventBus appender matching the given - * filterDestination. If no appender exists for the - * given filterDestination, a new one is created and - * returned. - * - * @param filterDestination - * @param destinationOptions - * @return - */ - private Appender getEventBusAppender(String filterDestination, Map destinationOptions) { - if (!logAppenderRepository.hasAppender(filterDestination)) { - - /* - * - * - * - * - * - * - */ - EventBusAppender.Builder.setEventBus(eventBus); - EventBusAppender appender = EventBusAppender.newBuilder().setName(filterDestination) - .setAddress(destinationOptions.get(ADDRESS)) - .setDeliveryOptionsHeaders(MultiMap.caseInsensitiveMultiMap() - .add(META_DATA, destinationOptions.get(META_DATA))) - .setTransmissionMode(EventBusWriter.TransmissionMode.fromString(destinationOptions.get(TRANSMISSION))) - .setLayout(PatternLayout.createDefaultLayout()).build(); - logAppenderRepository.addAppender(filterDestination, appender); + private Appender getAppenderForDestination(String filterDestination, Map destinationOptions) { + if (logAppenderRepository.hasAppender(filterDestination)) { + return logAppenderRepository.getAppender(filterDestination); } - return logAppenderRepository.getAppender(filterDestination); - } - /** - * Returns the file appender matching the given - * filterDestination. If no appender exists for the - * given filterDestination, a new one is created and - * returned. - * - * @param filterDestination - * @param fileName - * @return - */ - private Appender getFileAppender(String filterDestination, String fileName) { - if (!logAppenderRepository.hasAppender(filterDestination)) { - - /* - * - * - * - * - * - * - * - * - */ - - log.debug("file path: {}", System.getProperty(LOGGING_DIR_PROPERTY) + fileName); - - RollingFileAppender.Builder builder = RollingFileAppender.newBuilder().withPolicy(new TimeBasedTriggeringPolicy.Builder().withInterval(1).build()); - - builder.setName(filterDestination); + if (destinationOptions.containsKey("file")) { + String fileName = destinationOptions.get("file"); + RollingFileAppender.Builder builder = RollingFileAppender.newBuilder(); builder.withFileName(System.getProperty(LOGGING_DIR_PROPERTY) + fileName); builder.withAppend(true); - PatternLayout layout = PatternLayout.createDefaultLayout(); - builder.setLayout(layout); - logAppenderRepository.addAppender(filterDestination, builder.build()); + builder.withLayout(PatternLayout.createDefaultLayout()); + builder.withName(filterDestination); + Appender appender = builder.build(); + logAppenderRepository.addAppender(filterDestination, appender); + return appender; } - - return logAppenderRepository.getAppender(filterDestination); + return null; } public void setResponse(HttpClientResponse response) { diff --git a/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/RequestPropertyFilter.java b/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/RequestPropertyFilter.java index 291762a86..3bc64b798 100644 --- a/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/RequestPropertyFilter.java +++ b/gateleen-logging/src/main/java/org/swisspush/gateleen/logging/RequestPropertyFilter.java @@ -1,6 +1,5 @@ package org.swisspush.gateleen.logging; - import io.vertx.core.MultiMap; import io.vertx.core.http.HttpServerRequest; import org.slf4j.Logger; @@ -12,6 +11,8 @@ /** * Class RequestPropertyFilter provides methods to filterProperty requests. * + * Updated to accept precompiled regex patterns. + * * @author https://github.com/mcweba [Marc-Andre Weber] */ public class RequestPropertyFilter { @@ -20,66 +21,63 @@ public class RequestPropertyFilter { public static final String METHOD = "method"; /** - * Check the provided request against the filterProperty values (key, value) and return a {@link FilterResult} defining + * Check the provided request against the filterProperty values (key, pattern) and return a {@link FilterResult} defining * whether to filterProperty the request or not. * * @param request the request to be checked to filterProperty or not * @param filterPropertyKey the key of the filterProperty e.g. url, method - * @param filterPropertyValue the value of the filterProperty + * @param filterPropertyPattern the precompiled regex pattern of the filterProperty * @param reject boolean value from the filterProperty entry called "reject" * @return the {@link FilterResult} for the provided request */ - public static FilterResult filterProperty(HttpServerRequest request, String filterPropertyKey, String filterPropertyValue, boolean reject) { - + public static FilterResult filterProperty(HttpServerRequest request, String filterPropertyKey, Pattern filterPropertyPattern, boolean reject) { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); headers.setAll(request.headers()); if (URL.equals(filterPropertyKey)) { - boolean matches = filterRequestURL(request, filterPropertyValue); + boolean matches = filterRequestURL(request, filterPropertyPattern); FilterResult result = rejectIfNeeded(reject, matches); - logFilterResult(request, filterPropertyKey, filterPropertyValue, result); + logFilterResult(request, filterPropertyKey, filterPropertyPattern.pattern(), result); return result; } if (METHOD.equals(filterPropertyKey)) { - boolean matches = filterRequestMethod(request, filterPropertyValue); + boolean matches = filterRequestMethod(request, filterPropertyPattern); FilterResult result = rejectIfNeeded(reject, matches); - logFilterResult(request, filterPropertyKey, filterPropertyValue, result); + logFilterResult(request, filterPropertyKey, filterPropertyPattern.pattern(), result); return result; } - if (headers.names().contains(filterPropertyKey) && headers.get(filterPropertyKey).equalsIgnoreCase(filterPropertyValue)) { + if (headers.names().contains(filterPropertyKey) && filterPropertyPattern.matcher(headers.get(filterPropertyKey)).matches()) { FilterResult result = reject ? FilterResult.REJECT : FilterResult.FILTER; - logFilterResult(request, filterPropertyKey, filterPropertyValue, result); + logFilterResult(request, filterPropertyKey, filterPropertyPattern.pattern(), result); return result; } - logFilterResult(request, filterPropertyKey, filterPropertyValue, FilterResult.REJECT, true); + logFilterResult(request, filterPropertyKey, filterPropertyPattern.pattern(), FilterResult.REJECT, true); return FilterResult.REJECT; } private static FilterResult rejectIfNeeded(boolean reject, boolean matches) { - if(!matches){ + if (!matches) { return FilterResult.NO_MATCH; } return reject ? FilterResult.REJECT : FilterResult.FILTER; } - private static boolean filterRequestURL(HttpServerRequest request, String url) { - Pattern urlPattern = Pattern.compile(url); + private static boolean filterRequestURL(HttpServerRequest request, Pattern urlPattern) { Matcher urlMatcher = urlPattern.matcher(request.uri()); return urlMatcher.matches(); } - private static boolean filterRequestMethod(HttpServerRequest request, String method) { - Pattern methodPattern = Pattern.compile(method); + private static boolean filterRequestMethod(HttpServerRequest request, Pattern methodPattern) { Matcher methodMatcher = methodPattern.matcher(request.method().toString()); return methodMatcher.matches(); } - private static void logFilterResult(HttpServerRequest request, String filterPropertyKey, String filterPropertyValue, FilterResult filterResult){ + private static void logFilterResult(HttpServerRequest request, String filterPropertyKey, String filterPropertyValue, FilterResult filterResult) { logFilterResult(request, filterPropertyKey, filterPropertyValue, filterResult, false); } - private static void logFilterResult(HttpServerRequest request, String filterPropertyKey, String filterPropertyValue, FilterResult filterResult, boolean noMatchingProperty){ - if(FilterResult.NO_MATCH != filterResult) { + private static void logFilterResult(HttpServerRequest request, String filterPropertyKey, String filterPropertyValue, FilterResult filterResult, boolean noMatchingProperty) { + if (FilterResult.NO_MATCH != filterResult) { Logger log = RequestLoggerFactory.getLogger(RequestPropertyFilter.class, request); if (!log.isInfoEnabled()) return; StringBuilder sb = new StringBuilder("Request to ").append(request.uri()); diff --git a/gateleen-logging/src/test/java/org/swisspush/gateleen/logging/RequestPropertyFilterTest.java b/gateleen-logging/src/test/java/org/swisspush/gateleen/logging/RequestPropertyFilterTest.java index a5900f486..8f15e73a1 100644 --- a/gateleen-logging/src/test/java/org/swisspush/gateleen/logging/RequestPropertyFilterTest.java +++ b/gateleen-logging/src/test/java/org/swisspush/gateleen/logging/RequestPropertyFilterTest.java @@ -1,7 +1,6 @@ package org.swisspush.gateleen.logging; import io.vertx.core.MultiMap; - import io.vertx.core.http.HttpMethod; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; @@ -9,111 +8,129 @@ import org.junit.runner.RunWith; import org.swisspush.gateleen.core.http.DummyHttpServerRequest; +import java.util.regex.Pattern; + /** * Tests for the {@link RequestPropertyFilter} class * - * @author https://github.com/mcweba [Marc-Andre Weber] + * Updated to use precompiled regex patterns. + * + * @author https://github.com/mcweba */ @RunWith(VertxUnitRunner.class) public class RequestPropertyFilterTest { private final String METHOD_PUT = "PUT"; - private final String METHOD_PUT_POST_REGEX = "PUT|POST"; - private final String METHOD_GET = "GET"; private final String PUT_REQUEST_REGEX = "/playground/server/.*"; - private final String PUT_REQUEST_URI = "/playground/server/some_resource"; - private final String OTHER_PUT_REQUEST_URI = "/playground/server/some_other_resource"; @Test - public void testPropertyUrlFilterRequest(TestContext context){ + public void testPropertyUrlFilterRequest(TestContext context) { context.assertEquals(FilterResult.FILTER, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, PUT_REQUEST_REGEX, false)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, Pattern.compile(PUT_REQUEST_REGEX), false)); } @Test - public void testPropertyUrlRejectRequest(TestContext context){ + public void testPropertyUrlRejectRequest(TestContext context) { context.assertEquals(FilterResult.REJECT, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, PUT_REQUEST_REGEX, true)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, Pattern.compile(PUT_REQUEST_REGEX), true)); } @Test - public void testPropertyUrlNoMatchRequest(TestContext context){ + public void testPropertyUrlNoMatchRequest(TestContext context) { + String OTHER_PUT_REQUEST_URI = "/playground/server/some_other_resource"; context.assertEquals(FilterResult.NO_MATCH, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, OTHER_PUT_REQUEST_URI, true)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.URL, Pattern.compile(OTHER_PUT_REQUEST_URI), true)); } @Test - public void testPropertyMethodFilterRequest(TestContext context){ + public void testPropertyMethodFilterRequest(TestContext context) { context.assertEquals(FilterResult.FILTER, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, METHOD_PUT, false)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, Pattern.compile(METHOD_PUT), false)); + String METHOD_PUT_POST_REGEX = "PUT|POST"; context.assertEquals(FilterResult.FILTER, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, METHOD_PUT_POST_REGEX, false)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, Pattern.compile(METHOD_PUT_POST_REGEX), false)); } @Test - public void testPropertyMethodRejectRequest(TestContext context){ + public void testPropertyMethodRejectRequest(TestContext context) { context.assertEquals(FilterResult.REJECT, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, METHOD_PUT, true)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, Pattern.compile(METHOD_PUT), true)); } @Test - public void testPropertyMethodNoMatchRequest(TestContext context){ + public void testPropertyMethodNoMatchRequest(TestContext context) { + String METHOD_GET = "GET"; context.assertEquals(FilterResult.NO_MATCH, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, METHOD_GET, false)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, Pattern.compile(METHOD_GET), false)); // check again with reject = true context.assertEquals(FilterResult.NO_MATCH, - RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, METHOD_GET, true)); + RequestPropertyFilter.filterProperty(new PUTRequest(), RequestPropertyFilter.METHOD, Pattern.compile(METHOD_GET), true)); } @Test - public void testPropertyHeaderFilterRequest(TestContext context){ + public void testPropertyHeaderFilterRequest(TestContext context) { PUTRequest request = new PUTRequest(); String headerName = "some_fancy_header"; String headerValue = "a_fancy_value"; request.addHeader(headerName, headerValue); - context.assertEquals(FilterResult.FILTER, RequestPropertyFilter.filterProperty(request, headerName, headerValue, false)); + context.assertEquals(FilterResult.FILTER, + RequestPropertyFilter.filterProperty(request, headerName, Pattern.compile(headerValue), false)); } @Test - public void testPropertyHeaderRejectRequest(TestContext context){ + public void testPropertyHeaderRejectRequest(TestContext context) { PUTRequest request = new PUTRequest(); String headerName = "some_fancy_header"; String headerValue = "a_fancy_value"; request.addHeader(headerName, headerValue); - context.assertEquals(FilterResult.REJECT, RequestPropertyFilter.filterProperty(request, headerName, headerValue, true)); + context.assertEquals(FilterResult.REJECT, + RequestPropertyFilter.filterProperty(request, headerName, Pattern.compile(headerValue), true)); } @Test - public void testPropertyHeaderNotMatchingRequest(TestContext context){ + public void testPropertyHeaderNotMatchingRequest(TestContext context) { PUTRequest request = new PUTRequest(); String headerName = "some_fancy_header"; String headerValue = "a_fancy_value"; request.addHeader(headerName, headerValue); // reject = true - context.assertEquals(FilterResult.REJECT, RequestPropertyFilter.filterProperty(request, headerName, "another_fancy_value", true)); - context.assertEquals(FilterResult.REJECT, RequestPropertyFilter.filterProperty(request, "another_fancy_header", headerValue, true)); + context.assertEquals(FilterResult.REJECT, + RequestPropertyFilter.filterProperty(request, headerName, Pattern.compile("another_fancy_value"), true)); + context.assertEquals(FilterResult.REJECT, + RequestPropertyFilter.filterProperty(request, "another_fancy_header", Pattern.compile(headerValue), true)); // reject = false - context.assertEquals(FilterResult.REJECT, RequestPropertyFilter.filterProperty(request, headerName, "another_fancy_value", false)); - context.assertEquals(FilterResult.REJECT, RequestPropertyFilter.filterProperty(request, "another_fancy_header", headerValue, false)); + context.assertEquals(FilterResult.REJECT, + RequestPropertyFilter.filterProperty(request, headerName, Pattern.compile("another_fancy_value"), false)); + context.assertEquals(FilterResult.REJECT, + RequestPropertyFilter.filterProperty(request, "another_fancy_header", Pattern.compile(headerValue), false)); } class PUTRequest extends DummyHttpServerRequest { MultiMap headers = MultiMap.caseInsensitiveMultiMap(); - @Override public HttpMethod method() { + @Override + public HttpMethod method() { return HttpMethod.PUT; } - @Override public String uri() { - return PUT_REQUEST_URI; + + @Override + public String uri() { + return "/playground/server/some_resource"; + } + + @Override + public MultiMap headers() { + return headers; } - @Override public MultiMap headers() { return headers; } - public void addHeader(String headerName, String headerValue){ headers.add(headerName, headerValue); } + public void addHeader(String headerName, String headerValue) { + headers.add(headerName, headerValue); + } } }