aboutsummaryrefslogtreecommitdiffstats
path: root/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main
diff options
context:
space:
mode:
authorolegb <olegb@amdocs.com>2018-02-19 16:24:54 +0200
committerOren Kleks <orenkle@amdocs.com>2018-03-26 08:29:36 +0000
commitf2cee7829ae7d8fae58239dd0018b2aa790c0251 (patch)
treeef6ca1bd45d5364299020b6858f61584bfd59239 /openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main
parent365d1c3f7db39eeec66437cf9f6c77bcce729819 (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>
Diffstat (limited to 'openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main')
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/LoggingConstants.java40
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/api/ServiceBinder.java22
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/HttpHeader.java55
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/LoggingFilter.java136
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilter.java118
-rw-r--r--openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingResponseFilter.java147
6 files changed, 505 insertions, 13 deletions
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>
+ *
+ * &lt;filter&gt;
+ * &lt;filter-name&gt;LoggingServletFilter&lt;/filter-name&gt;
+ * &lt;filter-class&gt;org.openecomp.sdc.logging.servlet.LoggingFilter&lt;/filter-class&gt;
+ * &lt;init-param&gt;
+ * &lt;param-name&gt;requestIdHeaders&lt;/param-name&gt;
+ * &lt;param-value&gt;X-ONAP-RequestID&lt;/param-value&gt;
+ * &lt;/init-param&gt;
+ * &lt;init-param&gt;
+ * &lt;param-name&gt;partnerNameHeaders&lt;/param-name&gt;
+ * &lt;param-value&gt;USER_ID&lt;/param-value&gt;
+ * &lt;/init-param&gt;
+ * &lt;/filter&gt;
+ *
+ * &lt;filter-mapping&gt;
+ * &lt;filter-name&gt;LoggingServletFilter&lt;/filter-name&gt;
+ * &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
+ * &lt;/filter-mapping&gt;
+ *
+ * </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>
+ * &lt;jaxrs:providers&gt;
+ * &lt;bean class="org.openecomp.sdc.logging.ws.rs.LoggingRequestFilter"&gt;
+ * &lt;property name="requestIdHeaders" value="X-ONAP-RequestID"/&gt;
+ * &lt;property name="partnerNameHeaders" value="X-ONAP-InstanceID"/&gt;
+ * &lt;/bean&gt;
+ * &lt;/jaxrs:providers&gt;
+ * </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>
+ * &lt;jaxrs:providers&gt;
+ * &lt;bean class="org.openecomp.sdc.logging.ws.rs.LoggingResponseFilter"/&gt;
+ * &lt;/jaxrs:providers&gt;
+ * </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);
+ }
+ }
+}
+