diff --git a/README.md b/README.md
index 53939952..94aacb29 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,7 @@ All in all, you should do the following:
* adjust your logging configuration accordingly.
-Say, you want to make use of the *servlet filter* feature, then you need to add the following dependency to your POM with property `cf-logging-version` referring to the latest nexus version (currently `2.1.5`):
+Say, you want to make use of the *servlet filter* feature, then you need to add the following dependency to your POM with property `cf-logging-version` referring to the latest nexus version (currently `2.2.0`):
```xml
diff --git a/cf-java-logging-support-core/pom.xml b/cf-java-logging-support-core/pom.xml
index 60391daf..3a2d56a5 100644
--- a/cf-java-logging-support-core/pom.xml
+++ b/cf-java-logging-support-core/pom.xml
@@ -32,7 +32,7 @@
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
../pom.xml
diff --git a/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecord.java b/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecord.java
index 636e76a3..78bb1cde 100644
--- a/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecord.java
+++ b/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecord.java
@@ -1,6 +1,5 @@
package com.sap.hcp.cf.logging.common;
-import java.io.Closeable;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -39,7 +38,7 @@
*
*
*/
-public class RequestRecord implements Closeable {
+public class RequestRecord {
/*
* -- default values for request fields that are marked as "required"
@@ -100,7 +99,6 @@ public RequestRecord(String layerKey, Direction direction) {
addTag(Fields.DIRECTION, direction.toString());
setDefaults();
start();
- RequestRecordHolder.add(this);
}
/**
@@ -198,11 +196,6 @@ public long stop() {
return endMs;
}
- @Override
- public void close() {
- RequestRecordHolder.remove(this);
- }
-
@Override
public String toString() {
/*
diff --git a/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecordHolder.java b/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecordHolder.java
deleted file mode 100644
index d286b081..00000000
--- a/cf-java-logging-support-core/src/main/java/com/sap/hcp/cf/logging/common/RequestRecordHolder.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.sap.hcp.cf.logging.common;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * As we need to keep track of nested calls we use thread local storage to
- * manage a set of RequestRecord instances that are "in flight" for individual
- * thread.
- *
- * Upon removal of a record, we check whether that one is the last one so that
- * we can clear context information.
- *
- */
-public class RequestRecordHolder {
-
- private static final ThreadLocal> RECORD_SETS = new ThreadLocal>();
-
- public static void add(RequestRecord rr) {
- if (rr == null) {
- return;
- }
- Set recSet = RECORD_SETS.get();
- if (recSet == null) {
- recSet = new HashSet();
- RECORD_SETS.set(recSet);
- }
- recSet.add(rr);
- }
-
- public static void remove(RequestRecord rr) {
- if (rr == null) {
- return;
- }
- Set recSet = RECORD_SETS.get();
- if (recSet.remove(rr)) {
- /*
- * -- time to clean up if this was the last
- */
- if (recSet.isEmpty()) {
- rr.resetContext();
- }
- }
- }
-}
diff --git a/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/RequestRecordConfiguratorTest.java b/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/RequestRecordConfiguratorTest.java
index c704a4e2..b7b2c6bc 100644
--- a/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/RequestRecordConfiguratorTest.java
+++ b/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/RequestRecordConfiguratorTest.java
@@ -20,7 +20,6 @@ public void testAddingSingleActivatedOptionalTagToRequestRecord() throws JSONObj
String tag = "TestTag";
to(requestRecord).addOptionalTag(canBeLogged, key, tag);
- requestRecord.close();
assertEquals(tag, getFieldFromRequestRecord(requestRecord, key));
}
@@ -33,7 +32,6 @@ public void testAddingSingleForbiddenOptionalTagToRequestRecord() throws JSONObj
String tag = "TestTag";
to(requestRecord).addOptionalTag(canBeLogged, key, tag);
- requestRecord.close();
assertEquals(Defaults.REDACTED, getFieldFromRequestRecord(requestRecord, key));
}
@@ -46,7 +44,6 @@ public void testAddingSingleForbiddenOptionalNullTagToRequestRecord() throws JSO
String tag = Defaults.UNKNOWN;
to(requestRecord).addOptionalTag(canBeLogged, key, tag);
- requestRecord.close();
assertEquals(Defaults.UNKNOWN, getFieldFromRequestRecord(requestRecord, key));
}
@@ -59,7 +56,6 @@ public void testAddingSingleActivatedOptionalNullTagToRequestRecord() throws JSO
String tag = Defaults.UNKNOWN;
to(requestRecord).addOptionalTag(canBeLogged, key, tag);
- requestRecord.close();
assertEquals(Defaults.UNKNOWN, getFieldFromRequestRecord(requestRecord, key));
}
diff --git a/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/converter/TestJsonMessageConverter.java b/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/converter/TestJsonMessageConverter.java
index d5d808f0..e135da45 100644
--- a/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/converter/TestJsonMessageConverter.java
+++ b/cf-java-logging-support-core/src/test/java/com/sap/hcp/cf/logging/common/converter/TestJsonMessageConverter.java
@@ -58,7 +58,6 @@ public void testLogRecordMsgNotFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(formatMsg(jmc, lmsg), is(lmsg));
- lrec.close();
}
@Test
@@ -68,7 +67,6 @@ public void testLogRecordMsgFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(formatMsg(jmc, lmsg), is(lmsg.substring(1, lmsg.length() - 1)));
- lrec.close();
}
@Test
diff --git a/cf-java-logging-support-jersey/pom.xml b/cf-java-logging-support-jersey/pom.xml
index 10552e1d..a50fc3e4 100644
--- a/cf-java-logging-support-jersey/pom.xml
+++ b/cf-java-logging-support-jersey/pom.xml
@@ -9,7 +9,7 @@
../pom.xml
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
cf-java-logging-support-jersey
diff --git a/cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/ResponseHandler.java b/cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/ResponseHandler.java
index dd7d726a..4a79c4f8 100644
--- a/cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/ResponseHandler.java
+++ b/cf-java-logging-support-jersey/src/main/java/com/sap/hcp/cf/logging/jersey/filter/ResponseHandler.java
@@ -20,10 +20,6 @@ public void handle(ResponseContextAdapter responseContext, RequestRecord rr) {
rr.addValue(Fields.RESPONSE_STATUS, new LongValue(responseContext.getStatus()));
rr.stop();
LOGGER.info(Markers.REQUEST_MARKER, "{}", rr);
- /*
- * -- close this instance
- */
- rr.close();
} else {
LOGGER.error("No record found to handle response {}", responseContext);
}
diff --git a/cf-java-logging-support-log4j2/pom.xml b/cf-java-logging-support-log4j2/pom.xml
index 2b28aff5..2982900b 100644
--- a/cf-java-logging-support-log4j2/pom.xml
+++ b/cf-java-logging-support-log4j2/pom.xml
@@ -11,7 +11,7 @@
../pom.xml
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
diff --git a/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/converter/TestJsonMessageConverter.java b/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/converter/TestJsonMessageConverter.java
index 4f09b9b1..98dec5db 100644
--- a/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/converter/TestJsonMessageConverter.java
+++ b/cf-java-logging-support-log4j2/src/test/java/com/sap/hcp/cf/log4j2/converter/TestJsonMessageConverter.java
@@ -56,7 +56,6 @@ public void testLogRecordMsgNotFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(format(jmc, makeEvent(lmsg, NO_ARGS)), is(lmsg));
- lrec.close();
}
@Test
@@ -65,7 +64,6 @@ public void testLogRecordMsgFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(format(jmc, makeEvent(lmsg, NO_ARGS)), is(lmsg.substring(1, lmsg.length() - 1)));
- lrec.close();
}
@Test
diff --git a/cf-java-logging-support-logback/pom.xml b/cf-java-logging-support-logback/pom.xml
index 9a6f9ec5..7711472e 100644
--- a/cf-java-logging-support-logback/pom.xml
+++ b/cf-java-logging-support-logback/pom.xml
@@ -10,7 +10,7 @@
../pom.xml
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
diff --git a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/perf/PerfTestRequestRecord.java b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/perf/PerfTestRequestRecord.java
index f9d24b7b..8446a85a 100644
--- a/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/perf/PerfTestRequestRecord.java
+++ b/cf-java-logging-support-logback/src/main/java/com/sap/hcp/cf/logback/perf/PerfTestRequestRecord.java
@@ -12,44 +12,39 @@
public class PerfTestRequestRecord {
- private static final int DEF_ITERATIONS = 1000000;
- private final int iterations;
- private static final Logger LOGGER = LoggerFactory.getLogger(PerfTestRequestRecord.class);
-
- public PerfTestRequestRecord(int iterations) {
- if (iterations > 0) {
- this.iterations = iterations;
- } else {
- this.iterations = DEF_ITERATIONS;
- }
- }
-
- public static void main(String[] args) {
- new PerfTestRequestRecord(DEF_ITERATIONS).run(args);
- }
-
- private void run(String[] args) {
- long start = System.nanoTime();
- PrintStream defOut = System.out;
- System.setOut(new PrintStream(new OutputStream() {
-
- @Override
- public void write(int b) throws IOException {
- }
- }));
- for (int i = 0; i < iterations; i++) {
- RequestRecord rrec = null;
- try {
- rrec = new RequestRecord(PerfTestRequestRecord.class.getName());
- LOGGER.info(Markers.REQUEST_MARKER, rrec.toString());
- } finally {
- rrec.close();
- }
- }
- double delta = (System.nanoTime() - start) / 1000000.0;
- System.setOut(defOut);
- System.out.println("Writing " + iterations + " records took " + delta + " msecs, " + delta / iterations +
- " msecs per record");
- }
+ private static final int DEF_ITERATIONS = 1000000;
+ private final int iterations;
+ private static final Logger LOGGER = LoggerFactory.getLogger(PerfTestRequestRecord.class);
+
+ public PerfTestRequestRecord(int iterations) {
+ if (iterations > 0) {
+ this.iterations = iterations;
+ } else {
+ this.iterations = DEF_ITERATIONS;
+ }
+ }
+
+ public static void main(String[] args) {
+ new PerfTestRequestRecord(DEF_ITERATIONS).run(args);
+ }
+
+ private void run(String[] args) {
+ long start = System.nanoTime();
+ PrintStream defOut = System.out;
+ System.setOut(new PrintStream(new OutputStream() {
+
+ @Override
+ public void write(int b) throws IOException {
+ }
+ }));
+ for (int i = 0; i < iterations; i++) {
+ RequestRecord rrec = new RequestRecord(PerfTestRequestRecord.class.getName());
+ LOGGER.info(Markers.REQUEST_MARKER, rrec.toString());
+ }
+ double delta = (System.nanoTime() - start) / 1000000.0;
+ System.setOut(defOut);
+ System.out.println("Writing " + iterations + " records took " + delta + " msecs, " + delta / iterations
+ + " msecs per record");
+ }
}
diff --git a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/converter/TestJsonMessageConverter.java b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/converter/TestJsonMessageConverter.java
index c9fbdb17..af649988 100644
--- a/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/converter/TestJsonMessageConverter.java
+++ b/cf-java-logging-support-logback/src/test/java/com/sap/hcp/cf/logback/converter/TestJsonMessageConverter.java
@@ -66,7 +66,6 @@ public void testLogRecordMsgNotFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(jmc.convert(makeEvent(lmsg, NO_ARGS)), is(lmsg));
- lrec.close();
}
@Test
@@ -77,7 +76,6 @@ public void testLogRecordMsgFlattened() {
RequestRecord lrec = new RequestRecord(LOG_PROVIDER);
String lmsg = lrec.toString();
assertThat(jmc.convert(makeEvent(lmsg, NO_ARGS)), is(lmsg.substring(1, lmsg.length() - 1)));
- lrec.close();
}
@Test
diff --git a/cf-java-logging-support-servlet/pom.xml b/cf-java-logging-support-servlet/pom.xml
index db120faf..d340c655 100644
--- a/cf-java-logging-support-servlet/pom.xml
+++ b/cf-java-logging-support-servlet/pom.xml
@@ -9,7 +9,7 @@
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
../pom.xml
diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImpl.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImpl.java
new file mode 100644
index 00000000..2809320a
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImpl.java
@@ -0,0 +1,162 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.MDC;
+
+public class LoggingAsyncContextImpl implements AsyncContext {
+
+ private AsyncContext asyncContext;
+
+ public LoggingAsyncContextImpl(AsyncContext asyncContext, final RequestLoggingVisitor loggingVisitor) {
+ this.asyncContext = asyncContext;
+ asyncContext.addListener(new AsyncListener() {
+
+ @Override
+ public void onTimeout(AsyncEvent event) throws IOException {
+ generateLog(loggingVisitor);
+ }
+
+ private void generateLog(final RequestLoggingVisitor loggingVisitor) {
+ ServletRequest request = getRequest();
+ ServletResponse response = getResponse();
+ if (request instanceof HttpServletRequest && response instanceof HttpServletResponse) {
+ HttpServletRequest httpRequest = (HttpServletRequest) request;
+ HttpServletResponse httpResponse = (HttpServletResponse) response;
+ Map contextMap = getContextMap();
+ Map currentContextMap = MDC.getCopyOfContextMap();
+ try {
+ MDC.setContextMap(contextMap);
+ loggingVisitor.logRequest(httpRequest, httpResponse);
+ } finally {
+ if (currentContextMap != null) {
+ MDC.setContextMap(currentContextMap);
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void onStartAsync(AsyncEvent event) throws IOException {
+ }
+
+ @Override
+ public void onError(AsyncEvent event) throws IOException {
+ generateLog(loggingVisitor);
+ }
+
+ @Override
+ public void onComplete(AsyncEvent event) throws IOException {
+ generateLog(loggingVisitor);
+ }
+ });
+ }
+
+ private Map getContextMap() {
+ try {
+ @SuppressWarnings("unchecked")
+ Map fromRequest = (Map) getRequest().getAttribute(MDC.class.getName());
+ return fromRequest != null ? fromRequest : Collections.emptyMap();
+ } catch (ClassCastException ignored) {
+ return Collections.emptyMap();
+ }
+ }
+
+ @Override
+ public void start(Runnable run) {
+ try {
+ Map contextMap = getContextMap();
+ asyncContext.start(new Runnable() {
+
+ @Override
+ public void run() {
+ Map currentContextMap = MDC.getCopyOfContextMap();
+ try {
+ MDC.setContextMap(contextMap);
+ run.run();
+ } finally {
+ if (currentContextMap != null) {
+ MDC.setContextMap(currentContextMap);
+ }
+ }
+ }
+ });
+ } catch (ClassCastException ignored) {
+ asyncContext.start(run);
+ }
+ }
+
+ @Override
+ public ServletRequest getRequest() {
+ return asyncContext.getRequest();
+ }
+
+ @Override
+ public ServletResponse getResponse() {
+ return asyncContext.getResponse();
+ }
+
+ @Override
+ public boolean hasOriginalRequestAndResponse() {
+ return asyncContext.hasOriginalRequestAndResponse();
+ }
+
+ @Override
+ public void dispatch() {
+ asyncContext.dispatch();
+ }
+
+ @Override
+ public void dispatch(String path) {
+ asyncContext.dispatch(path);
+ }
+
+ @Override
+ public void dispatch(ServletContext context, String path) {
+ asyncContext.dispatch(context, path);
+ }
+
+ @Override
+ public void complete() {
+ asyncContext.complete();
+ }
+
+ @Override
+ public void addListener(AsyncListener listener) {
+ asyncContext.addListener(listener);
+ }
+
+ @Override
+ public void addListener(AsyncListener listener, ServletRequest servletRequest, ServletResponse servletResponse) {
+ asyncContext.addListener(listener, servletRequest, servletResponse);
+ }
+
+ @Override
+ public T createListener(Class clazz) throws ServletException {
+ return asyncContext.createListener(clazz);
+ }
+
+ @Override
+ public void setTimeout(long timeout) {
+ asyncContext.setTimeout(timeout);
+ }
+
+ @Override
+ public long getTimeout() {
+ return asyncContext.getTimeout();
+ }
+
+}
diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapper.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapper.java
new file mode 100644
index 00000000..d5b1f124
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapper.java
@@ -0,0 +1,28 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+public class LoggingContextRequestWrapper extends HttpServletRequestWrapper {
+
+ private RequestLoggingVisitor loggingVisitor;
+
+ public LoggingContextRequestWrapper(HttpServletRequest request, RequestLoggingVisitor loggingVisitor) {
+ super(request);
+ this.loggingVisitor = loggingVisitor;
+ }
+
+ @Override
+ public AsyncContext startAsync() throws IllegalStateException {
+ return new LoggingAsyncContextImpl(super.startAsync(), loggingVisitor);
+ }
+
+ @Override
+ public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)
+ throws IllegalStateException {
+ return new LoggingAsyncContextImpl(super.startAsync(servletRequest, servletResponse), loggingVisitor);
+ }
+}
diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingFilter.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingFilter.java
index 39fdd6c6..7aaa35fb 100644
--- a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingFilter.java
+++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingFilter.java
@@ -13,8 +13,6 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import com.sap.hcp.cf.logging.common.Defaults;
@@ -22,8 +20,6 @@
import com.sap.hcp.cf.logging.common.HttpHeaders;
import com.sap.hcp.cf.logging.common.LogContext;
import com.sap.hcp.cf.logging.common.LogOptionalFieldsSettings;
-import com.sap.hcp.cf.logging.common.LongValue;
-import com.sap.hcp.cf.logging.common.Markers;
import com.sap.hcp.cf.logging.common.RequestRecord;
import com.sap.hcp.cf.logging.servlet.dynlog.DynLogEnvironment;
import com.sap.hcp.cf.logging.servlet.dynlog.DynamicLogLevelProcessor;
@@ -34,163 +30,143 @@
*/
public class RequestLoggingFilter implements Filter {
- private static final Logger LOGGER = LoggerFactory.getLogger(RequestLoggingFilter.class);
- public static final String LOG_PROVIDER = "[SERVLET]";
- public static final String WRAP_RESPONSE_INIT_PARAM = "wrapResponse";
- public static final String WRAP_REQUEST_INIT_PARAM = "wrapRequest";
-
- private boolean wrapResponse = true;
- private boolean wrapRequest = true;
- private DynLogEnvironment dynLogEnvironment;
- private DynamicLogLevelProcessor dynamicLogLevelProcessor;
- protected LogOptionalFieldsSettings logOptionalFieldsSettings;
-
- public RequestLoggingFilter() {
- String invokingClass = this.getClass().getName().toString();
- logOptionalFieldsSettings = new LogOptionalFieldsSettings(invokingClass);
- dynLogEnvironment = new DynLogEnvironment();
- if (dynLogEnvironment.getRsaPublicKey() != null) {
- dynamicLogLevelProcessor = new DynamicLogLevelProcessor(dynLogEnvironment);
- }
- }
-
- @Override
- public void init(FilterConfig filterConfig) throws ServletException {
- String value = filterConfig.getInitParameter(WRAP_RESPONSE_INIT_PARAM);
- if (value != null && "false".equalsIgnoreCase(value)) {
- wrapResponse = false;
- }
- value = filterConfig.getInitParameter(WRAP_REQUEST_INIT_PARAM);
- if (value != null && "false".equalsIgnoreCase(value)) {
- wrapRequest = false;
- }
- }
-
- @Override
- public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
- ServletException {
- if (HttpServletRequest.class.isAssignableFrom(request.getClass()) && HttpServletResponse.class.isAssignableFrom(
- response.getClass())) {
- doFilterRequest((HttpServletRequest) request, (HttpServletResponse) response, chain);
- } else {
- chain.doFilter(request, response);
- }
- }
-
- @Override
- public void destroy() {
- MDC.clear();
- }
-
- private void doFilterRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain)
- throws IOException,
- ServletException {
- if (httpRequest.getHeader(dynLogEnvironment.getDynLogHeaderKey()) != null && dynamicLogLevelProcessor != null) {
- dynamicLogLevelProcessor.copyDynamicLogLevelToMDC(httpRequest);
- }
- /*
- * -- make sure correlation id is read from headers
- */
- LogContext.initializeContext(getCorrelationIdFromHeader(httpRequest));
-
- RequestRecord rr = null;
- try {
- rr = new RequestRecord(LOG_PROVIDER);
- ContentLengthTrackingResponseWrapper responseWrapper = null;
- ContentLengthTrackingRequestWrapper requestWrapper = null;
-
- /*
- * -- we essentially do three things here: -- a) we create a log
- * record using our library and log it via STDOUT -- b) keep track
- * of certain header fields so that they are available in later
- * processing steps -- b) inject a response wrapper to keep track of
- * content length (hopefully)
- */
- if (wrapResponse) {
- responseWrapper = new ContentLengthTrackingResponseWrapper(httpResponse);
- }
- if (wrapRequest) {
-
- requestWrapper = new ContentLengthTrackingRequestWrapper(httpRequest);
- }
-
- addHeaders(httpRequest, rr);
-
- /* -- start measuring right before calling up the filter chain -- */
- rr.start();
- if (chain != null) {
- chain.doFilter(requestWrapper != null ? requestWrapper : httpRequest, responseWrapper != null
- ? responseWrapper
- : httpResponse);
- }
- rr.stop();
-
- if (requestWrapper != null) {
- rr.addValue(Fields.REQUEST_SIZE_B, new LongValue(requestWrapper.getContentLength()));
- } else {
- rr.addValue(Fields.REQUEST_SIZE_B, new LongValue(httpRequest.getContentLength()));
- }
- String headerValue = httpResponse.getHeader(HttpHeaders.CONTENT_LENGTH);
- if (headerValue != null) {
- rr.addValue(Fields.RESPONSE_SIZE_B, new LongValue(Long.valueOf(headerValue)));
- } else {
- if (responseWrapper != null) {
- rr.addValue(Fields.RESPONSE_SIZE_B, new LongValue(responseWrapper.getContentLength()));
- }
- }
- rr.addTag(Fields.RESPONSE_CONTENT_TYPE, getValue(httpResponse.getHeader(HttpHeaders.CONTENT_TYPE)));
- rr.addValue(Fields.RESPONSE_STATUS, new LongValue(httpResponse.getStatus()));
- /*
- * -- log info
- */
- LOGGER.info(Markers.REQUEST_MARKER, "{}", rr);
- /*
- * -- close this
- */
- } finally {
- rr.close();
-
- if (dynamicLogLevelProcessor != null) {
- dynamicLogLevelProcessor.removeDynamicLogLevelFromMDC();
- }
- }
- }
-
- private String getCorrelationIdFromHeader(HttpServletRequest httpRequest) {
- String cId = httpRequest.getHeader(HttpHeaders.CORRELATION_ID);
- if (cId == null || cId.length() == 0) {
- cId = httpRequest.getHeader(HttpHeaders.X_VCAP_REQUEST_ID);
- }
- return cId;
- }
-
- private String getHeader(HttpServletRequest request, String headerName) {
- return getValue(request.getHeader(headerName));
- }
-
- private String getValue(String value) {
- return value != null ? value : Defaults.UNKNOWN;
- }
-
- private void addHeaders(HttpServletRequest request, RequestRecord lrec) {
- lrec.addTag(Fields.REQUEST, request.getQueryString() != null ? request.getRequestURI() + "?" + request
- .getQueryString()
- : request.getRequestURI());
- lrec.addTag(Fields.METHOD, request.getMethod());
- lrec.addTag(Fields.PROTOCOL, getValue(request.getProtocol()));
-
- boolean isSensitiveConnectionData = logOptionalFieldsSettings.isLogSensitiveConnectionData();
-
- to(lrec).addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_IP, getValue(request.getRemoteAddr()))
- .addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_HOST, getValue(request.getRemoteHost()))
- .addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_PORT, Integer.toString(request
- .getRemotePort()))
- .addOptionalTag(isSensitiveConnectionData, Fields.X_FORWARDED_FOR, getHeader(request,
- HttpHeaders.X_FORWARDED_FOR))
- .addOptionalTag(logOptionalFieldsSettings.isLogRemoteUserField(), Fields.REMOTE_USER, getValue(request
- .getRemoteUser()))
- .addOptionalTag(logOptionalFieldsSettings.isLogRefererField(), Fields.REFERER, getHeader(request,
- HttpHeaders.REFERER));
- lrec.addContextTag(Fields.REQUEST_ID, getHeader(request, HttpHeaders.X_VCAP_REQUEST_ID));
- }
+ public static final String LOG_PROVIDER = "[SERVLET]";
+ public static final String WRAP_RESPONSE_INIT_PARAM = "wrapResponse";
+ public static final String WRAP_REQUEST_INIT_PARAM = "wrapRequest";
+
+ private boolean wrapResponse = true;
+ private boolean wrapRequest = true;
+ private DynLogEnvironment dynLogEnvironment;
+ private DynamicLogLevelProcessor dynamicLogLevelProcessor;
+ protected LogOptionalFieldsSettings logOptionalFieldsSettings;
+
+ public RequestLoggingFilter() {
+ String invokingClass = this.getClass().getName().toString();
+ logOptionalFieldsSettings = new LogOptionalFieldsSettings(invokingClass);
+ dynLogEnvironment = new DynLogEnvironment();
+ if (dynLogEnvironment.getRsaPublicKey() != null) {
+ dynamicLogLevelProcessor = new DynamicLogLevelProcessor(dynLogEnvironment);
+ }
+ }
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ String value = filterConfig.getInitParameter(WRAP_RESPONSE_INIT_PARAM);
+ if (value != null && "false".equalsIgnoreCase(value)) {
+ wrapResponse = false;
+ }
+ value = filterConfig.getInitParameter(WRAP_REQUEST_INIT_PARAM);
+ if (value != null && "false".equalsIgnoreCase(value)) {
+ wrapRequest = false;
+ }
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ if (HttpServletRequest.class.isAssignableFrom(request.getClass())
+ && HttpServletResponse.class.isAssignableFrom(response.getClass())) {
+ doFilterRequest((HttpServletRequest) request, (HttpServletResponse) response, chain);
+ } else {
+ chain.doFilter(request, response);
+ }
+ }
+
+ @Override
+ public void destroy() {
+ MDC.clear();
+ }
+
+ private void doFilterRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain)
+ throws IOException, ServletException {
+ if (httpRequest.getHeader(dynLogEnvironment.getDynLogHeaderKey()) != null && dynamicLogLevelProcessor != null) {
+ dynamicLogLevelProcessor.copyDynamicLogLevelToMDC(httpRequest);
+ }
+ /*
+ * -- make sure correlation id is read from headers
+ */
+ LogContext.initializeContext(getCorrelationIdFromHeader(httpRequest));
+
+ try {
+ RequestRecord rr = new RequestRecord(LOG_PROVIDER);
+ ContentLengthTrackingResponseWrapper responseWrapper = null;
+ ContentLengthTrackingRequestWrapper requestWrapper = null;
+
+ /*
+ * -- we essentially do three things here: -- a) we create a log
+ * record using our library and log it via STDOUT -- b) keep track
+ * of certain header fields so that they are available in later
+ * processing steps -- b) inject a response wrapper to keep track of
+ * content length (hopefully)
+ */
+ if (wrapResponse) {
+ responseWrapper = new ContentLengthTrackingResponseWrapper(httpResponse);
+ }
+
+ RequestLoggingVisitor loggingVisitor = new RequestLoggingVisitor(rr, responseWrapper);
+
+ if (wrapRequest) {
+ httpRequest = new LoggingContextRequestWrapper(httpRequest, loggingVisitor);
+ httpRequest = new ContentLengthTrackingRequestWrapper(httpRequest);
+ }
+
+ addHeaders(httpRequest, rr);
+ httpRequest.setAttribute(MDC.class.getName(), MDC.getCopyOfContextMap());
+
+
+ /* -- start measuring right before calling up the filter chain -- */
+ rr.start();
+ if (chain != null) {
+ chain.doFilter(httpRequest, httpResponse);
+ }
+
+ if (!httpRequest.isAsyncStarted()) {
+ loggingVisitor.logRequest(httpRequest, httpResponse);
+ }
+ /*
+ * -- close this
+ */
+ } finally {
+ if (dynamicLogLevelProcessor != null) {
+ dynamicLogLevelProcessor.removeDynamicLogLevelFromMDC();
+ }
+ }
+ }
+
+ private String getCorrelationIdFromHeader(HttpServletRequest httpRequest) {
+ String cId = httpRequest.getHeader(HttpHeaders.CORRELATION_ID);
+ if (cId == null || cId.length() == 0) {
+ cId = httpRequest.getHeader(HttpHeaders.X_VCAP_REQUEST_ID);
+ }
+ return cId;
+ }
+
+ private String getHeader(HttpServletRequest request, String headerName) {
+ return getValue(request.getHeader(headerName));
+ }
+
+ private String getValue(String value) {
+ return value != null ? value : Defaults.UNKNOWN;
+ }
+
+ private void addHeaders(HttpServletRequest request, RequestRecord lrec) {
+ lrec.addTag(Fields.REQUEST, request.getQueryString() != null
+ ? request.getRequestURI() + "?" + request.getQueryString() : request.getRequestURI());
+ lrec.addTag(Fields.METHOD, request.getMethod());
+ lrec.addTag(Fields.PROTOCOL, getValue(request.getProtocol()));
+
+ boolean isSensitiveConnectionData = logOptionalFieldsSettings.isLogSensitiveConnectionData();
+
+ to(lrec).addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_IP, getValue(request.getRemoteAddr()))
+ .addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_HOST, getValue(request.getRemoteHost()))
+ .addOptionalTag(isSensitiveConnectionData, Fields.REMOTE_PORT,
+ Integer.toString(request.getRemotePort()))
+ .addOptionalTag(isSensitiveConnectionData, Fields.X_FORWARDED_FOR,
+ getHeader(request, HttpHeaders.X_FORWARDED_FOR))
+ .addOptionalTag(logOptionalFieldsSettings.isLogRemoteUserField(), Fields.REMOTE_USER,
+ getValue(request.getRemoteUser()))
+ .addOptionalTag(logOptionalFieldsSettings.isLogRefererField(), Fields.REFERER,
+ getHeader(request, HttpHeaders.REFERER));
+ lrec.addContextTag(Fields.REQUEST_ID, getHeader(request, HttpHeaders.X_VCAP_REQUEST_ID));
+ }
}
diff --git a/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitor.java b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitor.java
new file mode 100644
index 00000000..123d896d
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/main/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitor.java
@@ -0,0 +1,50 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.sap.hcp.cf.logging.common.Defaults;
+import com.sap.hcp.cf.logging.common.Fields;
+import com.sap.hcp.cf.logging.common.HttpHeaders;
+import com.sap.hcp.cf.logging.common.LongValue;
+import com.sap.hcp.cf.logging.common.Markers;
+import com.sap.hcp.cf.logging.common.RequestRecord;
+
+public class RequestLoggingVisitor {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RequestLoggingVisitor.class);
+
+ private ContentLengthTrackingResponseWrapper responseWrapper;
+ private RequestRecord requestRecord;
+
+ public RequestLoggingVisitor(RequestRecord requestRecord, ContentLengthTrackingResponseWrapper responseWrapper) {
+ this.requestRecord = requestRecord;
+ this.responseWrapper = responseWrapper;
+ }
+
+ public void logRequest(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
+ requestRecord.stop();
+ requestRecord.addValue(Fields.REQUEST_SIZE_B, new LongValue(httpRequest.getContentLength()));
+ String headerValue = httpResponse.getHeader(HttpHeaders.CONTENT_LENGTH);
+ if (headerValue != null) {
+ requestRecord.addValue(Fields.RESPONSE_SIZE_B, new LongValue(Long.valueOf(headerValue)));
+ } else {
+ if (responseWrapper != null) {
+ requestRecord.addValue(Fields.RESPONSE_SIZE_B, new LongValue(responseWrapper.getContentLength()));
+ }
+ }
+ requestRecord.addTag(Fields.RESPONSE_CONTENT_TYPE, getValue(httpResponse.getHeader(HttpHeaders.CONTENT_TYPE)));
+ requestRecord.addValue(Fields.RESPONSE_STATUS, new LongValue(httpResponse.getStatus()));
+
+ LOG.info(Markers.REQUEST_MARKER, "{}", requestRecord);
+
+ }
+
+ private String getValue(String value) {
+ return value != null ? value : Defaults.UNKNOWN;
+ }
+
+}
diff --git a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImplTest.java b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImplTest.java
new file mode 100644
index 00000000..3cf7453b
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingAsyncContextImplTest.java
@@ -0,0 +1,170 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import static java.util.Collections.emptyMap;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.hamcrest.Matchers.both;
+import static org.hamcrest.Matchers.empty;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.AsyncEvent;
+import javax.servlet.AsyncListener;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.mockito.stubbing.Answer;
+import org.slf4j.MDC;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LoggingAsyncContextImplTest {
+
+ @Mock
+ private AsyncContext wrappedContext;
+
+ @Mock
+ private RequestLoggingVisitor loggingVisitor;
+
+ @Mock
+ private HttpServletRequest request;
+
+ @Mock
+ private HttpServletResponse response;
+
+ private ExecutorService executor = newSingleThreadExecutor();
+
+ @Captor
+ private ArgumentCaptor asyncListener;
+
+ @InjectMocks
+ private LoggingAsyncContextImpl testedContext;
+
+ @Before
+ public void initWrappedContext() {
+ when(wrappedContext.getRequest()).thenReturn(request);
+ when(wrappedContext.getResponse()).thenReturn(response);
+ verify(wrappedContext).addListener(asyncListener.capture());
+ doAnswer(new Answer() {
+
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ Runnable runnable = (Runnable) invocation.getArguments()[0];
+ Future> future = executor.submit(runnable);
+ future.get();
+ return null;
+ }
+ }).when(wrappedContext).start(any(Runnable.class));
+ }
+
+ @Test
+ public void hasEmptyMDCWhenNoMapInRequest() throws Exception {
+ Map contextMap = new HashMap<>();
+ testedContext.start(putAllContextMap(contextMap));
+ assertThat(contextMap.entrySet(), is(empty()));
+ }
+
+ private Runnable putAllContextMap(Map contextMap) {
+ return new Runnable() {
+
+ @Override
+ public void run() {
+ contextMap.putAll(MDC.getCopyOfContextMap());
+ }
+ };
+ }
+
+ @Test
+ public void importsMDCEntriesFromRequest() throws Exception {
+ Map mdcAttributes = new HashMap<>();
+ mdcAttributes.put("this-key", "this-value");
+ mdcAttributes.put("that-key", "that-value");
+ when(request.getAttribute(MDC.class.getName())).thenReturn(mdcAttributes);
+ Map contextMap = new HashMap<>();
+ testedContext.start(putAllContextMap(contextMap));
+ assertThat(contextMap, both(hasEntry("that-key", "that-value")).and(hasEntry("this-key", "this-value")));
+ }
+
+ @Test
+ public void resetsMDCEntriesBetweenConsequtiveRuns() throws Exception {
+ Map mdcAttributes = new HashMap<>();
+ mdcAttributes.put("this-key", "this-value");
+ mdcAttributes.put("that-key", "that-value");
+ when(request.getAttribute(MDC.class.getName())).thenReturn(mdcAttributes);
+ Map firstContextMap = new HashMap<>();
+ testedContext.start(putAllContextMap(firstContextMap));
+ reset(request);
+ when(request.getAttribute(MDC.class.getName())).thenReturn(emptyMap());
+ Map secondContextMap = new HashMap<>();
+ testedContext.start(putAllContextMap(secondContextMap));
+
+ assertThat(firstContextMap.entrySet(), is(not(empty())));
+ assertThat(secondContextMap.entrySet(), is(empty()));
+ }
+
+ @Test
+ public void savesAndRestoresThreadMDC() throws Exception {
+ executor.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ MDC.put("initial-key", "initial-value");
+ }
+ });
+ Map requestContextMap = new HashMap<>();
+ testedContext.start(putAllContextMap(requestContextMap));
+ Map finalContextMap = new HashMap<>();
+ executor.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ finalContextMap.putAll(MDC.getCopyOfContextMap());
+ }
+ });
+
+ assertThat(requestContextMap.entrySet(), is(empty()));
+ assertThat(finalContextMap, hasEntry("initial-key", "initial-value"));
+ }
+
+ @Test
+ public void writesRequestLogWithMDCEntries() throws Exception {
+ Map mdcAttributes = new HashMap<>();
+ mdcAttributes.put("this-key", "this-value");
+ mdcAttributes.put("that-key", "that-value");
+ when(request.getAttribute(MDC.class.getName())).thenReturn(mdcAttributes);
+ Map contextMap = new HashMap<>();
+ doAnswer(new Answer() {
+
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ contextMap.putAll(MDC.getCopyOfContextMap());
+ return null;
+ }
+ }).when(loggingVisitor).logRequest(any(HttpServletRequest.class), any(HttpServletResponse.class));
+ asyncListener.getValue().onComplete(mock(AsyncEvent.class));
+
+ assertThat(contextMap, both(hasEntry("that-key", "that-value")).and(hasEntry("this-key", "this-value")));
+
+ }
+}
diff --git a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapperTest.java b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapperTest.java
new file mode 100644
index 00000000..25e5a48a
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/LoggingContextRequestWrapperTest.java
@@ -0,0 +1,40 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LoggingContextRequestWrapperTest {
+
+ @Mock
+ private HttpServletRequest request;
+
+ @Mock
+ private HttpServletResponse response;
+
+ @Test
+ public void wrapsAsyncContext() throws Exception {
+ when(request.startAsync()).thenReturn(mock(AsyncContext.class));
+ LoggingContextRequestWrapper wrapper = new LoggingContextRequestWrapper(request, null);
+ assertThat(wrapper.startAsync(), instanceOf(LoggingAsyncContextImpl.class));
+ }
+
+ @Test
+ public void wrapsAsyncContextWithRequestResponseParameters() throws Exception {
+ when(request.startAsync(request, response)).thenReturn(mock(AsyncContext.class));
+ LoggingContextRequestWrapper wrapper = new LoggingContextRequestWrapper(request, null);
+ assertThat(wrapper.startAsync(request, response), instanceOf(LoggingAsyncContextImpl.class));
+ }
+
+}
diff --git a/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitorTest.java b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitorTest.java
new file mode 100644
index 00000000..e00cec28
--- /dev/null
+++ b/cf-java-logging-support-servlet/src/test/java/com/sap/hcp/cf/logging/servlet/filter/RequestLoggingVisitorTest.java
@@ -0,0 +1,93 @@
+package com.sap.hcp.cf.logging.servlet.filter;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import com.sap.hcp.cf.logging.common.Fields;
+import com.sap.hcp.cf.logging.common.HttpHeaders;
+import com.sap.hcp.cf.logging.common.RequestRecord;
+import com.sap.hcp.cf.logging.common.Value;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RequestLoggingVisitorTest {
+
+ @Mock
+ private ContentLengthTrackingResponseWrapper responseWrapper;
+
+ @Mock
+ private RequestRecord requestRecord;
+
+ @Mock
+ private HttpServletRequest httpRequest;
+
+ @Mock
+ private HttpServletResponse httpResponse;
+
+ @InjectMocks
+ private RequestLoggingVisitor visitor;
+
+ @Captor
+ private ArgumentCaptor valueCaptor;
+
+ @Test
+ public void stopsRequestRecord() throws Exception {
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).stop();
+ }
+
+ @Test
+ public void addsHttpStatusAsValue() throws Exception {
+ when(httpResponse.getStatus()).thenReturn(123);
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).addValue(eq(Fields.RESPONSE_STATUS), valueCaptor.capture());
+ assertThat(valueCaptor.getValue().asLong(), is(123L));
+ }
+
+ @Test
+ public void addsResponseContentTypeAsTag() throws Exception {
+ when(httpResponse.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn("application/vnd.test");
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).addTag(Fields.RESPONSE_CONTENT_TYPE, "application/vnd.test");
+ }
+
+ @Test
+ public void addsRequestContentLengthAsValue() throws Exception {
+ when(httpRequest.getContentLength()).thenReturn(12345);
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).addValue(eq(Fields.REQUEST_SIZE_B), valueCaptor.capture());
+ assertThat(valueCaptor.getValue().asLong(), is(12345L));
+ }
+
+ @Test
+ public void addsResponseContentLengthAsValueFromHeaderIfAvailable() throws Exception {
+ when(httpResponse.getHeader(HttpHeaders.CONTENT_LENGTH)).thenReturn("1234");
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).addValue(eq(Fields.RESPONSE_SIZE_B), valueCaptor.capture());
+ verifyZeroInteractions(responseWrapper);
+ assertThat(valueCaptor.getValue().asLong(), is(1234L));
+ }
+
+ @Test
+ public void addsResponseContentLengthAsValueFromWrapperAsFAllback() throws Exception {
+ when(responseWrapper.getContentLength()).thenReturn(1234L);
+ visitor.logRequest(httpRequest, httpResponse);
+ verify(requestRecord).addValue(eq(Fields.RESPONSE_SIZE_B), valueCaptor.capture());
+ assertThat(valueCaptor.getValue().asLong(), is(1234L));
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index db18bce2..6fff03c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
pom
Cloud Foundry Java logging support components
diff --git a/sample/pom.xml b/sample/pom.xml
index 33f34504..6c13dfaa 100644
--- a/sample/pom.xml
+++ b/sample/pom.xml
@@ -6,7 +6,7 @@
com.sap.hcp.cf.logging
cf-java-logging-support-parent
- 2.1.5
+ 2.2.0
../pom.xml
@@ -18,7 +18,7 @@
1.7.12
2.22.2
2.0.1
- 2.1.5
+ 2.2.0