Limit the length of logged responses in outgoingRequestLog
[vid.git] / vid-app-common / src / main / java / org / onap / vid / utils / Logging.java
index 0d8e588..ce811b4 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.vid.utils;
 
+import static java.util.Collections.emptyMap;
 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
 import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCause;
 import static org.apache.commons.lang3.exception.ExceptionUtils.getThrowableList;
@@ -33,8 +34,12 @@ import com.google.common.collect.ImmutableList;
 import io.joshworks.restclient.http.HttpResponse;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.UUID;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.core.Response;
 import org.apache.commons.io.IOUtils;
@@ -42,6 +47,8 @@ import org.apache.commons.lang3.StringUtils;
 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
 import org.onap.portalsdk.core.util.SystemProperties;
 import org.onap.vid.exceptions.GenericUncheckedException;
+import org.onap.vid.utils.Unchecked.UncheckedThrowingSupplier;
+import org.slf4j.MDC;
 import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Service;
 import org.springframework.web.context.request.RequestContextHolder;
@@ -116,7 +123,7 @@ public class Logging {
         }
         try {
             response.bufferEntity();
-            logger.debug("Received {} {} Status: {} . Body: {}", method.name(), url, response.getStatus(), response.readEntity(entityClass));
+            logger.debug("Received {} {} Status: {} . Body: {}", method.name(), url, response.getStatus(), new Substring(response.readEntity(entityClass)));
         }
         catch (Exception e) {
             logger.debug("Received {} {} Status: {} . Failed to read response as {}", method.name(), url, response.getStatus(), entityClass.getName());
@@ -126,7 +133,7 @@ public class Logging {
     public <T> void logResponse(final EELFLogger logger, final HttpMethod method, final String url, final HttpResponse<T> response) {
         try {
             logger.debug("Received {} {} Status: {} . Body: {}", method.name(),
-                url, response.getStatus(), IOUtils.toString(response.getRawBody(), StandardCharsets.UTF_8));
+                url, response.getStatus(), new Substring(IOUtils.toString(response.getRawBody(), StandardCharsets.UTF_8)));
             response.getRawBody().reset();
         }
         catch (Exception e) {
@@ -197,5 +204,49 @@ public class Logging {
         }
     }
 
+    /**
+     * This class defers the toString() and truncation to the point in time where logger needs it.
+     * This will save some bytes in memory if logger will decide to discard the logging (mostly because logging level
+     * is filtering the message out).
+     */
+    static class Substring {
+        private final Object obj;
+        private final int maxLen = 1_000_000;
+
+        public Substring(Object obj) {
+            this.obj = obj;
+        }
+
+        @Override
+        public String toString() {
+            // null safe truncation
+            return StringUtils.left(Objects.toString(obj), maxLen);
+        }
+    }
+
+    /**
+     * in order to be able to write the correct data while creating the node on a new thread save a copy of the current
+     * thread's context map, with keys and values of type String.
+     */
+    public <T> Callable<T> withMDC(Map<String, String> copyOfParentMDC, Callable<T> callable) {
+        return () -> withMDCInternal(copyOfParentMDC, callable::call);
+    }
+
+    /**
+     * in order to be able to write the correct data while creating the node on a new thread save a copy of the current
+     * thread's context map, with keys and values of type String.
+     */
+    public <T, U> Function<T, U> withMDC(Map<String, String> copyOfParentMDC, Function<T, U> function) {
+        return t -> withMDCInternal(copyOfParentMDC, () -> function.apply(t));
+    }
+
+    <T> T withMDCInternal(Map<String, String> copyOfParentMDC, UncheckedThrowingSupplier<T> supplier) {
+        try {
+            MDC.setContextMap(defaultIfNull(copyOfParentMDC, emptyMap()));
+            return supplier.get();
+        } finally {
+            MDC.clear();
+        }
+    }
 
 }