Make request/response logging paths configurable 01/141101/3
authorsaul.gill <saul.gill@est.tech>
Wed, 4 Jun 2025 15:49:38 +0000 (16:49 +0100)
committersaul.gill <saul.gill@est.tech>
Fri, 6 Jun 2025 08:26:01 +0000 (09:26 +0100)
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 <saul.gill@est.tech>
a1-policy-management/config/application.yaml
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ApplicationConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/configuration/ReactiveEntryExitFilterConfig.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilter.java
a1-policy-management/src/main/java/org/onap/ccsdk/oran/a1policymanagementservice/util/v3/ReactiveEntryExitFilterCondition.java
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludeMultiPathTest.java [new file with mode: 0644]
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterExcludePathTest.java [new file with mode: 0644]
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterNullPathTest.java [new file with mode: 0644]
a1-policy-management/src/test/java/org/onap/ccsdk/oran/a1policymanagementservice/utils/v3/ReactiveEntryExitFilterTest.java

index 4b1dbce..e18b841 100644 (file)
@@ -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
index 360369c..db707f9 100644 (file)
@@ -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,
index 5c82b3f..a4a0ee3 100644 (file)
@@ -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;
     }
 }
index 2bb9dd0..a23e3ae 100644 (file)
@@ -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<PathPattern> excludedPatterns = new ArrayList<>();
+
+    public ReactiveEntryExitFilter excludePathPatterns(PathPattern... patterns) {
+        excludedPatterns.addAll(Arrays.asList(patterns));
+        return this;
+    }
+
     @Override
     public Mono<Void> 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());
 
index 70094c9..4b9de3a 100644 (file)
@@ -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 (file)
index 0000000..7b2f28d
--- /dev/null
@@ -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<ResponseEntity<String>> 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<ResponseEntity<String>> 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 (file)
index 0000000..9588e0c
--- /dev/null
@@ -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<ResponseEntity<String>> 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 (file)
index 0000000..ac4779e
--- /dev/null
@@ -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<ResponseEntity<String>> 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:"));
+    }
+}
index 0086c1e..c51d27b 100644 (file)
@@ -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<ResponseEntity<String>> 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:"));
+    }
 }