diff options
author | olegb <olegb@amdocs.com> | 2018-02-19 16:24:54 +0200 |
---|---|---|
committer | Oren Kleks <orenkle@amdocs.com> | 2018-03-26 08:29:36 +0000 |
commit | f2cee7829ae7d8fae58239dd0018b2aa790c0251 (patch) | |
tree | ef6ca1bd45d5364299020b6858f61584bfd59239 | |
parent | 365d1c3f7db39eeec66437cf9f6c77bcce729819 (diff) |
Added JAX-RS filters for logging
Change-Id: I57ab333278d4ea26530e916ef89670df8b51b555
Issue-ID: SDC-772
Signed-off-by: olegb <olegb@amdocs.com>
Signed-off-by: vempo <vitaliy.emporopulo@amdocs.com>
18 files changed, 1707 insertions, 848 deletions
diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml index 7710032cbc..589406df72 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/beans-services.xml @@ -81,6 +81,11 @@ <ref bean="jsonProvider"/> <bean class="org.openecomp.sdc.action.errors.ActionExceptionMapper"/> <bean class="org.openecomp.sdcrests.errors.DefaultExceptionMapper"/> + <bean class="org.openecomp.sdc.logging.servlet.jaxrs.LoggingRequestFilter"> + <property name="requestIdHeaders" value="X-ECOMP-RequestID,X-ONAP-RequestID"/> + <property name="partnerNameHeaders" value="USER_ID"/> + </bean> + <bean class="org.openecomp.sdc.logging.servlet.jaxrs.LoggingResponseFilter"/> </jaxrs:providers> <jaxrs:outInterceptors> diff --git a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/web.xml b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/web.xml index 64be5ab20c..b98ae4e82d 100644 --- a/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/web.xml +++ b/openecomp-be/api/openecomp-sdc-rest-webapp/onboarding-rest-war/src/main/webapp/WEB-INF/web.xml @@ -72,15 +72,6 @@ <filter-name>AuthZ</filter-name> <url-pattern>/workflow/v1.0/actions/*</url-pattern> </filter-mapping> - <filter> - <filter-name>LoggingServletFilter</filter-name> - <filter-class>org.openecomp.sdc.logging.servlet.LoggingFilter</filter-class> - </filter> - - <filter-mapping> - <filter-name>LoggingServletFilter</filter-name> - <url-pattern>/*</url-pattern> - </filter-mapping> <filter> <filter-name>SessionContextFilter</filter-name> <filter-class>org.openecomp.server.filters.OnboardingSessionContextFilter</filter-class> @@ -100,7 +91,6 @@ <!-- CXF --> <servlet> <servlet-name>CXFServlet</servlet-name> - <display-name>CXF Servlet</display-name> <servlet-class> org.apache.cxf.transport.servlet.CXFServlet </servlet-class> @@ -130,12 +120,5 @@ <servlet-name>CXFServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> - <!--servlet> - <servlet-name>logger-aspect</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>2</load-on-startup> - </servlet--> - - </web-app> diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/pom.xml b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/pom.xml index 927f45454f..754a103706 100644 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/pom.xml @@ -15,11 +15,48 @@ <dependencies> <dependency> + <groupId>javax.ws.rs</groupId> + <artifactId>javax.ws.rs-api</artifactId> + <version>${ws.rs.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>servlet-api</artifactId> + <version>${servlet-api.version}</version> + <scope>provided</scope> + </dependency> + <!-- testing --> + <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>${testng.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>${easymock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-easymock</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-core</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-testng</artifactId> + <version>${powermock.version}</version> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/LoggingConstants.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/LoggingConstants.java new file mode 100644 index 0000000000..e8635b2418 --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/LoggingConstants.java @@ -0,0 +1,40 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging; + +/** + * Constants that must be visible across the logging API. + * + * @author evitaliy + * @since 18 Mar 2018 + */ +public class LoggingConstants { + + /** + * Default HTTP header for propagation of a request ID for distributed tracing. + */ + public static final String DEFAULT_REQUEST_ID_HEADER = "X-ECOMP-RequestID"; + + /** + * Default HTTP header for exchanging a partner name between components. + */ + public static final String DEFAULT_PARTNER_NAME_HEADER = "USER_ID"; + + private LoggingConstants() { + // prevent instantiation of the constants class + } +} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java index e3c9ea02de..6706d6fa46 100644 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java @@ -4,9 +4,9 @@ * 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. @@ -16,25 +16,22 @@ package org.openecomp.sdc.logging.api; -import org.openecomp.sdc.logging.spi.LoggerCreationService; -import org.openecomp.sdc.logging.spi.LoggingContextService; -import org.openecomp.sdc.logging.spi.LoggingServiceProvider; - import java.util.Iterator; import java.util.Optional; import java.util.ServiceLoader; +import org.openecomp.sdc.logging.spi.LoggerCreationService; +import org.openecomp.sdc.logging.spi.LoggingContextService; +import org.openecomp.sdc.logging.spi.LoggingServiceProvider; /** * <p>Binds to a concrete implementation of logging services.</p> - * * <p>In order to use the factory, a particular (e.g. framework-specific) implementation of a service must be * configured as described in * <a href="http://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html">java.util.ServiceLoader</a>).</p> * * @author evitaliy - * @since 13/09/2016. - * * @see ServiceLoader + * @since 13 Sep 2016 */ // No advanced logging can be used here because we don't know @@ -44,7 +41,7 @@ class ServiceBinder { private static final LoggingServiceProvider PROVIDER = lookupProvider(); - private ServiceBinder () { + private ServiceBinder() { // prevent instantiation } @@ -54,9 +51,8 @@ class ServiceBinder { Iterator<LoggingServiceProvider> iterator = loader.iterator(); if (!iterator.hasNext()) { - System.err.printf("[ERROR] No provider configured for logging services %s. " + - "Default implementation will be used.\n", - LoggingServiceProvider.class.getName()); + System.err.printf("[ERROR] No provider configured for logging services %s. " + + "Default implementation will be used.\n", LoggingServiceProvider.class.getName()); return null; } diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/HttpHeader.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/HttpHeader.java new file mode 100644 index 0000000000..4dcc197796 --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/HttpHeader.java @@ -0,0 +1,55 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet; + +import java.util.function.Function; + +/** + * Handles any of possible header names to read a value for that header. This is useful for backward compatibility, if + * multiple headers may have the same meaning. For instance, when requests come from multiple service, some using an old + * header and others using a new header to pass the same information. + * + * @author evitaliy + * @since 25 Mar 2018 + */ +public class HttpHeader { + + private final String[] keys; + + public HttpHeader(String... keys) { + this.keys = keys; + } + + /** + * Returns the value of any of the possible headers. + * + * @param reader function for reading an HTTP header. + * @return value or null if not found + */ + public String getAny(Function<String, String> reader) { + + for (String k : keys) { + + String value = reader.apply(k); + if (value != null) { + return value; + } + } + + return null; + } +} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java new file mode 100644 index 0000000000..e53f28119c --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java @@ -0,0 +1,136 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet; + +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_PARTNER_NAME_HEADER; +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_REQUEST_ID_HEADER; + +import java.io.IOException; +import java.util.UUID; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.openecomp.sdc.logging.api.ContextData; +import org.openecomp.sdc.logging.api.Logger; +import org.openecomp.sdc.logging.api.LoggerFactory; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.openecomp.sdc.logging.servlet.jaxrs.LoggingRequestFilter; +import org.openecomp.sdc.logging.servlet.jaxrs.LoggingResponseFilter; + +/** + * <p>Places logging context information. Must be configured as a servlet filter in <i>web.xml</i>. The behavior can be + * customized via init-params.</p> + * <p>Example:</p> + * <pre> + * + * <filter> + * <filter-name>LoggingServletFilter</filter-name> + * <filter-class>org.openecomp.sdc.logging.servlet.LoggingFilter</filter-class> + * <init-param> + * <param-name>requestIdHeaders</param-name> + * <param-value>X-ONAP-RequestID</param-value> + * </init-param> + * <init-param> + * <param-name>partnerNameHeaders</param-name> + * <param-value>USER_ID</param-value> + * </init-param> + * </filter> + * + * <filter-mapping> + * <filter-name>LoggingServletFilter</filter-name> + * <url-pattern>/*</url-pattern> + * </filter-mapping> + * + * </pre> + * + * @author evitaliy + * @since 25 Jul 2016 + * @deprecated Kept for backward compatibility. For JAX-RS application, use + * {@link LoggingRequestFilter} and + * {@link LoggingResponseFilter} instead. + */ +@Deprecated +public class LoggingFilter implements Filter { + + static final String MULTI_VALUE_SEPARATOR = ","; + + static final String REQUEST_ID_HEADERS_PARAM = "requestIdHeaders"; + static final String PARTNER_NAME_HEADERS_PARAM = "partnerNameHeaders"; + + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class); + + private HttpHeader requestIdHeaders; + private HttpHeader partnerNameHeaders; + + @Override + public void init(FilterConfig config) { + requestIdHeaders = getInitParam(config, REQUEST_ID_HEADERS_PARAM, DEFAULT_REQUEST_ID_HEADER); + partnerNameHeaders = getInitParam(config, PARTNER_NAME_HEADERS_PARAM, DEFAULT_PARTNER_NAME_HEADER); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest httpRequest = HttpServletRequest.class.cast(request); + + try { + + LoggingContext.clear(); + + ContextData.ContextDataBuilder contextData = ContextData.builder(); + + contextData.serviceName(httpRequest.getRequestURI()); + + String requestId = requestIdHeaders.getAny(httpRequest::getHeader); + contextData.requestId(requestId == null ? UUID.randomUUID().toString() : requestId); + + String partner = partnerNameHeaders.getAny(httpRequest::getHeader); + if (partner != null) { + contextData.partnerName(partner); + } + + LoggingContext.put(contextData.build()); + + chain.doFilter(request, response); + + } finally { + LoggingContext.clear(); + } + } + + @Override + public void destroy() { + // forced by the interface - not implemented + } + + private HttpHeader getInitParam(FilterConfig config, String paramName, String defaultValue) { + + String value = config.getInitParameter(paramName); + LOGGER.debug("Logging filter configuration param '{}' value '{}'", paramName, value); + + if (value == null) { + return new HttpHeader(defaultValue); + } else { + return new HttpHeader(value.split(MULTI_VALUE_SEPARATOR)); + } + } +} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilter.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilter.java new file mode 100644 index 0000000000..06660f35f4 --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilter.java @@ -0,0 +1,118 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet.jaxrs; + +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_PARTNER_NAME_HEADER; +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_REQUEST_ID_HEADER; + +import java.util.UUID; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerRequestFilter; +import javax.ws.rs.container.ResourceInfo; +import javax.ws.rs.core.Context; +import javax.ws.rs.ext.Provider; +import org.openecomp.sdc.logging.api.ContextData; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.openecomp.sdc.logging.servlet.HttpHeader; + +/** + * <p>Takes care of logging initialization an HTTP request hits the application. This includes populating logging + * context and storing the request processing start time, so that it can be used for audit. The filter was built + * <b>works in tandem</b> with {@link LoggingResponseFilter} or a similar implementation.</p> + * <p>The filter requires a few HTTP header names to be configured. These HTTP headers are used for propagating logging + * and tracing information between ONAP components.</p> + * <p>Sample configuration for a Spring environment:</p> + * <pre> + * <jaxrs:providers> + * <bean class="org.openecomp.sdc.logging.ws.rs.LoggingRequestFilter"> + * <property name="requestIdHeaders" value="X-ONAP-RequestID"/> + * <property name="partnerNameHeaders" value="X-ONAP-InstanceID"/> + * </bean> + * </jaxrs:providers> + * </pre> + * <p>Keep in mind that the filters does nothing in case when a request cannot be mapped to a working JAX-RS resource + * (implementation). For instance, when the path is invalid (404), or there is no handler for a particular method (405). + * </p> + * + * @author evitaliy, katyr + * @since 29 Oct 17 + * + * @see ContainerRequestFilter + */ +@Provider +public class LoggingRequestFilter implements ContainerRequestFilter { + + static final String MULTI_VALUE_SEPARATOR = ","; + + static final String START_TIME_KEY = "audit.start.time"; + + private ResourceInfo resource; + + private HttpHeader requestIdHeader = new HttpHeader(DEFAULT_REQUEST_ID_HEADER); + private HttpHeader partnerNameHeader = new HttpHeader(DEFAULT_PARTNER_NAME_HEADER); + + /** + * Injection of a resource that matches the request from JAX-RS context. + * + * @param resource automatically injected by JAX-RS container + */ + @Context + public void setResource(ResourceInfo resource) { + this.resource = resource; + } + + /** + * Configuration parameter for request ID HTTP header. + */ + public void setRequestIdHeaders(String requestIdHeaders) { + this.requestIdHeader = new HttpHeader(requestIdHeaders.split(MULTI_VALUE_SEPARATOR)); + } + + /** + * Configuration parameter for partner name HTTP header. + */ + public void setPartnerNameHeaders(String partnerNameHeaders) { + this.partnerNameHeader = new HttpHeader(partnerNameHeaders.split(MULTI_VALUE_SEPARATOR)); + } + + @Override + public void filter(ContainerRequestContext containerRequestContext) { + + if (resource == null) { + // JAX-RS could not find a mapping this response, probably due to HTTP 404 (not found), + // 405 (method not allowed), 415 (unsupported media type), etc. with a message in Web server log + return; + } + + containerRequestContext.setProperty(START_TIME_KEY, System.currentTimeMillis()); + + LoggingContext.clear(); + + ContextData.ContextDataBuilder contextData = ContextData.builder(); + contextData.serviceName(resource.getResourceClass().getName() + "." + resource.getResourceMethod().getName()); + + String partnerName = partnerNameHeader.getAny(containerRequestContext::getHeaderString); + if (partnerName != null) { + contextData.partnerName(partnerName); + } + + String requestId = requestIdHeader.getAny(containerRequestContext::getHeaderString); + contextData.requestId(requestId == null ? UUID.randomUUID().toString() : requestId); + + LoggingContext.put(contextData.build()); + } +} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilter.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilter.java new file mode 100644 index 0000000000..fbe28a79eb --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilter.java @@ -0,0 +1,147 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet.jaxrs; + +import static org.openecomp.sdc.logging.api.StatusCode.COMPLETE; +import static org.openecomp.sdc.logging.api.StatusCode.ERROR; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.container.ContainerResponseFilter; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.Provider; +import org.openecomp.sdc.logging.api.AuditData; +import org.openecomp.sdc.logging.api.Logger; +import org.openecomp.sdc.logging.api.LoggerFactory; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.openecomp.sdc.logging.api.StatusCode; + +/** + * <p>Takes care of logging when an HTTP request leaves the application. This includes writing to audit and clearing + * logging context. This filter <b>only works properly in tandem</b> with {@link LoggingRequestFilter} or a similar + * implementation.</p> + * <p>Sample configuration for a Spring environment:</p> + * <pre> + * <jaxrs:providers> + * <bean class="org.openecomp.sdc.logging.ws.rs.LoggingResponseFilter"/> + * </jaxrs:providers> + * </pre> + * <p><i>It is highly recommended to configure a custom JAX-RS exception mapper so that this filter will not be bypassed + * due to unhandled application or container exceptions.</i></p> + * + * @author evitaliy + * @since 29 Oct 17 + * + * @see ContainerResponseFilter + */ +@Provider +public class LoggingResponseFilter implements ContainerResponseFilter { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * Tracks reporting configuration problems to the log. We want to report them only once, and not to write to log + * upon every request, as the configuration will not change in runtime. + */ + private boolean reportBadConfiguration = true; + + private HttpServletRequest httpRequest; + + /** + * Injection of HTTP request object from JAX-RS context. + * + * @param httpRequest automatically injected by JAX-RS container + */ + @Context + public void setHttpRequest(HttpServletRequest httpRequest) { + this.httpRequest = httpRequest; + } + + @Override + public void filter(ContainerRequestContext containerRequestContext, + ContainerResponseContext containerResponseContext) { + + try { + writeAudit(containerRequestContext, containerResponseContext); + } finally { + LoggingContext.clear(); + } + } + + private void writeAudit(ContainerRequestContext containerRequestContext, + ContainerResponseContext containerResponseContext) { + + if (!logger.isAuditEnabled()) { + return; + } + + long start = readStartTime(containerRequestContext); + long end = System.currentTimeMillis(); + + Response.StatusType statusInfo = containerResponseContext.getStatusInfo(); + int responseCode = statusInfo.getStatusCode(); + StatusCode statusCode = isSuccess(responseCode) ? COMPLETE : ERROR; + + AuditData auditData = AuditData.builder().startTime(start).endTime(end).statusCode(statusCode) + .responseCode(Integer.toString(responseCode)) + .responseDescription(statusInfo.getReasonPhrase()) + .clientIpAddress(httpRequest.getRemoteAddr()).build(); + logger.audit(auditData); + } + + private boolean isSuccess(int responseCode) { + return responseCode > 199 && responseCode < 400; + } + + private long readStartTime(ContainerRequestContext containerRequestContext) { + + Object startTime = containerRequestContext.getProperty(LoggingRequestFilter.START_TIME_KEY); + if (startTime == null) { + return handleMissingStartTime(); + } + + return parseStartTime(startTime); + } + + private long handleMissingStartTime() { + reportConfigProblem("{} key was not found in JAX-RS request context. " + + "Make sure you configured a request filter", LoggingRequestFilter.START_TIME_KEY); + return 0; + } + + private long parseStartTime(Object startTime) { + + try { + return Long.class.cast(startTime); + } catch (ClassCastException e) { + reportConfigProblem("{} key in JAX-RS request context contains an object of type '{}', but 'java.lang.Long'" + + " is expected", LoggingRequestFilter.START_TIME_KEY, startTime.getClass().getName()); + return 0; + } + } + + private void reportConfigProblem(String message, Object... arguments) { + + if (reportBadConfiguration) { + reportBadConfiguration = false; + logger.error(message, arguments); + } + } +} + diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java new file mode 100644 index 0000000000..535a50ca42 --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java @@ -0,0 +1,199 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet; + +import static org.easymock.EasyMock.anyObject; +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_PARTNER_NAME_HEADER; +import static org.openecomp.sdc.logging.LoggingConstants.DEFAULT_REQUEST_ID_HEADER; +import static org.openecomp.sdc.logging.servlet.LoggingFilter.PARTNER_NAME_HEADERS_PARAM; +import static org.openecomp.sdc.logging.servlet.LoggingFilter.REQUEST_ID_HEADERS_PARAM; + +import java.io.IOException; +import java.util.UUID; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.easymock.EasyMock; +import org.openecomp.sdc.logging.api.ContextData; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +/** + * Unit-tests logging filter for initialization and data retrieval. + * + * @author evitaliy + * @since 17 Aug 2016 + */ +@PrepareForTest(LoggingContext.class) +public class LoggingFilterTest extends PowerMockTestCase { + + private static final String RANDOM_REQUEST_URI = UUID.randomUUID().toString(); + private static final String RANDOM_REQUEST_ID = UUID.randomUUID().toString(); + private static final String RANDOM_PARTNER_NAME = UUID.randomUUID().toString(); + + /** + * Verify all mocks after each test. + */ + @AfterMethod + public void verifyMocks(ITestResult result) { + + try { + PowerMock.verifyAll(); + } catch (AssertionError e) { + throw new AssertionError("Expectations failed in: " + result.getMethod().getMethodName(), e); + } + } + + + @Test + public void filterPopulatesValuesWhenNoInitParamsAndNoHeaders() throws Exception { + + mockLoggingContext(); + LoggingFilter loggingFilter = new LoggingFilter(); + loggingFilter.init(mockFilterConfig(null, null)); + loggingFilter.doFilter(new MockRequestBuilder().build(), mockResponse(), mockChain()); + } + + @Test + public void filterPopulatesValuesWhenNoInitParamsAndExistingHeaders() throws Exception { + + mockLoggingContext(); + + LoggingFilter loggingFilter = new LoggingFilter(); + loggingFilter.init(mockFilterConfig(null, null)); + + HttpServletRequest mockRequest = new MockRequestBuilder().partnerName(RANDOM_PARTNER_NAME) + .requestId(RANDOM_REQUEST_ID).build(); + loggingFilter.doFilter(mockRequest, mockResponse(), mockChain()); + } + + @Test + public void filterPopulatesValuesWhenCustomInitParamsAndNoHeaders() throws Exception { + + mockLoggingContext(); + + final String requestIdHeader = "x-request"; + final String partnerNameHeader = "x-partner"; + + LoggingFilter loggingFilter = new LoggingFilter(); + FilterConfig mockConfig = mockFilterConfig(requestIdHeader, partnerNameHeader); + loggingFilter.init(mockConfig); + + HttpServletRequest mockRequest = new MockRequestBuilder().requestIdHeader(requestIdHeader) + .partnerNameHeader(partnerNameHeader).build(); + loggingFilter.doFilter(mockRequest, mockResponse(), mockChain()); + } + + @Test + public void filterPopulatesValuesWhenCustomInitParamsAndExistingHeaders() throws Exception { + + mockLoggingContext(); + + final String requestIdHeader = "x-request-id"; + final String partnerNameHeader = "x-partner-name"; + + LoggingFilter loggingFilter = new LoggingFilter(); + FilterConfig mockConfig = mockFilterConfig(requestIdHeader, partnerNameHeader); + loggingFilter.init(mockConfig); + + HttpServletRequest mockRequest = new MockRequestBuilder() + .partnerNameHeader(partnerNameHeader).partnerName(RANDOM_PARTNER_NAME) + .requestIdHeader(requestIdHeader).requestId(RANDOM_REQUEST_ID).build(); + loggingFilter.doFilter(mockRequest, mockResponse(), mockChain()); + } + + private FilterConfig mockFilterConfig(String requestIdHeader, String partnerNameHeader) { + FilterConfig config = EasyMock.mock(FilterConfig.class); + EasyMock.expect(config.getInitParameter(REQUEST_ID_HEADERS_PARAM)).andReturn(requestIdHeader); + EasyMock.expect(config.getInitParameter(PARTNER_NAME_HEADERS_PARAM)).andReturn(partnerNameHeader); + EasyMock.replay(config); + return config; + } + + private FilterChain mockChain() throws IOException, ServletException { + FilterChain chain = EasyMock.mock(FilterChain.class); + chain.doFilter(anyObject(ServletRequest.class), anyObject(ServletResponse.class)); + EasyMock.expectLastCall().once(); + EasyMock.replay(chain); + return chain; + } + + private ServletResponse mockResponse() { + HttpServletResponse servletResponse = EasyMock.mock(HttpServletResponse.class); + EasyMock.replay(servletResponse); + return servletResponse; + } + + private void mockLoggingContext() { + + PowerMock.mockStatic(LoggingContext.class); + + LoggingContext.clear(); + EasyMock.expectLastCall().times(2); + + LoggingContext.put(anyObject(ContextData.class)); + EasyMock.expectLastCall().once(); + + PowerMock.replay(LoggingContext.class); + } + + private static class MockRequestBuilder { + + private String requestIdHeader = DEFAULT_REQUEST_ID_HEADER; + private String partnerNameHeader = DEFAULT_PARTNER_NAME_HEADER; + private String requestId = null; + private String partnerName = null; + + MockRequestBuilder requestIdHeader(String h) { + this.requestIdHeader = h; + return this; + } + + MockRequestBuilder requestId(String id) { + this.requestId = id; + return this; + } + + MockRequestBuilder partnerNameHeader(String h) { + this.partnerNameHeader = h; + return this; + } + + MockRequestBuilder partnerName(String name) { + this.partnerName = name; + return this; + } + + HttpServletRequest build() { + HttpServletRequest mockRequest = EasyMock.mock(HttpServletRequest.class); + EasyMock.expect(mockRequest.getRequestURI()).andReturn(RANDOM_REQUEST_URI); + EasyMock.expect(mockRequest.getHeader(requestIdHeader)).andReturn(requestId); + EasyMock.expect(mockRequest.getHeader(partnerNameHeader)).andReturn(partnerName); + EasyMock.replay(mockRequest); + return mockRequest; + } + } +} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/HttpHeaderTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/HttpHeaderTest.java new file mode 100644 index 0000000000..3376c922e4 --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/HttpHeaderTest.java @@ -0,0 +1,98 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet.jaxrs; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; + +import java.util.function.Function; +import org.openecomp.sdc.logging.servlet.HttpHeader; +import org.testng.annotations.Test; + +/** + * Unit tests mutliple-option headers. + * + * @author evitaliy + * @since 25 Mar 2018 + */ +public class HttpHeaderTest { + + @Test + public void valueReturnedWhenSinglePossibleHeader() { + + final String key = "Head"; + final String value = "1234"; + + Function<String, String> reader = createReader(key, value); + HttpHeader header = new HttpHeader(key); + assertEquals(header.getAny(reader), value); + } + + @Test + public void nullReturnedWhenSingleNoMatchingHeader() { + + final String key = "Head"; + + Function<String, String> reader = createReader(key, null); + HttpHeader header = new HttpHeader(key); + assertNull(header.getAny(reader)); + } + + @Test + public void nullReturnedWhenNoneHeaderMatches() { + Function<String, String> reader = createReader("None", "Value"); + HttpHeader header = new HttpHeader("A", "B", "C"); + assertNull(header.getAny(reader)); + } + + @Test + public void valueReturnedWhenLastHeaderMatches() { + + final String lastKey = "Last"; + final String value = "1234"; + + Function<String, String> reader = createReader(lastKey, value); + HttpHeader header = new HttpHeader("First", "Second", lastKey); + assertEquals(header.getAny(reader), value); + } + + @Test + public void valueReturnedWhenFirstHeaderMatches() { + + final String firstKey = "First"; + final String value = "1234"; + + Function<String, String> reader = createReader(firstKey, value); + HttpHeader header = new HttpHeader(firstKey, "Second", "Third"); + assertEquals(header.getAny(reader), value); + } + + @Test + public void valueReturnedWhenMiddleHeaderMatches() { + + final String middleKey = "Second"; + final String value = "1234"; + + Function<String, String> reader = createReader(middleKey, value); + HttpHeader header = new HttpHeader("First", middleKey, "Third"); + assertEquals(header.getAny(reader), value); + } + + private Function<String, String> createReader(String key, String value) { + return h -> h.equals(key) ? value : null; + } +}
\ No newline at end of file diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilterTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilterTest.java new file mode 100644 index 0000000000..0e0be017df --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilterTest.java @@ -0,0 +1,262 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet.jaxrs; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.anyString; +import static org.openecomp.sdc.logging.servlet.jaxrs.LoggingRequestFilter.START_TIME_KEY; + +import java.lang.reflect.Method; +import java.util.UUID; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ResourceInfo; +import org.easymock.EasyMock; +import org.openecomp.sdc.logging.LoggingConstants; +import org.openecomp.sdc.logging.api.ContextData; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + +/** + * Unit testing JAX-RS request filter. + * + * @author evitaliy + * @since 19 Mar 2018 + */ +@PrepareForTest({LoggingContext.class, ContextData.class}) +public class LoggingRequestFilterTest extends PowerMockTestCase { + + private static final Class RESOURCE_CLASS = MockResource.class; + private static final Method RESOURCE_METHOD = MockResource.class.getDeclaredMethods()[0]; + private static final String RESOURCE_NAME = RESOURCE_CLASS.getName() + "." + RESOURCE_METHOD.getName(); + + private static final String RANDOM_REQUEST_ID = UUID.randomUUID().toString(); + private static final String RANDOM_PARTNER_NAME = UUID.randomUUID().toString(); + + /** + * Verify all mocks after each test. + */ + @AfterMethod + public void verifyMocks(ITestResult result) { + + try { + PowerMock.verifyAll(); + } catch (AssertionError e) { + throw new AssertionError("Expectations failed in: " + result.getMethod().getMethodName(), e); + } + } + + @Test + public void notHandledWhenNoMatchingResource() { + + PowerMock.mockStatic(LoggingContext.class); + PowerMock.replay(LoggingContext.class); + + new LoggingRequestFilter().filter(mockEmptyContainerRequestContext()); + } + + @Test + public void serviceNamePopulatedWhenThereIsMatchingResource() { + + mockContextDataBuilder(null, RESOURCE_NAME, null); + mockLoggingContext(); + + LoggingRequestFilter filter = new LoggingRequestFilter(); + filter.setResource(mockResource()); + + filter.filter(mockContainerRequestContext( + new RequestIdHeader(null), + new PartnerHeader(null))); + } + + @Test + public void partnerNamePopulatedWhenPresentInDefaultHeader() { + + mockContextDataBuilder(null, RESOURCE_NAME, RANDOM_PARTNER_NAME); + mockLoggingContext(); + + LoggingRequestFilter filter = new LoggingRequestFilter(); + filter.setResource(mockResource()); + + filter.filter(mockContainerRequestContext( + new RequestIdHeader(null), + new PartnerHeader(RANDOM_PARTNER_NAME))); + } + + @Test + public void partnerNamePopulatedWhenPresentInCustomHeader() { + + final String partnerHeader = "x-partner-header"; + mockContextDataBuilder(null, RESOURCE_NAME, RANDOM_PARTNER_NAME); + mockLoggingContext(); + + LoggingRequestFilter filter = new LoggingRequestFilter(); + filter.setResource(mockResource()); + filter.setPartnerNameHeaders(partnerHeader); + + filter.filter(mockContainerRequestContext( + new RequestIdHeader(null), + new PartnerHeader(partnerHeader, RANDOM_PARTNER_NAME))); + } + + @Test + public void requestIdPopulatedWhenPresentInDefaultHeader() { + + mockContextDataBuilder(RANDOM_REQUEST_ID, RESOURCE_NAME, null); + mockLoggingContext(); + + LoggingRequestFilter filter = new LoggingRequestFilter(); + filter.setResource(mockResource()); + + filter.filter(mockContainerRequestContext( + new RequestIdHeader(RANDOM_REQUEST_ID), + new PartnerHeader(null))); + } + + @Test + public void requestIdPopulatedWhenPresentInCustomHeader() { + + final String requestIdHeader = "x-request-id"; + mockContextDataBuilder(RANDOM_REQUEST_ID, RESOURCE_NAME, null); + mockLoggingContext(); + + LoggingRequestFilter filter = new LoggingRequestFilter(); + filter.setResource(mockResource()); + filter.setRequestIdHeaders(requestIdHeader); + + filter.filter(mockContainerRequestContext( + new RequestIdHeader(requestIdHeader, RANDOM_REQUEST_ID), + new PartnerHeader(null))); + } + + private ResourceInfo mockResource() { + ResourceInfo resource = EasyMock.mock(ResourceInfo.class); + //noinspection unchecked + EasyMock.expect(resource.getResourceClass()).andReturn(RESOURCE_CLASS); + EasyMock.expect(resource.getResourceMethod()).andReturn(RESOURCE_METHOD); + EasyMock.replay(resource); + return resource; + } + + private ContainerRequestContext mockEmptyContainerRequestContext() { + ContainerRequestContext requestContext = EasyMock.mock(ContainerRequestContext.class); + EasyMock.replay(requestContext); + return requestContext; + } + + private ContainerRequestContext mockContainerRequestContext(Header... headers) { + + ContainerRequestContext requestContext = EasyMock.mock(ContainerRequestContext.class); + + for (Header h : headers) { + EasyMock.expect(requestContext.getHeaderString(h.key)).andReturn(h.value); + } + + requestContext.setProperty(EasyMock.eq(START_TIME_KEY), EasyMock.anyLong()); + EasyMock.expectLastCall(); + + EasyMock.replay(requestContext); + return requestContext; + } + + private void mockContextDataBuilder(String requestId, String serviceName, String partnerName) { + + ContextData.ContextDataBuilder mockBuilder = EasyMock.mock(ContextData.ContextDataBuilder.class); + + if (requestId != null) { + EasyMock.expect(mockBuilder.requestId(requestId)).andReturn(mockBuilder); + } else { + EasyMock.expect(mockBuilder.requestId(anyString())).andReturn(mockBuilder); + } + + if (serviceName != null) { + EasyMock.expect(mockBuilder.serviceName(serviceName)).andReturn(mockBuilder); + } + + if (partnerName != null) { + EasyMock.expect(mockBuilder.partnerName(partnerName)).andReturn(mockBuilder); + } + + EasyMock.expect(mockBuilder.build()).andReturn(EasyMock.mock(ContextData.class)); + EasyMock.replay(mockBuilder); + + PowerMock.mockStatic(ContextData.class); + + ContextData.builder(); + PowerMock.expectLastCall().andReturn(mockBuilder); + + PowerMock.replay(ContextData.class); + } + + private void mockLoggingContext() { + + PowerMock.mockStatic(LoggingContext.class); + + LoggingContext.clear(); + EasyMock.expectLastCall().once(); + + LoggingContext.put(anyObject(ContextData.class)); + EasyMock.expectLastCall().once(); + + PowerMock.replay(LoggingContext.class); + } + + private abstract static class Header { + + private final String key; + private final String value; + + private Header(String key, String value) { + this.key = key; + this.value = value; + } + } + + private static class PartnerHeader extends Header { + + private PartnerHeader(String value) { + super(LoggingConstants.DEFAULT_PARTNER_NAME_HEADER, value); + } + + private PartnerHeader(String key, String value) { + super(key, value); + } + } + + private static class RequestIdHeader extends Header { + + private RequestIdHeader(String value) { + super(LoggingConstants.DEFAULT_REQUEST_ID_HEADER, value); + } + + private RequestIdHeader(String key, String value) { + super(key, value); + } + } + + private static class MockResource { + + @SuppressWarnings("EmptyMethod") + void process() { + // no-op + } + } +}
\ No newline at end of file diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilterTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilterTest.java new file mode 100644 index 0000000000..37627d5d5a --- /dev/null +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilterTest.java @@ -0,0 +1,234 @@ +/* + * Copyright © 2016-2018 European Support Limited + * + * 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.openecomp.sdc.logging.servlet.jaxrs; + +import static org.easymock.EasyMock.anyObject; +import static org.easymock.EasyMock.anyString; + +import java.util.Comparator; +import java.util.Objects; +import java.util.function.Consumer; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.container.ContainerRequestContext; +import javax.ws.rs.container.ContainerResponseContext; +import javax.ws.rs.core.Response; +import org.easymock.EasyMock; +import org.easymock.LogicalOperator; +import org.openecomp.sdc.logging.api.AuditData; +import org.openecomp.sdc.logging.api.Logger; +import org.openecomp.sdc.logging.api.LoggerFactory; +import org.openecomp.sdc.logging.api.LoggingContext; +import org.openecomp.sdc.logging.api.StatusCode; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.testng.PowerMockTestCase; +import org.testng.ITestResult; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.Test; + + +/** + * Unit testing JAX-RS response filter. + * + * @author evitaliy + * @since 19 Mar 2018 + */ +@PrepareForTest({LoggingContext.class, LoggerFactory.class}) +public class LoggingResponseFilterTest extends PowerMockTestCase { + + /** + * Verify all mocks after each test. + */ + @AfterMethod + public void verifyMocks(ITestResult result) { + + try { + PowerMock.verifyAll(); + } catch (AssertionError e) { + throw new AssertionError("Expectations failed in: " + result.getMethod().getMethodName(), e); + } + } + + @Test + public void noAuditWhenAuditDisabled() { + mockLogger(false, AuditData.builder().build()); + mockLoggingContext(); + new LoggingResponseFilter().filter(mockDisabledRequestContext(), mockDisabledResponseContext()); + } + + private void mockLogger(boolean enabled, AuditData auditData, Consumer<Logger>... additionalMockings) { + + Logger logger = EasyMock.mock(Logger.class); + + EasyMock.expect(logger.isAuditEnabled()).andReturn(enabled).atLeastOnce(); + + if (enabled) { + logger.audit(EasyMock.cmp(auditData, new AuditDataComparator(), LogicalOperator.EQUAL)); + EasyMock.expectLastCall(); + } + + for (Consumer<Logger> mocking : additionalMockings) { + mocking.accept(logger); + } + + EasyMock.replay(logger); + + PowerMock.mockStatic(LoggerFactory.class); + LoggerFactory.getLogger(LoggingResponseFilter.class); + PowerMock.expectLastCall().andReturn(logger); + PowerMock.replay(LoggerFactory.class); + } + + private void mockLoggingContext() { + PowerMock.mockStatic(LoggingContext.class); + LoggingContext.clear(); + EasyMock.expectLastCall().once(); + PowerMock.replay(LoggingContext.class); + } + + private ContainerRequestContext mockDisabledRequestContext() { + ContainerRequestContext requestContext = EasyMock.mock(ContainerRequestContext.class); + EasyMock.replay(requestContext); + return requestContext; + } + + private ContainerResponseContext mockDisabledResponseContext() { + ContainerResponseContext responseContext = EasyMock.mock(ContainerResponseContext.class); + EasyMock.replay(responseContext); + return responseContext; + } + + @Test + public void startTimeReadWhenPresentInRequestContext() { + + final String clientIp = "10.56.56.10"; + final long startTime = 12345L; + final Response.Status ok = Response.Status.OK; + + mockLogger(true, buildAuditData(startTime, clientIp, ok, StatusCode.COMPLETE)); + + mockLoggingContext(); + LoggingResponseFilter filter = new LoggingResponseFilter(); + filter.setHttpRequest(mockHttpRequest(clientIp)); + + filter.filter(mockRequestContext(startTime), mockResponseContext(ok)); + } + + private AuditData buildAuditData(long startTime, String clientIp, Response.Status responseStatus, + StatusCode status) { + return AuditData.builder().startTime(startTime).responseCode(Integer.toString(responseStatus.getStatusCode())) + .responseDescription(responseStatus.getReasonPhrase()).clientIpAddress(clientIp) + .statusCode(status).build(); + } + + private HttpServletRequest mockHttpRequest(String clientIp) { + HttpServletRequest servletRequest = EasyMock.mock(HttpServletRequest.class); + EasyMock.expect(servletRequest.getRemoteAddr()).andReturn(clientIp); + EasyMock.replay(servletRequest); + return servletRequest; + } + + private ContainerRequestContext mockRequestContext(Object startTime) { + ContainerRequestContext requestContext = EasyMock.mock(ContainerRequestContext.class); + EasyMock.expect(requestContext.getProperty(LoggingRequestFilter.START_TIME_KEY)).andReturn(startTime); + EasyMock.replay(requestContext); + return requestContext; + } + + private ContainerResponseContext mockResponseContext(Response.StatusType statusInfo) { + ContainerResponseContext responseContext = EasyMock.mock(ContainerResponseContext.class); + EasyMock.expect(responseContext.getStatusInfo()).andReturn(statusInfo); + EasyMock.replay(responseContext); + return responseContext; + } + + @Test + public void startTimeZeroWhenNotPresentInRequestContext() { + + final String clientIp = "10.56.56.12"; + final Response.Status ok = Response.Status.OK; + + AuditData expectedAuditData = buildAuditData(0, clientIp, ok, StatusCode.COMPLETE); + + mockLogger(true, expectedAuditData, logger -> { + logger.error(anyString(), anyObject(Object[].class)); + EasyMock.expectLastCall(); + }); + + mockLoggingContext(); + LoggingResponseFilter filter = new LoggingResponseFilter(); + filter.setHttpRequest(mockHttpRequest(clientIp)); + + filter.filter(mockRequestContext(null), mockResponseContext(ok)); + } + + @Test + public void startTimeZeroWhenIncorrectObjectType() { + + final String clientIp = "10.56.56.13"; + final Response.Status accepted = Response.Status.ACCEPTED; + + AuditData expectedAuditData = buildAuditData(0, clientIp, accepted, StatusCode.COMPLETE); + + mockLogger(true, expectedAuditData, logger -> { + logger.error(anyString(), new Object[] {anyString(), anyString()}); + EasyMock.expectLastCall(); + }); + + mockLoggingContext(); + LoggingResponseFilter filter = new LoggingResponseFilter(); + filter.setHttpRequest(mockHttpRequest(clientIp)); + + filter.filter(mockRequestContext("string object"), mockResponseContext(accepted)); + } + + @Test + public void statusErrorWhenHttpResponseGreaterThan399() { + + final Response.Status error = Response.Status.BAD_REQUEST; + final String clientIp = "10.56.56.13"; + final long startTime = 88668603L; + + AuditData expectedAuditData = buildAuditData(startTime, clientIp, error, StatusCode.ERROR); + + mockLogger(true, expectedAuditData); + + mockLoggingContext(); + LoggingResponseFilter filter = new LoggingResponseFilter(); + filter.setHttpRequest(mockHttpRequest(clientIp)); + + filter.filter(mockRequestContext(startTime), mockResponseContext(error)); + } + + private static class AuditDataComparator implements Comparator<AuditData> { + + @Override + public int compare(AuditData one, AuditData two) { + + // don't compare end time as it changes + if (Objects.equals(one.getClientIpAddress(), two.getClientIpAddress()) && Objects + .equals(one.getResponseCode(), two.getResponseCode()) && Objects + .equals(one.getResponseDescription(), one.getResponseDescription()) && one.getStartTime() == two + .getStartTime() && Objects.equals(one.getStatusCode(), two.getStatusCode())) { + + return 0; + } + + return -1; + } + } +}
\ No newline at end of file diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/pom.xml b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/pom.xml index 14f54b0835..dbaef2ada6 100644 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/pom.xml +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/pom.xml @@ -45,8 +45,6 @@ <version>${servlet.version}</version> <scope>provided</scope> </dependency> - - <!-- for testing --> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java deleted file mode 100644 index 0ca550a20a..0000000000 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright © 2016-2017 European Support Limited - * - * 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.openecomp.sdc.logging.servlet; - -import org.openecomp.sdc.logging.api.Logger; -import org.openecomp.sdc.logging.api.LoggerFactory; -import org.slf4j.MDC; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicLong; - - -/** - * - * <p>Pushes information required by EELF onto MDC (Mapped Diagnostic Context).</p> - * - * <p>This is servlet filter that should be configured in <i>web.xml</i> to be used. Example:</p> - * - * <pre> - * - * <filter> - * <filter-name>LoggingServletFilter</filter-name> - * <filter-class>org.openecomp.sdc.logging.servlet.LoggingFilter</filter-class> - * </filter> - * - * <filter-mapping> - * <filter-name>LoggingServletFilter</filter-name> - * <url-pattern>/*</url-pattern> - * </filter-mapping> - * - * </pre> - * - * @author evitaliy - * @since 25/07/2016. - */ -public class LoggingFilter implements Filter { - - // should be cashed to avoid low-level call, but with a timeout to account for IP or FQDN changes - private static final HostAddressCache HOST_ADDRESS = new HostAddressCache(); - private static final String UNKNOWN = "UNKNOWN"; - - private static final Logger LOGGER = LoggerFactory.getLogger(LoggingFilter.class); - - public void destroy() { - } - - public void doFilter(ServletRequest request, ServletResponse response, - FilterChain chain) throws IOException, ServletException { - - try { - - MDC.clear(); - - MDC.put("RequestId", UUID.randomUUID().toString()); - MDC.put("ServiceInstanceId", "N/A"); // not applicable - MDC.put("ServiceName", "ASDC"); - MDC.put("InstanceUUID", "N/A"); - - // For some reason chooses IPv4 or IPv6 in a random way - MDC.put("RemoteHost", request.getRemoteHost()); - - InetAddress host = HOST_ADDRESS.get(); - - String ipAddress; - String hostName; - if (host == null) { - ipAddress = UNKNOWN; - hostName = UNKNOWN; - } else { - ipAddress = host.getHostAddress(); - hostName = host.getHostName(); - } - - MDC.put("ServerIPAddress", ipAddress); - MDC.put("ServerFQDN", hostName); - - if(request instanceof HttpServletRequest) { - String userName = ((HttpServletRequest) request).getHeader("USER_ID"); - MDC.put("PartnerName", userName); - } - // TODO: Clarify what these stand for - // MDC.put("AlertSeverity", ); - // MDC.put("Timer", ); - - chain.doFilter(request, response); - - } finally { - MDC.clear(); - } - } - - public void init(FilterConfig config) throws ServletException { } - - private static class HostAddressCache { - - private static final long REFRESH_TIME = 1000L; - - private final AtomicLong lastUpdated = new AtomicLong(0L); - private InetAddress hostAddress; - - public InetAddress get() { - - long current = System.currentTimeMillis(); - if (current - lastUpdated.get() > REFRESH_TIME) { - - synchronized (this) { - - try { - lastUpdated.set(current); // set now to register the attempt even if failed - hostAddress = InetAddress.getLocalHost(); - } catch (UnknownHostException e) { - LOGGER.error("Failed to retrieve local hostname for logging", e); - hostAddress = null; - } - } - } - - return hostAddress; - } - } -} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/aspects/MetricsAspectTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/aspects/MetricsAspectTest.java index 4d3d6be34b..e4cd37995a 100644 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/aspects/MetricsAspectTest.java +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/aspects/MetricsAspectTest.java @@ -1,12 +1,12 @@ /* - * Copyright © 2016-2017 European Support Limited + * Copyright © 2016-2018 European Support Limited * * 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. @@ -16,6 +16,12 @@ package org.openecomp.sdc.logging.aspects; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.SourceLocation; @@ -30,399 +36,394 @@ import org.powermock.modules.testng.PowerMockTestCase; import org.testng.Assert; import org.testng.annotations.Test; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; - /** - * @author EVITALIY - * @since 17/08/2016. + * Unit-tests metrics aspect (AOP) behavior. + * + * @author evitaliy + * @since 17 Aug 2016 */ @PrepareForTest(LoggerFactory.class) public class MetricsAspectTest extends PowerMockTestCase { - private static final Object OBJ_TO_RETURN = new Object(); - private static final String EXPECTED_MESSAGE = "'{}' took {} milliseconds"; - - @Test - public void testLogExecutionTime() throws Throwable { - - String className = UUID.randomUUID().toString(); - String methodName = UUID.randomUUID().toString(); - - TestLogger logger = initLogging(className, true); - - MetricsAspect aspect = new MetricsAspect(); - MockProceedingJoinPoint pjp = new MockProceedingJoinPoint(className, methodName); - Object returned = aspect.logExecutionTime(pjp); - - Assert.assertEquals(OBJ_TO_RETURN, returned); - assertExecution(methodName, pjp, logger); - } - - @Test - public void testMetricsDisabled() throws Throwable { - - String className = UUID.randomUUID().toString(); - String methodName = UUID.randomUUID().toString(); - - TestLogger logger = initLogging(className, false); - - MetricsAspect aspect = new MetricsAspect(); - MockProceedingJoinPoint pjp = new MockProceedingJoinPoint(className, methodName); - Object returned = aspect.logExecutionTime(pjp); - - Assert.assertEquals(OBJ_TO_RETURN, returned); - Assert.assertEquals(1, pjp.getCount()); - // return any event - must be empty - Assert.assertFalse(logger.contains((event) -> true)); - } - - @Test(expectedExceptions = IllegalArgumentException.class) - public void testThrowingError() throws Throwable { - - String className = UUID.randomUUID().toString(); - String methodName = UUID.randomUUID().toString(); - - final TestLogger logger = initLogging(className, true); - - MetricsAspect aspect = new MetricsAspect(); - MockProceedingJoinPoint pjp = new MockProceedingJoinPointWithException(className, methodName); - - try { - aspect.logExecutionTime(pjp); - } finally { - assertExecution(methodName, pjp, logger); - } - } - - private TestLogger initLogging(String className, boolean enabled) { - TestLogger logger = new TestLogger(enabled); - PowerMock.mockStatic(LoggerFactory.class); - EasyMock.expect(LoggerFactory.getLogger(className)).andReturn(logger); - PowerMock.replay(LoggerFactory.class); - return logger; - } - - private void assertExecution(String methodName, MockProceedingJoinPoint pjp, TestLogger logger) { - - Assert.assertEquals(1, pjp.getCount()); - Assert.assertTrue(logger.contains((event) -> - (event != null) && (event.length == 3) && EXPECTED_MESSAGE.equals(event[0]) - && methodName.equals(event[1]) && (event[2] instanceof Long))); - } - - private static class MockSignature implements Signature { - - private final String className; - private final String methodName; - - private MockSignature(String className, String methodName) { - this.className = className; - this.methodName = methodName; - } - - @Override - public String toShortString() { - return null; - } - - @Override - public String toLongString() { - return null; - } - - @Override - public String getName() { - return methodName; - } - - @Override - public int getModifiers() { - return 0; - } - - @Override - public Class getDeclaringType() { - return null; - } - - @Override - public String getDeclaringTypeName() { - return className; - } - } - - private static class MockProceedingJoinPoint implements ProceedingJoinPoint { - - private AtomicInteger count = new AtomicInteger(0); - private Signature signature; - - MockProceedingJoinPoint(String className, String methodName) { - this.signature = new MockSignature(className, methodName); - } - - int getCount() { - return count.get(); - } - - @Override - public Object proceed() throws Throwable { - count.incrementAndGet(); - return OBJ_TO_RETURN; - } + private static final Object OBJ_TO_RETURN = new Object(); + private static final String EXPECTED_MESSAGE = "'{}' took {} milliseconds"; - @Override - public void set$AroundClosure(AroundClosure aroundClosure) { + @Test + public void testLogExecutionTime() throws Throwable { - } + String className = UUID.randomUUID().toString(); + String methodName = UUID.randomUUID().toString(); - @Override - public Object proceed(Object[] objects) throws Throwable { - return null; - } + TestLogger logger = initLogging(className, true); - @Override - public String toShortString() { - return null; - } + MetricsAspect aspect = new MetricsAspect(); + MockProceedingJoinPoint pjp = new MockProceedingJoinPoint(className, methodName); + Object returned = aspect.logExecutionTime(pjp); - @Override - public String toLongString() { - return null; + Assert.assertEquals(OBJ_TO_RETURN, returned); + assertExecution(methodName, pjp, logger); } - @Override - public Object getThis() { - return null; + private TestLogger initLogging(String className, boolean enabled) { + TestLogger logger = new TestLogger(enabled); + PowerMock.mockStatic(LoggerFactory.class); + EasyMock.expect(LoggerFactory.getLogger(className)).andReturn(logger); + PowerMock.replay(LoggerFactory.class); + return logger; } - @Override - public Object getTarget() { - return null; - } + private void assertExecution(String methodName, MockProceedingJoinPoint pjp, TestLogger logger) { - @Override - public Object[] getArgs() { - return new Object[0]; + Assert.assertEquals(1, pjp.getCount()); + Assert.assertTrue(logger.contains( + (event) -> (event != null) && (event.length == 3) && EXPECTED_MESSAGE.equals(event[0]) && methodName + .equals(event[1]) && (event[2] instanceof Long))); } - @Override - public Signature getSignature() { - return this.signature; - } + @Test + public void testMetricsDisabled() throws Throwable { - @Override - public SourceLocation getSourceLocation() { - return null; - } + String className = UUID.randomUUID().toString(); + String methodName = UUID.randomUUID().toString(); - @Override - public String getKind() { - return null; - } - - @Override - public StaticPart getStaticPart() { - return null; - } - } + TestLogger logger = initLogging(className, false); - private static class MockProceedingJoinPointWithException extends MockProceedingJoinPoint { + MetricsAspect aspect = new MetricsAspect(); + MockProceedingJoinPoint pjp = new MockProceedingJoinPoint(className, methodName); + Object returned = aspect.logExecutionTime(pjp); - MockProceedingJoinPointWithException(String className, String methodName) { - super(className, methodName); + Assert.assertEquals(OBJ_TO_RETURN, returned); + Assert.assertEquals(1, pjp.getCount()); + // return any event - must be empty + Assert.assertFalse(logger.contains((event) -> true)); } - @Override - public Object proceed() throws Throwable { - super.proceed(); - throw new IllegalArgumentException(); - } - } + @Test(expectedExceptions = IllegalArgumentException.class) + public void testThrowingError() throws Throwable { - private class TestLogger implements Logger { + String className = UUID.randomUUID().toString(); + String methodName = UUID.randomUUID().toString(); - private final boolean enabled; - private List<Object[]> events = Collections.synchronizedList(new ArrayList<>(10)); + final TestLogger logger = initLogging(className, true); - TestLogger(boolean enabled) { - this.enabled = enabled; - } + MetricsAspect aspect = new MetricsAspect(); + MockProceedingJoinPoint pjp = new MockProceedingJoinPointWithException(className, methodName); - @Override - public String getName() { - throw new RuntimeException("Not implemented"); + try { + aspect.logExecutionTime(pjp); + } finally { + assertExecution(methodName, pjp, logger); + } } - @Override - public boolean isMetricsEnabled() { - return this.enabled; - } + private static class MockSignature implements Signature { - @Override - public void metrics(String var1) { - throw new RuntimeException("Not implemented"); - } + private final String className; + private final String methodName; - @Override - public void metrics(String var1, Object var2) { - throw new RuntimeException("Not implemented"); - } + private MockSignature(String className, String methodName) { + this.className = className; + this.methodName = methodName; + } - @Override - public void metrics(String var1, Object var2, Object var3) { + @Override + public String toShortString() { + return null; + } - if (this.enabled) { - events.add(new Object[]{var1, var2, var3}); - } - } + @Override + public String toLongString() { + return null; + } - @Override - public void metrics(String var1, Object... var2) { - throw new RuntimeException("Not implemented"); - } + @Override + public String getName() { + return methodName; + } - @Override - public void metrics(String var1, Throwable throwable) { - throw new RuntimeException("Not implemented"); - } + @Override + public int getModifiers() { + return 0; + } - @Override - public boolean isAuditEnabled() { - throw new RuntimeException("Not implemented"); - } + @Override + public Class getDeclaringType() { + return null; + } - @Override - public void audit(AuditData var1) { - throw new RuntimeException("Not implemented"); + @Override + public String getDeclaringTypeName() { + return className; + } } - @Override - public boolean isDebugEnabled() { - throw new RuntimeException("Not implemented"); - } + private static class MockProceedingJoinPoint implements ProceedingJoinPoint { - @Override - public void debug(String var1) { - throw new RuntimeException("Not implemented"); - } + private final AtomicInteger count = new AtomicInteger(0); + private final Signature signature; - @Override - public void debug(String var1, Object var2) { - throw new RuntimeException("Not implemented"); - } + MockProceedingJoinPoint(String className, String methodName) { + this.signature = new MockSignature(className, methodName); + } - @Override - public void debug(String var1, Object var2, Object var3) { - throw new RuntimeException("Not implemented"); - } + int getCount() { + return count.get(); + } - @Override - public void debug(String var1, Object... var2) { - throw new RuntimeException("Not implemented"); - } + @Override + public void set$AroundClosure(AroundClosure aroundClosure) { - @Override - public void debug(String var1, Throwable throwable) { - throw new RuntimeException("Not implemented"); - } + } - @Override - public boolean isInfoEnabled() { - throw new RuntimeException("Not implemented"); - } + @Override + public Object proceed() throws Throwable { + count.incrementAndGet(); + return OBJ_TO_RETURN; + } - @Override - public void info(String var1) { - throw new RuntimeException("Not implemented"); - } + @Override + public Object proceed(Object[] objects) { + return null; + } - @Override - public void info(String var1, Object var2) { - throw new RuntimeException("Not implemented"); - } + @Override + public String toShortString() { + return null; + } - @Override - public void info(String var1, Object var2, Object var3) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void info(String var1, Object... var2) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void info(String var1, Throwable throwable) { - throw new RuntimeException("Not implemented"); - } - - @Override - public boolean isWarnEnabled() { - throw new RuntimeException("Not implemented"); - } - - @Override - public void warn(String var1) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void warn(String var1, Object var2) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void warn(String var1, Object... var2) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void warn(String var1, Object var2, Object var3) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void warn(String var1, Throwable throwable) { - throw new RuntimeException("Not implemented"); - } - - @Override - public boolean isErrorEnabled() { - throw new RuntimeException("Not implemented"); - } - - @Override - public void error(String var1) { - throw new RuntimeException("Not implemented"); - } + @Override + public String toLongString() { + return null; + } - @Override - public void error(String var1, Object var2) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void error(String var1, Object var2, Object var3) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void error(String var1, Object... var2) { - throw new RuntimeException("Not implemented"); - } - - @Override - public void error(String var1, Throwable throwable) { - throw new RuntimeException("Not implemented"); - } + @Override + public Object getThis() { + return null; + } - public boolean contains(Predicate<Object[]> predicate) { - return events.stream().anyMatch(predicate); + @Override + public Object getTarget() { + return null; + } + + @Override + public Object[] getArgs() { + return new Object[0]; + } + + @Override + public Signature getSignature() { + return this.signature; + } + + @Override + public SourceLocation getSourceLocation() { + return null; + } + + @Override + public String getKind() { + return null; + } + + @Override + public StaticPart getStaticPart() { + return null; + } + } + + private static class MockProceedingJoinPointWithException extends MockProceedingJoinPoint { + + MockProceedingJoinPointWithException(String className, String methodName) { + super(className, methodName); + } + + @Override + public Object proceed() throws Throwable { + super.proceed(); + throw new IllegalArgumentException(); + } + } + + private class TestLogger implements Logger { + + private final boolean enabled; + private final List<Object[]> events = Collections.synchronizedList(new ArrayList<>(10)); + + TestLogger(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String getName() { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isMetricsEnabled() { + return this.enabled; + } + + @Override + public void metrics(String var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void metrics(String var1, Object var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void metrics(String var1, Object var2, Object var3) { + + if (this.enabled) { + events.add(new Object[] {var1, var2, var3}); + } + } + + @Override + public void metrics(String var1, Object... var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void metrics(String var1, Throwable throwable) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isAuditEnabled() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void audit(AuditData var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isDebugEnabled() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void debug(String var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void debug(String var1, Object var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void debug(String var1, Object var2, Object var3) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void debug(String var1, Object... var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void debug(String var1, Throwable throwable) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isInfoEnabled() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void info(String var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void info(String var1, Object var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void info(String var1, Object var2, Object var3) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void info(String var1, Object... var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void info(String var1, Throwable throwable) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isWarnEnabled() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void warn(String var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void warn(String var1, Object var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void warn(String var1, Object... var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void warn(String var1, Object var2, Object var3) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void warn(String var1, Throwable throwable) { + throw new RuntimeException("Not implemented"); + } + + @Override + public boolean isErrorEnabled() { + throw new RuntimeException("Not implemented"); + } + + @Override + public void error(String var1) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void error(String var1, Object var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void error(String var1, Object var2, Object var3) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void error(String var1, Object... var2) { + throw new RuntimeException("Not implemented"); + } + + @Override + public void error(String var1, Throwable throwable) { + throw new RuntimeException("Not implemented"); + } + + boolean contains(Predicate<Object[]> predicate) { + return events.stream().anyMatch(predicate); + } } - } } diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java deleted file mode 100644 index 95a5421be0..0000000000 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/servlet/LoggingFilterTest.java +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright © 2016-2017 European Support Limited - * - * 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.openecomp.sdc.logging.servlet; - -import org.slf4j.MDC; -import org.testng.Assert; -import org.testng.annotations.Test; - -import javax.servlet.FilterChain; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNull; - -/** - * TODO: Add more tests - * - * @author EVITALIY - * @since 17/08/2016. - */ -public class LoggingFilterTest { - - private static final String REMOTE_HOST = UUID.randomUUID().toString(); - - @Test - public void testDoFilter() throws Exception { - LoggingFilter loggingFilter = new LoggingFilter(); - ServletRequest mockRequest = new TestServletRequest(); - ServletResponse mockResponse = new TestServletResponse(); - TestFilterChain mockChain = new TestFilterChain(); - loggingFilter.doFilter(mockRequest, mockResponse, mockChain); - assertEquals(1, mockChain.getCount()); - assertNull(MDC.getCopyOfContextMap()); - } - - private static class TestServletRequest implements ServletRequest { - - @Override - public Object getAttribute(String s) { - return null; - } - - @Override - public Enumeration getAttributeNames() { - return null; - } - - @Override - public String getCharacterEncoding() { - return null; - } - - @Override - public void setCharacterEncoding(String s) throws UnsupportedEncodingException { - - } - - @Override - public int getContentLength() { - return 0; - } - - @Override - public String getContentType() { - return null; - } - - @Override - public ServletInputStream getInputStream() throws IOException { - return null; - } - - @Override - public String getParameter(String s) { - return null; - } - - @Override - public Enumeration getParameterNames() { - return null; - } - - @Override - public String[] getParameterValues(String s) { - return new String[0]; - } - - @Override - public Map getParameterMap() { - return null; - } - - @Override - public String getProtocol() { - return null; - } - - @Override - public String getScheme() { - return null; - } - - @Override - public String getServerName() { - return null; - } - - @Override - public int getServerPort() { - return 0; - } - - @Override - public BufferedReader getReader() throws IOException { - return null; - } - - @Override - public String getRemoteAddr() { - return null; - } - - @Override - public String getRemoteHost() { - return REMOTE_HOST; - } - - @Override - public void setAttribute(String s, Object o) { - - } - - @Override - public void removeAttribute(String s) { - - } - - @Override - public Locale getLocale() { - return null; - } - - @Override - public Enumeration getLocales() { - return null; - } - - @Override - public boolean isSecure() { - return false; - } - - @Override - public RequestDispatcher getRequestDispatcher(String s) { - return null; - } - - @Override - public String getRealPath(String s) { - return null; - } - - @Override - public int getRemotePort() { - return 0; - } - - @Override - public String getLocalName() { - return null; - } - - @Override - public String getLocalAddr() { - return null; - } - - @Override - public int getLocalPort() { - return 0; - } - } - - private static class TestFilterChain implements FilterChain { - - private AtomicInteger count = new AtomicInteger(0); - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) - throws IOException, ServletException { - - Assert.assertNotNull(MDC.get("RequestId")); - Assert.assertEquals(MDC.get("ServiceInstanceId"), "N/A"); - Assert.assertEquals(MDC.get("ServiceName"), "ASDC"); - Assert.assertEquals(MDC.get("InstanceUUID"), "N/A"); - Assert.assertEquals(MDC.get("RemoteHost"), REMOTE_HOST); - - InetAddress host = InetAddress.getLocalHost(); - Assert.assertEquals(MDC.get("ServerIPAddress"), host.getHostAddress()); - Assert.assertEquals(MDC.get("ServerFQDN"), host.getHostName()); - - count.incrementAndGet(); - } - - public int getCount() { - return count.get(); - } - } - - private static class TestServletResponse implements ServletResponse { - - @Override - public String getCharacterEncoding() { - return null; - } - - @Override - public void setCharacterEncoding(String s) { - - } - - @Override - public String getContentType() { - return null; - } - - @Override - public void setContentType(String s) { - - } - - @Override - public ServletOutputStream getOutputStream() throws IOException { - return null; - } - - @Override - public PrintWriter getWriter() throws IOException { - return null; - } - - @Override - public void setContentLength(int i) { - - } - - @Override - public int getBufferSize() { - return 0; - } - - @Override - public void setBufferSize(int i) { - - } - - @Override - public void flushBuffer() throws IOException { - - } - - @Override - public void resetBuffer() { - - } - - @Override - public boolean isCommitted() { - return false; - } - - @Override - public void reset() { - - } - - @Override - public Locale getLocale() { - return null; - } - - @Override - public void setLocale(Locale locale) { - - } - } -} diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapperTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapperTest.java index 9093f7d066..2c5233f9c9 100644 --- a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapperTest.java +++ b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapperTest.java @@ -39,9 +39,12 @@ import org.slf4j.MDC; import org.testng.annotations.Test; /** + * Unit-test of SLF4J implementation of Logger. + * * @author evitaliy * @since 05 Mar 18 */ +@SuppressWarnings("CheckStyle") public class SLF4JLoggerWrapperTest { @Test @@ -170,21 +173,29 @@ public class SLF4JLoggerWrapperTest { @Override public Object invoke(Object proxy, Method method, Object[] args) { - // return the remembered MDC for spying - if (method.getName().equals("mdc")) { + if (isReturnMdcMethod(method)) { return mdc; } - // filter out everything that's not related to audit - if (!method.getName().equals("info") || args.length == 0 || !args[0].equals(Markers.AUDIT)) { - throw new UnsupportedOperationException("Method " + method.getName() + " with arguments " + - Arrays.toString(args) + " wasn't supposed to be called"); + if (!isAuditMethod(method, args)) { + throw new UnsupportedOperationException("Method " + method.getName() + " with arguments " + + Arrays.toString(args) + " wasn't supposed to be called"); } - // remember the MDC that was active during the invocation + storeEffectiveMdc(); + return null; + } + + private boolean isAuditMethod(Method method, Object[] args) { + return (method.getName().equals("info") && args.length > 0 && args[0].equals(Markers.AUDIT)); + } + + private void storeEffectiveMdc() { mdc = MDC.getCopyOfContextMap(); + } - return null; + private boolean isReturnMdcMethod(Method method) { + return method.equals(SpyLogger.class.getDeclaredMethods()[0]); } } }
\ No newline at end of file |