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;
import com.fasterxml.jackson.databind.SerializationFeature;
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;
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;
}
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());
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(), response.getBody());
+ logger.debug("Received {} {} Status: {} . Body: {}", method.name(),
+ url, response.getStatus(), new Substring(IOUtils.toString(response.getRawBody(), StandardCharsets.UTF_8)));
+ response.getRawBody().reset();
}
catch (Exception e) {
logger.debug("Received {} {} Status: {} . Failed to read response", method.name(), url, response.getStatus());
}
}
+ /**
+ * 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();
+ }
+ }
}