Minor improvements to logging for servlets 19/59219/4
authorvempo <vitaliy.emporopulo@amdocs.com>
Mon, 6 Aug 2018 13:21:01 +0000 (16:21 +0300)
committerAvi Gaffa <avi.gaffa@amdocs.com>
Mon, 6 Aug 2018 16:09:04 +0000 (16:09 +0000)
Fixed import order, more resilient statuses for Spring,
cleaner code and tests, more convenient APIs.

Change-Id: I6493c81d9c3c1c543c354562e074876268794438
Issue-ID: SDC-1580
Signed-off-by: vempo <vitaliy.emporopulo@amdocs.com>
16 files changed:
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/CombinedTracker.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/HttpHeader.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/main/java/org/openecomp/sdc/logging/servlet/jaxrs/LoggingRequestFilter.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/CombinedTrackerTest.java [new file with mode: 0644]
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/ContextTrackerTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/HttpHeaderTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/logback/DispatchingAppender.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/GlobalContextProvider.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/MDCDelegate.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/Markers.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/RequestContextProvider.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapper.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/main/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggingServiceProvider.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-core/src/test/java/org/openecomp/sdc/logging/slf4j/SLF4JLoggerWrapperTest.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-spring/src/main/java/org/openecomp/sdc/logging/servlet/spring/LoggingInterceptor.java
openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-spring/src/test/java/org/openecomp/sdc/logging/servlet/spring/LoggingInterceptorTest.java

index 2dd2c12..5d940aa 100644 (file)
 package org.openecomp.sdc.logging.servlet;
 
 import javax.servlet.http.HttpServletRequest;
