From: saul.gill Date: Wed, 4 Jun 2025 15:49:38 +0000 (+0100) Subject: Make request/response logging paths configurable X-Git-Tag: 2.1.0~2 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=30738f1d9f8fd0b540d8befb057df5b93b5ea69b;p=ccsdk%2Foran.git Make request/response logging paths configurable Added property to control excluded paths Reactive Filter will not log calls to those paths Issue-ID: CCSDK-4117 Change-Id: I6bf920f03ba68c110336efd265e3c045eb2b6441 Signed-off-by: saul.gill --- diff --git a/a1-policy-management/config/application.yaml b/a1-policy-management/config/application.yaml index 4b1dbcef..e18b8417 100644 --- a/a1-policy-management/config/application.yaml +++ b/a1-policy-management/config/application.yaml @@ -67,6 +67,7 @@ logging: config: ${LOGBACK_CONFIG_FILE:classpath:logback-plain.xml} # Reactive logging filter reactive-entry-exit-filter-enabled: true + reactive-entry-exit-filter-exclude-paths: "" # Configuration of logging file: name: /var/log/policy-agent/application.log diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java index 360369c0..db707f91 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java @@ -111,6 +111,10 @@ public class ApplicationConfig { @Value("${app.database-enabled:}") private boolean databaseEnabled; + @Getter + @Value("${logging.reactive-entry-exit-filter-exclude-paths:null}") + private String loggingReactiveEntryExitFilterExcludePaths; + public enum ValidateSchema { NONE, INFO, diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ReactiveEntryExitFilterConfig.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ReactiveEntryExitFilterConfig.java index 5c82b3fb..a4a0ee39 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ReactiveEntryExitFilterConfig.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ReactiveEntryExitFilterConfig.java @@ -1,3 +1,23 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + package org.onap.ccsdk.oran.a1policymanagementservice.configuration; import org.onap.ccsdk.oran.a1policymanagementservice.util.v3.ReactiveEntryExitFilter; @@ -6,12 +26,31 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.web.server.WebFilter; +import org.springframework.web.util.pattern.PathPatternParser; @Configuration public class ReactiveEntryExitFilterConfig { + + private ApplicationConfig applicationConfig; + + public ReactiveEntryExitFilterConfig(ApplicationConfig applicationConfig) { + this.applicationConfig = applicationConfig; + } + @Bean @Conditional(ReactiveEntryExitFilterCondition.class) public WebFilter reactiveEntryExitFilter() { - return new ReactiveEntryExitFilter(); + // Check if the exclude paths are set in the application configuration + String excludePaths = this.applicationConfig.getLoggingReactiveEntryExitFilterExcludePaths(); + if (excludePaths == null || excludePaths.isEmpty()) { + return new ReactiveEntryExitFilter(); + } + PathPatternParser parser = new PathPatternParser(); + String[] paths = excludePaths.split(","); + ReactiveEntryExitFilter filter = new ReactiveEntryExitFilter(); + for (String path : paths) { + filter.excludePathPatterns(parser.parse(path.trim())); + } + return filter; } } diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilter.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilter.java index 2bb9dd03..a23e3aea 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilter.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilter.java @@ -24,7 +24,11 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Base64; +import java.util.List; + import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,6 +41,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; +import org.springframework.web.util.pattern.PathPattern; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.context.Context; @@ -49,9 +54,23 @@ public class ReactiveEntryExitFilter implements WebFilter { private static final String FACILITY_KEY = "facility"; private static final String SUBJECT_KEY = "subject"; + private final List excludedPatterns = new ArrayList<>(); + + public ReactiveEntryExitFilter excludePathPatterns(PathPattern... patterns) { + excludedPatterns.addAll(Arrays.asList(patterns)); + return this; + } + @Override public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + // Skip processing for excluded patterns + for (PathPattern pattern : excludedPatterns) { + if (pattern.matches(exchange.getRequest().getPath().pathWithinApplication())) { + return chain.filter(exchange); + } + } + // sets FACILITY_KEY and SUBJECT_KEY in MDC auditLog(exchange.getRequest()); diff --git a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilterCondition.java b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilterCondition.java index 70094c9d..4b9de3a7 100644 --- a/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilterCondition.java +++ b/a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilterCondition.java @@ -1,3 +1,23 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + package org.onap.ccsdk.oran.a1policymanagementservice.util.v3; import org.slf4j.Logger; diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludeMultiPathTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludeMultiPathTest.java new file mode 100644 index 00000000..7b2f28d6 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludeMultiPathTest.java @@ -0,0 +1,92 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.utils.v3; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.OpenPolicyAgentSimulatorController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import reactor.core.publisher.Mono; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertFalse; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith({OutputCaptureExtension.class}) +@TestPropertySource(properties = { + "server.ssl.key-store=./config/keystore.jks", + "app.webclient.trust-store=./config/truststore.jks", + "app.vardata-directory=./target", + "app.config-file-schema-path=/application_configuration_schema.json", + "logging.reactive-entry-exit-filter-enabled=true", + "logging.level.org.onap.ccsdk.oran.a1policymanagementservice=TRACE", + "logging.reactive-entry-exit-filter-exclude-paths=/actuator/**,/a1-policy-management/v1/rics/**" +}) +class ReactiveEntryExitFilterExcludeMultiPathTest { + + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private TestHelperTest testHelperTest; + + @LocalServerPort + private int port; + + @BeforeEach + void init() { + testHelperTest.port = port; + this.applicationConfig.setAuthProviderUrl(testHelperTest.baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL); + } + + @Test + @DisplayName("test verify entry exit log for health actuator is absent") + void testHealthActuatorFilterOmitted(CapturedOutput capturedOutput) throws Exception { + String url = "/actuator/health"; + Mono> responseGetHealthMono = + testHelperTest.restClient(testHelperTest.baseUrl(), false).getForEntity(url); + testHelperTest.testSuccessResponse(responseGetHealthMono, HttpStatus.OK, responseBody -> responseBody.contains("UP")); + assertFalse(capturedOutput.getOut().contains("Request received with path: /actuator/health")); + assertFalse(capturedOutput.getOut().contains("the response is:")); + } + + @Test + @DisplayName("test verify entry exit log for the rics endpoint is absent") + void testGetRicsFilterOmitted(CapturedOutput capturedOutput) throws Exception { + String url = "/rics"; + Mono> responseEntityMono = testHelperTest.restClientV3().getForEntity(url); + testHelperTest.testSuccessResponse(responseEntityMono, HttpStatus.OK, responseBody -> responseBody + .contains("{\"rics\":[]}")); + assertFalse(capturedOutput.getOut().contains("Request received with path: /rics")); + assertFalse(capturedOutput.getOut().contains("the response is:")); + } +} \ No newline at end of file diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludePathTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludePathTest.java new file mode 100644 index 00000000..9588e0c5 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludePathTest.java @@ -0,0 +1,80 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.utils.v3; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.OpenPolicyAgentSimulatorController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import reactor.core.publisher.Mono; + + +import static org.junit.jupiter.api.Assertions.assertFalse; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith({OutputCaptureExtension.class}) +@TestPropertySource(properties = { + "server.ssl.key-store=./config/keystore.jks", + "app.webclient.trust-store=./config/truststore.jks", + "app.vardata-directory=./target", + "app.config-file-schema-path=/application_configuration_schema.json", + "logging.reactive-entry-exit-filter-enabled=true", + "logging.level.org.onap.ccsdk.oran.a1policymanagementservice=TRACE", + "logging.reactive-entry-exit-filter-exclude-paths=/actuator/**" +}) +class ReactiveEntryExitFilterExcludePathTest { + + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private TestHelperTest testHelperTest; + + @LocalServerPort + private int port; + + @BeforeEach + void init() { + testHelperTest.port = port; + this.applicationConfig.setAuthProviderUrl(testHelperTest.baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL); + } + + @Test + @DisplayName("test verify entry exit log for health actuator is absent") + void testHealthActuatorFilterOmitted(CapturedOutput capturedOutput) throws Exception { + String url = "/actuator/health"; + Mono> responseGetHealthMono = + testHelperTest.restClient(testHelperTest.baseUrl(), false).getForEntity(url); + testHelperTest.testSuccessResponse(responseGetHealthMono, HttpStatus.OK, responseBody -> responseBody.contains("UP")); + assertFalse(capturedOutput.getOut().contains("Request received with path: /actuator/health")); + assertFalse(capturedOutput.getOut().contains("the response is:")); + } +} \ No newline at end of file diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterNullPathTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterNullPathTest.java new file mode 100644 index 00000000..ac4779e7 --- /dev/null +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterNullPathTest.java @@ -0,0 +1,77 @@ +/*- + * ========================LICENSE_START================================= + * ONAP : ccsdk oran + * ====================================================================== + * Copyright (C) 2025 OpenInfra Foundation Europe. All rights reserved. + * ====================================================================== + * 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. + * ========================LICENSE_END=================================== + */ + +package org.onap.ccsdk.oran.a1policymanagementservice.utils.v3; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.onap.ccsdk.oran.a1policymanagementservice.configuration.ApplicationConfig; +import org.onap.ccsdk.oran.a1policymanagementservice.controllers.OpenPolicyAgentSimulatorController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; +import reactor.core.publisher.Mono; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith({OutputCaptureExtension.class}) +@TestPropertySource(properties = { + "server.ssl.key-store=./config/keystore.jks", + "app.webclient.trust-store=./config/truststore.jks", + "app.vardata-directory=./target", + "app.config-file-schema-path=/application_configuration_schema.json", + "logging.reactive-entry-exit-filter-enabled=true", + "logging.level.org.onap.ccsdk.oran.a1policymanagementservice=TRACE", + "logging.reactive-entry-exit-filter-exclude-paths=null" +}) +class ReactiveEntryExitFilterNullPathTest { + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private TestHelperTest testHelperTest; + + @LocalServerPort + private int port; + + @BeforeEach + void init() { + testHelperTest.port = port; + this.applicationConfig.setAuthProviderUrl(testHelperTest.baseUrl() + OpenPolicyAgentSimulatorController.ACCESS_CONTROL_URL); + } + + @Test + @DisplayName("test verify entry exit log for health actuator is present") + void testHealthActuatorFilterOmitted(CapturedOutput capturedOutput) throws Exception { + String url = "/actuator/health"; + Mono> responseGetHealthMono = + testHelperTest.restClient(testHelperTest.baseUrl(), false).getForEntity(url); + testHelperTest.testSuccessResponse(responseGetHealthMono, HttpStatus.OK, responseBody -> responseBody.contains("UP")); + assertTrue(capturedOutput.getOut().contains("Request received with path: /actuator/health")); + assertTrue(capturedOutput.getOut().contains("the response is:")); + } +} diff --git a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterTest.java b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterTest.java index 0086c1eb..c51d27b4 100644 --- a/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterTest.java +++ b/a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterTest.java @@ -99,4 +99,15 @@ class ReactiveEntryExitFilterTest { assertTrue(capturedOutput.getOut().contains("the Status code of the response: 201 CREATED")); assertTrue(capturedOutput.getOut().contains("the response is:")); } + + @Test + @DisplayName("test verify entry exit log for health actuator is present") + void testHealthActuatorFilterIncluded(CapturedOutput capturedOutput) throws Exception { + String url = "/actuator/health"; + Mono> responseGetHealthMono = + testHelperTest.restClient(testHelperTest.baseUrl(), false).getForEntity(url); + testHelperTest.testSuccessResponse(responseGetHealthMono, HttpStatus.OK, responseBody -> responseBody.contains("UP")); + assertTrue(capturedOutput.getOut().contains("Request received with path: /actuator/health")); + assertTrue(capturedOutput.getOut().contains("the response is:")); + } }