diff --git a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java index d5eb62906e..e9d9269047 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java @@ -70,7 +70,13 @@ public final class CharSequenceEncoder extends AbstractEncoder { return Flux.from(inputStream).map(charSequence -> { if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) { String logPrefix = Hints.getLogPrefix(hints); - logger.debug(logPrefix + "Writing '" + charSequence + "'"); + String s = logPrefix + "Writing " + formatValue(charSequence, logger.isTraceEnabled()); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } CharBuffer charBuffer = CharBuffer.wrap(charSequence); ByteBuffer byteBuffer = charset.encode(charBuffer); @@ -89,6 +95,15 @@ public final class CharSequenceEncoder extends AbstractEncoder { return charset; } + private String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + + /** * Create a {@code CharSequenceEncoder} that supports only "text/plain". */ diff --git a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java index c16d58f96d..ceac18bf90 100644 --- a/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java +++ b/spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java @@ -207,7 +207,13 @@ public final class StringDecoder extends AbstractDataBufferDecoder { DataBufferUtils.release(dataBuffer); String value = charBuffer.toString(); if (logger.isDebugEnabled()) { - logger.debug(Hints.getLogPrefix(hints) + "Decoded '" + value + "'"); + String s = Hints.getLogPrefix(hints) + "Decoded " + formatValue(value, logger.isTraceEnabled()); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } return value; } @@ -221,6 +227,14 @@ public final class StringDecoder extends AbstractDataBufferDecoder { } } + private String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + /** * Create a {@code StringDecoder} for {@code "text/plain"}. diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java index 3d6e0abca1..a893c69798 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java @@ -108,15 +108,26 @@ public class FormHttpMessageReader extends LoggingCodecSupport String body = charBuffer.toString(); DataBufferUtils.release(buffer); MultiValueMap formData = parseFormData(charset, body); - if (logger.isDebugEnabled()) { - String details = isEnableLoggingRequestDetails() ? - formData.toString() : "form fields " + formData.keySet() + " (content masked)"; - logger.debug(Hints.getLogPrefix(hints) + "Read " + details); - } + logFormData(formData, hints); return formData; }); } + private void logFormData(MultiValueMap formData, Map hints) { + if (logger.isDebugEnabled()) { + String s = Hints.getLogPrefix(hints) + "Read " + + (isEnableLoggingRequestDetails() ? + formatValue(formData, logger.isTraceEnabled()) : + "form fields " + formData.keySet() + " (content masked)"); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } + } + } + private Charset getMediaTypeCharset(@Nullable MediaType mediaType) { if (mediaType != null && mediaType.getCharset() != null) { return mediaType.getCharset(); diff --git a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java index ecd54c1f1d..c27d0b0cc0 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java @@ -132,9 +132,15 @@ public class FormHttpMessageWriter extends LoggingCodecSupport return Mono.from(inputStream).flatMap(form -> { if (logger.isDebugEnabled()) { - String details = isEnableLoggingRequestDetails() ? - form.toString() : "form fields " + form.keySet() + " (content masked)"; - logger.debug(Hints.getLogPrefix(hints) + "Writing " + details); + String s = Hints.getLogPrefix(hints) + "Writing " + + (isEnableLoggingRequestDetails() ? + form.toString() : "form fields " + form.keySet() + " (content masked)"); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } String value = serializeForm(form, charset); ByteBuffer byteBuffer = charset.encode(value); diff --git a/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java index 3502adc0e6..5191c990b5 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java @@ -19,6 +19,7 @@ package org.springframework.http.codec; import org.apache.commons.logging.Log; import org.springframework.http.HttpLogging; +import org.springframework.lang.Nullable; /** * Base class for {@link org.springframework.core.codec.Encoder}, @@ -55,4 +56,20 @@ public class LoggingCodecSupport { return this.enableLoggingRequestDetails; } + /** + * Format the given value via {{toString()}}, either in full or truncated + * if it has 100 or more characters. + * @param value the value to format + * @param logFullValue whether to log in full or truncate if necessary + * @return the formatted value + * @since 5.1 + */ + protected String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java index f614e957dc..cd222927c5 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java @@ -116,7 +116,14 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple try { Object value = reader.readValue(tokenBuffer.asParser(getObjectMapper())); if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) { - logger.debug(Hints.getLogPrefix(hints) +"Decoded [" + value + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = Hints.getLogPrefix(hints) + "Decoded [" + formatValue(value, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } return value; } diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java index 6bf4f7bf38..1ff5b413a6 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java @@ -142,7 +142,14 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple ResolvableType elementType, @Nullable Map hints, JsonEncoding encoding) { if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) { - logger.debug(Hints.getLogPrefix(hints) + "Encoding [" + value + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = Hints.getLogPrefix(hints) + "Encoding [" + formatValue(value, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } JavaType javaType = getJavaType(elementType.getType(), null); diff --git a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java index ff3efa860c..1b2439f41a 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java +++ b/spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java @@ -125,4 +125,12 @@ public abstract class Jackson2CodecSupport { @Nullable protected abstract A getAnnotation(MethodParameter parameter, Class annotType); + String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + } diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java index b4b0b4628a..791b62610c 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java @@ -95,9 +95,15 @@ public class MultipartHttpMessageReader extends LoggingCodecSupport .collectMultimap(Part::name) .doOnNext(map -> { if (logger.isDebugEnabled()) { - String details = isEnableLoggingRequestDetails() ? - map.toString() : "parts " + map.keySet() + " (content masked)"; - logger.debug(Hints.getLogPrefix(hints) + "Parsed " + details); + String s = Hints.getLogPrefix(hints) + "Parsed " + + (isEnableLoggingRequestDetails() ? + map.toString() : "parts " + map.keySet() + " (content masked)"); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } }) .map(this::toMultiValueMap); diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java index d9d28b0f07..7a7a940617 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java @@ -226,9 +226,15 @@ public class MultipartHttpMessageWriter extends LoggingCodecSupport outputMessage.getHeaders().setContentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params)); if (logger.isDebugEnabled()) { - String details = isEnableLoggingRequestDetails() ? - map.toString() : "parts " + map.keySet() + " (content masked)"; - logger.debug(Hints.getLogPrefix(hints) + "Encoding " + details); + String s = Hints.getLogPrefix(hints) + "Encoding " + + (isEnableLoggingRequestDetails() ? + map.toString() : "parts " + map.keySet() + " (content masked)"); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } Flux body = Flux.fromIterable(map.entrySet()) diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java index c54ce4827f..23e4615647 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/SynchronossPartHttpMessageReader.java @@ -96,9 +96,14 @@ public class SynchronossPartHttpMessageReader extends LoggingCodecSupport implem return Flux.create(new SynchronossPartGenerator(message, this.bufferFactory, this.streamStorageFactory)) .doOnNext(part -> { if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) { - String details = isEnableLoggingRequestDetails() ? - part.toString() : "parts '" + part.name() + "' (content masked)"; - logger.debug(Hints.getLogPrefix(hints) + "Parsed " + details); + String s = Hints.getLogPrefix(hints) + "Parsed " + (isEnableLoggingRequestDetails() ? + part.toString() : "parts '" + part.name() + "' (content masked)"); + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } }); } diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java index 97bdcaffb8..ec02c715f2 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlDecoder.java @@ -108,7 +108,14 @@ public class Jaxb2XmlDecoder extends AbstractDecoder { return splitEvents.map(events -> { Object value = unmarshal(events, outputClass); if (logger.isDebugEnabled()) { - logger.debug(Hints.getLogPrefix(hints) + "Decoded [" + value + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = Hints.getLogPrefix(hints) + "Decoded [" + formatValue(value, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } return value; }); @@ -206,6 +213,14 @@ public class Jaxb2XmlDecoder extends AbstractDecoder { return xmlEventFlux.flatMap(new SplitFunction(desiredName)); } + String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + private static class SplitFunction implements Function>> { diff --git a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java index 331f5e05b0..5d991268a4 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/xml/Jaxb2XmlEncoder.java @@ -79,7 +79,14 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder { ResolvableType type, @Nullable MimeType mimeType, @Nullable Map hints) { try { if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) { - logger.debug(Hints.getLogPrefix(hints) + "Encoding [" + value + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = Hints.getLogPrefix(hints) + "Encoding [" + formatValue(value, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } DataBuffer buffer = dataBufferFactory.allocateBuffer(1024); OutputStream outputStream = buffer.asOutputStream(); @@ -97,4 +104,12 @@ public class Jaxb2XmlEncoder extends AbstractSingleValueEncoder { } } + String formatValue(@Nullable Object value, boolean logFullValue) { + if (value == null) { + return ""; + } + String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString(); + return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java index 434abb73ac..acb4c41977 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java @@ -224,8 +224,15 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements } if (logger.isDebugEnabled()) { - logger.debug("Read \"" + contentType + "\" to " + - "[" + (body instanceof String ? "\"" + body + "\"" : body) + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = "Read \"" + contentType + "\" to [" + + RequestMappingHandlerAdapter.formatValue(body, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } return body; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java index 5077764fd9..58df0c4f32 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java @@ -281,7 +281,14 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe inputMessage, outputMessage); if (body != null) { if (logger.isDebugEnabled()) { - logger.debug("Writing [" + formatValue(body) + "]"); + boolean traceOn = logger.isTraceEnabled(); + String s = "Writing [" + RequestMappingHandlerAdapter.formatValue(body, traceOn) + "]"; + if (traceOn) { + logger.trace(s); + } + else { + logger.debug(s); + } } addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { @@ -398,10 +405,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe return (MediaType.SPECIFICITY_COMPARATOR.compare(acceptType, produceTypeToUse) <= 0 ? acceptType : produceTypeToUse); } - static String formatValue(Object body) { - return (body instanceof CharSequence ? "\"" + body + "\"" : body.toString()); - } - /** * Check if the path has a file extension and whether the extension is * either {@link #WHITELISTED_EXTENSIONS whitelisted} or explicitly diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java index 84da9dcdd1..f859b730bf 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.java @@ -885,8 +885,13 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0]; asyncManager.clearConcurrentResult(); if (logger.isDebugEnabled()) { - String formatted = AbstractMessageConverterMethodProcessor.formatValue(result); - logger.debug("Resume with async result [" + formatted + "]"); + String s = "Resume with async result [" + formatValue(result, logger.isTraceEnabled()) + "]"; + if (logger.isTraceEnabled()) { + logger.trace(s); + } + else { + logger.debug(s); + } } invocableMethod = invocableMethod.wrapConcurrentResult(result); } @@ -1019,4 +1024,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter return mav; } + static String formatValue(@Nullable Object body, boolean logFullBody) { + if (body == null) { + return ""; + } + String s = body instanceof CharSequence ? "\"" + body + "\"" : body.toString(); + return logFullBody || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)..."; + } + }