-import org.openecomp.sdc.logging.api.Logger;
 
 /**
- * Tracker for all the elements of ONAP logging and tracing at an entry point to an application - context and audit.
- * The order of invocations is important, assuming the context must be kept as long as audit hasn't been finished.
+ * Tracker for all the elements of ONAP logging and tracing at an entry point to an application.
+ * The order of invocations is important, and on {@link #preRequest(HttpServletRequest)} it respects the order of
+ * trackers passed to the constructor. On {@link #postRequest(RequestProcessingResult)}, the invocation will be in the
+ * <b>reverse</b> order.
  *
  * @author evitaliy
  * @since 01 Aug 2018
  */
 public class CombinedTracker implements Tracker {
 
-    private final ContextTracker context;
-    private final AuditTracker audit;
+    private final Tracker[] trackers;
 
-    public CombinedTracker(Logger logger, HttpHeader partnerNameHeader, HttpHeader requestIdHeader) {
-        this.context = new ContextTracker(partnerNameHeader, requestIdHeader);
-        this.audit = new AuditTracker(logger);
-    }
-
-    public CombinedTracker(Class<?> resourceType, HttpHeader partnerNameHeader, HttpHeader requestIdHeader) {
-        this.context = new ContextTracker(partnerNameHeader, requestIdHeader);
-        this.audit = new AuditTracker(resourceType);
+    public CombinedTracker(Tracker... trackers) {
+        this.trackers = new Tracker[trackers.length];
+        System.arraycopy(trackers, 0, this.trackers, 0, trackers.length);
     }
 
     @Override
     public void preRequest(HttpServletRequest request) {
-        this.context.preRequest(request);
-        this.audit.preRequest(request);
+
+        for (Tracker t : trackers) {
+            t.preRequest(request);
+        }
     }
 
     @Override
     public void postRequest(RequestProcessingResult result) {
-        this.audit.postRequest(result);
-        this.context.postRequest(result);
+
+        for (int i = trackers.length - 1; i > -1; i--) {
+            trackers[i].postRequest(result);
+        }
     }
 }
index db10c2e..dd5af85 100644 (file)
@@ -43,7 +43,7 @@ public class HttpHeader {
      *
      * @param headerNames cannot be null or empty
      */
-    public HttpHeader(String[] headerNames) {
+    public HttpHeader(String... headerNames) {
 
         if (Objects.requireNonNull(headerNames, NAMES_CANNOT_BE_NULL).length < 1) {
             throw new IllegalArgumentException(AT_LEAST_ONE_NAME_REQUIRED);
index f846359..fb65149 100644 (file)
@@ -27,7 +27,9 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.ext.Provider;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.api.LoggerFactory;
+import org.openecomp.sdc.logging.servlet.AuditTracker;
 import org.openecomp.sdc.logging.servlet.CombinedTracker;
+import org.openecomp.sdc.logging.servlet.ContextTracker;
 import org.openecomp.sdc.logging.servlet.HttpHeader;
 import org.openecomp.sdc.logging.servlet.Tracker;
 
@@ -63,8 +65,8 @@ public class LoggingRequestFilter implements ContainerRequestFilter {
 
     private HttpServletRequest httpRequest;
 
-    private HttpHeader requestIdHeader = new HttpHeader(new String[] {DEFAULT_REQUEST_ID_HEADER});
-    private HttpHeader partnerNameHeader = new HttpHeader(new String[] {DEFAULT_PARTNER_NAME_HEADER});
+    private HttpHeader requestIdHeader = new HttpHeader(DEFAULT_REQUEST_ID_HEADER);
+    private HttpHeader partnerNameHeader = new HttpHeader(DEFAULT_PARTNER_NAME_HEADER);
 
     private ResourceInfo resource;
 
@@ -107,7 +109,9 @@ public class LoggingRequestFilter implements ContainerRequestFilter {
     @Override
     public void filter(ContainerRequestContext requestContext) {
         Class<?> resourceClass = resource.getResourceMethod().getDeclaringClass();
-        Tracker tracker = new CombinedTracker(resourceClass, partnerNameHeader, requestIdHeader);
+        Tracker tracker = new CombinedTracker(
+                new ContextTracker(partnerNameHeader, requestIdHeader),
+                new AuditTracker(resourceClass));
         requestContext.setProperty(LOGGING_TRACKER_KEY, tracker);
         tracker.preRequest(httpRequest);
     }
diff --git a/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/CombinedTrackerTest.java b/openecomp-be/lib/openecomp-sdc-logging-lib/openecomp-sdc-logging-api/src/test/java/org/openecomp/sdc/logging/servlet/CombinedTrackerTest.java
new file mode 100644 (file)
index 0000000..4454493
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import javax.servlet.http.HttpServletRequest;
+import org.junit.Test;
+
+/**
+ * Test the construction and invocation of combined tracker.
+ *
+ * @author evitaliy
+ * @since 01 Aug 2018
+ */
+public class CombinedTrackerTest {
+
+    @Test(expected = NullPointerException.class)
+    public void throwExceptionWhenTrackersNull() {
+        new CombinedTracker((Tracker[]) null);
+    }
+
+    @Test
+    public void trackersCalledWhenPreRequest() {
+        Tracker firstTracker = mock(Tracker.class);
+        Tracker secondTracker = mock(Tracker.class);
+        CombinedTracker tracker = new CombinedTracker(firstTracker, secondTracker);
+        tracker.preRequest(mock(HttpServletRequest.class));
+        verify(firstTracker, times(1)).preRequest(any(HttpServletRequest.class));
+        verify(secondTracker, times(1)).preRequest(any(HttpServletRequest.class));
+    }
+
+    @Test
+    public void trackersCalledWhenPostRequest() {
+        Tracker firstTracker = mock(Tracker.class);
+        Tracker secondTracker = mock(Tracker.class);
+        CombinedTracker tracker = new CombinedTracker(firstTracker, secondTracker);
+        tracker.postRequest(mock(RequestProcessingResult.class));
+        verify(firstTracker, times(1)).postRequest(any(RequestProcessingResult.class));
+        verify(secondTracker, times(1)).postRequest(any(RequestProcessingResult.class));
+    }
+}
\ No newline at end of file
index a247d11..59c690d 100644 (file)
@@ -45,10 +45,10 @@ import org.powermock.modules.junit4.PowerMockRunner;
 public class ContextTrackerTest {
 
     private static final String X_REQUEST_ID = "X-REQUEST-ID";
-    private static final HttpHeader REQUEST_ID_HEADER = new HttpHeader(new String[] {X_REQUEST_ID});
+    private static final HttpHeader REQUEST_ID_HEADER = new HttpHeader(X_REQUEST_ID);
 
     private static final String X_PARTNER_NAME = "X-PARTNER-NAME";
-    private static final HttpHeader PARTNER_NAME_HEADER = new HttpHeader(new String[] {X_PARTNER_NAME});
+    private static final HttpHeader PARTNER_NAME_HEADER = new HttpHeader(X_PARTNER_NAME);
 
     @Test(expected = NullPointerException.class)
     public void throwExceptionWhenPartnerNamesNull() {
index 5cbd0c0..2effa9f 100644 (file)
@@ -49,7 +49,7 @@ public class HttpHeaderTest {
 
     @Test(expected = IllegalArgumentException.class)
     public void throwExceptionWhenInputArrayEmpty() {
-        new HttpHeader(new String[0]);
+        new HttpHeader();
     }
 
     @Test(expected = IllegalArgumentException.class)
@@ -59,7 +59,7 @@ public class HttpHeaderTest {
 
     @Test
     public void valueNotReturnedWhenNameInArrayNotRequested() {
-        HttpHeader header = new HttpHeader(new String[] {"A"});
+        HttpHeader header = new HttpHeader("A");
         assertFalse(header.getAny(NULL_WHEN_NAME_NOT_B).isPresent());
     }
 
@@ -71,7 +71,7 @@ public class HttpHeaderTest {
 
     @Test
     public void valueReturnedWhenSinglePossibleHeaderInArrayMatches() {
-        HttpHeader header = new HttpHeader(new String[] {"B"});
+        HttpHeader header = new HttpHeader("B");
         assertTrue(header.getAny(NULL_WHEN_NAME_NOT_B).isPresent());
     }
 
@@ -83,7 +83,7 @@ public class HttpHeaderTest {
 
     @Test
     public void valueReturnedWhenLastHeaderInArrayMatches() throws Throwable {
-        HttpHeader header = new HttpHeader(new String[] {"A", "B"});
+        HttpHeader header = new HttpHeader("A", "B");
         header.getAny(NULL_WHEN_NAME_NOT_B).orElseThrow(VALUE_EXPECTED);
     }
 
@@ -95,7 +95,7 @@ public class HttpHeaderTest {
 
     @Test
     public void valueReturnedWhenFirstHeaderInArrayMatches() throws Throwable {
-        HttpHeader header = new HttpHeader(new String[] {"B", "A"});
+        HttpHeader header = new HttpHeader("B", "A");
         header.getAny(NULL_WHEN_NAME_NOT_B).orElseThrow(VALUE_EXPECTED);
     }
 
@@ -107,7 +107,7 @@ public class HttpHeaderTest {
 
     @Test
     public void valueReturnedWhenMiddleHeaderInArrayMatches() throws Throwable {
-        HttpHeader header = new HttpHeader(new String[] {"A", "B", "C"});
+        HttpHeader header = new HttpHeader("A", "B", "C");
         header.getAny(NULL_WHEN_NAME_NOT_B).orElseThrow(VALUE_EXPECTED);
     }
 
index b4abb8b..fb0ceb9 100644 (file)
@@ -24,11 +24,10 @@ import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.AppenderBase;
 import ch.qos.logback.core.joran.spi.DefaultClass;
 import ch.qos.logback.core.sift.Discriminator;
-import org.slf4j.LoggerFactory;
-
 import java.util.Iterator;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import org.slf4j.LoggerFactory;
 
 /**
  * <p>Allows to use EELF logging configuration almost as is, by using a custom routing function, but pre-configured
@@ -55,7 +54,7 @@ public class DispatchingAppender extends AppenderBase<ILoggingEvent> {
     // "magic" appender to indicate a missing appender
     private static final Appender<ILoggingEvent> NO_APPENDER = new DispatchingAppender();
 
-    private Map<String, Appender<ILoggingEvent>> appenders = new ConcurrentHashMap<>();
+    private final Map<String, Appender<ILoggingEvent>> appenders = new ConcurrentHashMap<>();
 
     private Discriminator<ILoggingEvent> discriminator;
     private String appenderNamePattern;
index 3370ebf..f6e933d 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
-import org.openecomp.sdc.logging.context.HostAddressCache;
-import org.openecomp.sdc.logging.context.InstanceId;
-
 import java.net.InetAddress;
 import java.util.EnumMap;
 import java.util.Map;
 import java.util.Optional;
+import org.openecomp.sdc.logging.context.HostAddressCache;
+import org.openecomp.sdc.logging.context.InstanceId;
 
 /**
  * Maps global logging context to corresponding MDC fields.
index 7d2d846..17439da 100644 (file)
@@ -41,10 +41,10 @@ import org.slf4j.MarkerFactory;
  */
 public class Markers {
 
+    public static final Marker AUDIT = MarkerFactory.getMarker("AUDIT");
+    public static final Marker METRICS = MarkerFactory.getMarker("METRICS");
+
     private Markers() {
         // prevent instantiation
     }
-
-    public static final Marker AUDIT = MarkerFactory.getMarker("AUDIT");
-    public static final Marker METRICS = MarkerFactory.getMarker("METRICS");
 }
index 248ad43..fbb5fbf 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
+import java.text.SimpleDateFormat;
 import org.openecomp.sdc.logging.api.AuditData;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.api.MetricsData;
 import org.slf4j.LoggerFactory;
 import org.slf4j.MDC;
 
-import java.text.SimpleDateFormat;
-
 /**
  * Delegates log calls to SLF4J API and MDC.
  *
index c187168..9d4d507 100644 (file)
 
 package org.openecomp.sdc.logging.slf4j;
 
+import java.util.Objects;
+import java.util.concurrent.Callable;
 import org.openecomp.sdc.logging.api.ContextData;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.spi.LoggingServiceProvider;
 
-import java.util.Objects;
-import java.util.concurrent.Callable;
-
 /**
  * Uses SLF4J as backend for logging service.
  *
index 73dd25c..f2f0c1d 100644 (file)
@@ -265,8 +265,8 @@ public class SLF4JLoggerWrapperTest {
         // build a dynamic proxy to avoid implementing the long list of Logger methods
         // when we actually need just Logger.info() with the audit marker
         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-        return SpyLogger.class.cast(
-                Proxy.newProxyInstance(classLoader, new Class<?>[]{SpyLogger.class}, new SpyingInvocationHandler()));
+        return (SpyLogger) Proxy.newProxyInstance(classLoader, new Class<?>[] {SpyLogger.class},
+                new SpyingInvocationHandler());
     }
 
     private static class SpyingInvocationHandler implements InvocationHandler {
index a467a9e..cfcb62b 100644 (file)
@@ -18,6 +18,8 @@ package org.openecomp.sdc.logging.servlet.spring;
 
 import static org.openecomp.sdc.logging.api.StatusCode.COMPLETE;
 import static org.openecomp.sdc.logging.api.StatusCode.ERROR;
+import static org.springframework.http.HttpStatus.Series.REDIRECTION;
+import static org.springframework.http.HttpStatus.Series.SUCCESSFUL;
 
 import java.util.Objects;
 import javax.servlet.http.HttpServletRequest;
@@ -25,16 +27,19 @@ import javax.servlet.http.HttpServletResponse;
 import org.openecomp.sdc.logging.api.Logger;
 import org.openecomp.sdc.logging.api.LoggerFactory;
 import org.openecomp.sdc.logging.api.StatusCode;
+import org.openecomp.sdc.logging.servlet.AuditTracker;
 import org.openecomp.sdc.logging.servlet.CombinedTracker;
+import org.openecomp.sdc.logging.servlet.ContextTracker;
 import org.openecomp.sdc.logging.servlet.HttpHeader;
 import org.openecomp.sdc.logging.servlet.RequestProcessingResult;
 import org.openecomp.sdc.logging.servlet.Tracker;
 import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
 /**
- * <p>IMPORTANT: For this interceptor to work, all exceptions must be properly handled before being returned to a
+ * <p><b>IMPORTANT</b>: For this interceptor to work, all exceptions must be properly handled before being returned to a
  * client. Any unexpected, automatically handled exception bypasses the interceptor and will not be logged.</p>
  * <p>The interceptor must be either registered in Spring configuration XML as a bean, or programmatically as described
  * in <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-config-interceptors">
@@ -43,6 +48,7 @@ import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  * @author evitaliy
  * @since 02 Aug 2018
  */
+@Component
 public class LoggingInterceptor extends HandlerInterceptorAdapter {
 
     static final String LOGGING_TRACKER_KEY = "onap.logging.tracker";
@@ -60,7 +66,9 @@ public class LoggingInterceptor extends HandlerInterceptorAdapter {
     @Override
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
         Class<?> resourceClass = getResourceType(handler);
-        Tracker tracker = new CombinedTracker(resourceClass, partnerNameHeader, requestIdHeader);
+        Tracker tracker = new CombinedTracker(
+                new ContextTracker(partnerNameHeader, requestIdHeader),
+                new AuditTracker(resourceClass));
         request.setAttribute(LOGGING_TRACKER_KEY, tracker);
         tracker.preRequest(request);
         return true;
@@ -91,25 +99,63 @@ public class LoggingInterceptor extends HandlerInterceptorAdapter {
 
     static class ServletResponseResult implements RequestProcessingResult {
 
-        private final HttpStatus status;
+        private final StatusInfo statusInfo;
 
         ServletResponseResult(int status) {
-            this.status = HttpStatus.valueOf(status);
+            this.statusInfo = init(status);
+        }
+
+        private StatusInfo init(int status) {
+
+            try {
+                return new StatusInfo(HttpStatus.valueOf(status));
+            } catch (IllegalArgumentException e) {
+                return new StatusInfo(status, "Non-standard HTTP status", HttpStatus.Series.valueOf(status));
+            }
         }
 
         @Override
         public int getStatus() {
-            return status.value();
+            return statusInfo.getStatus();
         }
 
         @Override
         public StatusCode getStatusCode() {
-            return status.is2xxSuccessful() || status.is3xxRedirection() ? COMPLETE : ERROR;
+            return statusInfo.getStatusCode();
         }
 
         @Override
         public String getStatusPhrase() {
-            return status.getReasonPhrase();
+            return statusInfo.getReasonPhrase();
+        }
+    }
+
+    private static class StatusInfo {
+
+        private final int status;
+        private final String reasonPhrase;
+        private final HttpStatus.Series series;
+
+        private StatusInfo(HttpStatus httpStatus) {
+            this(httpStatus.value(), httpStatus.getReasonPhrase(), httpStatus.series());
+        }
+
+        private StatusInfo(int status, String reasonPhrase, HttpStatus.Series series) {
+            this.status = status;
+            this.reasonPhrase = reasonPhrase;
+            this.series = series;
+        }
+
+        private int getStatus() {
+            return status;
+        }
+
+        private String getReasonPhrase() {
+            return reasonPhrase;
+        }
+
+        private StatusCode getStatusCode() {
+            return series.equals(SUCCESSFUL) || series.equals(REDIRECTION) ? COMPLETE : ERROR;
         }
     }
 }
\ No newline at end of file
index ccd0b70..1aba519 100644 (file)
@@ -114,4 +114,44 @@ public class LoggingInterceptorTest {
         assertEquals(status, result.getStatus());
         assertEquals(COMPLETE, result.getStatusCode());
     }
+
+    @Test
+    public void errorStatusWhenNonStandardInformationalCode() {
+        final int status = 133;
+        LoggingInterceptor.ServletResponseResult result = new LoggingInterceptor.ServletResponseResult(status);
+        assertEquals(status, result.getStatus());
+        assertEquals(ERROR, result.getStatusCode());
+    }
+
+    @Test
+    public void errorStatusWhenNonStandardClientErrorCode() {
+        final int status = 485;
+        LoggingInterceptor.ServletResponseResult result = new LoggingInterceptor.ServletResponseResult(status);
+        assertEquals(status, result.getStatus());
+        assertEquals(ERROR, result.getStatusCode());
+    }
+
+    @Test
+    public void errorStatusWhenNonStandardServerErrorCode() {
+        final int status = 547;
+        LoggingInterceptor.ServletResponseResult result = new LoggingInterceptor.ServletResponseResult(status);
+        assertEquals(status, result.getStatus());
+        assertEquals(ERROR, result.getStatusCode());
+    }
+
+    @Test
+    public void completeStatusWhenNonStandardSuccessCode() {
+        final int status = 277;
+        LoggingInterceptor.ServletResponseResult result = new LoggingInterceptor.ServletResponseResult(status);
+        assertEquals(status, result.getStatus());
+        assertEquals(COMPLETE, result.getStatusCode());
+    }
+
+    @Test
+    public void completeStatusWhenNonStandardRedirectionCode() {
+        final int status = 364;
+        LoggingInterceptor.ServletResponseResult result = new LoggingInterceptor.ServletResponseResult(status);
+        assertEquals(status, result.getStatus());
+        assertEquals(COMPLETE, result.getStatusCode());
+    }
 }
\ No newline at end of file