Skip to content

Commit e62298e

Browse files
committed
Truncate logged encoded and decoded values if necessary
At DEBUG show up to 100 chars, at TRACE show full formatted value. Note that the formatValue helper method is duplicated a number of times in this commit. A utility method will likely be added in spring-core through an extra commit. Issue: SPR-17254
1 parent 66c66ba commit e62298e

16 files changed

+187
-32
lines changed

spring-core/src/main/java/org/springframework/core/codec/CharSequenceEncoder.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,13 @@ public Flux<DataBuffer> encode(Publisher<? extends CharSequence> inputStream,
7070
return Flux.from(inputStream).map(charSequence -> {
7171
if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) {
7272
String logPrefix = Hints.getLogPrefix(hints);
73-
logger.debug(logPrefix + "Writing '" + charSequence + "'");
73+
String s = logPrefix + "Writing " + formatValue(charSequence, logger.isTraceEnabled());
74+
if (logger.isTraceEnabled()) {
75+
logger.trace(s);
76+
}
77+
else {
78+
logger.debug(s);
79+
}
7480
}
7581
CharBuffer charBuffer = CharBuffer.wrap(charSequence);
7682
ByteBuffer byteBuffer = charset.encode(charBuffer);
@@ -89,6 +95,15 @@ private Charset getCharset(@Nullable MimeType mimeType) {
8995
return charset;
9096
}
9197

98+
private String formatValue(@Nullable Object value, boolean logFullValue) {
99+
if (value == null) {
100+
return "";
101+
}
102+
String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString();
103+
return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)...";
104+
}
105+
106+
92107
/**
93108
* Create a {@code CharSequenceEncoder} that supports only "text/plain".
94109
*/

spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,13 @@ protected String decodeDataBuffer(DataBuffer dataBuffer, ResolvableType elementT
207207
DataBufferUtils.release(dataBuffer);
208208
String value = charBuffer.toString();
209209
if (logger.isDebugEnabled()) {
210-
logger.debug(Hints.getLogPrefix(hints) + "Decoded '" + value + "'");
210+
String s = Hints.getLogPrefix(hints) + "Decoded " + formatValue(value, logger.isTraceEnabled());
211+
if (logger.isTraceEnabled()) {
212+
logger.trace(s);
213+
}
214+
else {
215+
logger.debug(s);
216+
}
211217
}
212218
return value;
213219
}
@@ -221,6 +227,14 @@ private static Charset getCharset(@Nullable MimeType mimeType) {
221227
}
222228
}
223229

230+
private String formatValue(@Nullable Object value, boolean logFullValue) {
231+
if (value == null) {
232+
return "";
233+
}
234+
String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString();
235+
return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)...";
236+
}
237+
224238

225239
/**
226240
* Create a {@code StringDecoder} for {@code "text/plain"}.

spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageReader.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,26 @@ public Mono<MultiValueMap<String, String>> readMono(ResolvableType elementType,
108108
String body = charBuffer.toString();
109109
DataBufferUtils.release(buffer);
110110
MultiValueMap<String, String> formData = parseFormData(charset, body);
111-
if (logger.isDebugEnabled()) {
112-
String details = isEnableLoggingRequestDetails() ?
113-
formData.toString() : "form fields " + formData.keySet() + " (content masked)";
114-
logger.debug(Hints.getLogPrefix(hints) + "Read " + details);
115-
}
111+
logFormData(formData, hints);
116112
return formData;
117113
});
118114
}
119115

116+
private void logFormData(MultiValueMap<String, String> formData, Map<String, Object> hints) {
117+
if (logger.isDebugEnabled()) {
118+
String s = Hints.getLogPrefix(hints) + "Read " +
119+
(isEnableLoggingRequestDetails() ?
120+
formatValue(formData, logger.isTraceEnabled()) :
121+
"form fields " + formData.keySet() + " (content masked)");
122+
if (logger.isTraceEnabled()) {
123+
logger.trace(s);
124+
}
125+
else {
126+
logger.debug(s);
127+
}
128+
}
129+
}
130+
120131
private Charset getMediaTypeCharset(@Nullable MediaType mediaType) {
121132
if (mediaType != null && mediaType.getCharset() != null) {
122133
return mediaType.getCharset();

spring-web/src/main/java/org/springframework/http/codec/FormHttpMessageWriter.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,15 @@ public Mono<Void> write(Publisher<? extends MultiValueMap<String, String>> input
132132

133133
return Mono.from(inputStream).flatMap(form -> {
134134
if (logger.isDebugEnabled()) {
135-
String details = isEnableLoggingRequestDetails() ?
136-
form.toString() : "form fields " + form.keySet() + " (content masked)";
137-
logger.debug(Hints.getLogPrefix(hints) + "Writing " + details);
135+
String s = Hints.getLogPrefix(hints) + "Writing " +
136+
(isEnableLoggingRequestDetails() ?
137+
form.toString() : "form fields " + form.keySet() + " (content masked)");
138+
if (logger.isTraceEnabled()) {
139+
logger.trace(s);
140+
}
141+
else {
142+
logger.debug(s);
143+
}
138144
}
139145
String value = serializeForm(form, charset);
140146
ByteBuffer byteBuffer = charset.encode(value);

spring-web/src/main/java/org/springframework/http/codec/LoggingCodecSupport.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.apache.commons.logging.Log;
2020

2121
import org.springframework.http.HttpLogging;
22+
import org.springframework.lang.Nullable;
2223

2324
/**
2425
* Base class for {@link org.springframework.core.codec.Encoder},
@@ -55,4 +56,20 @@ public boolean isEnableLoggingRequestDetails() {
5556
return this.enableLoggingRequestDetails;
5657
}
5758

59+
/**
60+
* Format the given value via {{toString()}}, either in full or truncated
61+
* if it has 100 or more characters.
62+
* @param value the value to format
63+
* @param logFullValue whether to log in full or truncate if necessary
64+
* @return the formatted value
65+
* @since 5.1
66+
*/
67+
protected String formatValue(@Nullable Object value, boolean logFullValue) {
68+
if (value == null) {
69+
return "";
70+
}
71+
String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString();
72+
return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)...";
73+
}
74+
5875
}

spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Decoder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,14 @@ private Flux<Object> decodeInternal(Flux<TokenBuffer> tokens, ResolvableType ele
116116
try {
117117
Object value = reader.readValue(tokenBuffer.asParser(getObjectMapper()));
118118
if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) {
119-
logger.debug(Hints.getLogPrefix(hints) +"Decoded [" + value + "]");
119+
boolean traceOn = logger.isTraceEnabled();
120+
String s = Hints.getLogPrefix(hints) + "Decoded [" + formatValue(value, traceOn) + "]";
121+
if (traceOn) {
122+
logger.trace(s);
123+
}
124+
else {
125+
logger.debug(s);
126+
}
120127
}
121128
return value;
122129
}

spring-web/src/main/java/org/springframework/http/codec/json/AbstractJackson2Encoder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,14 @@ private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBu
142142
ResolvableType elementType, @Nullable Map<String, Object> hints, JsonEncoding encoding) {
143143

144144
if (logger.isDebugEnabled() && !Hints.isLoggingSuppressed(hints)) {
145-
logger.debug(Hints.getLogPrefix(hints) + "Encoding [" + value + "]");
145+
boolean traceOn = logger.isTraceEnabled();
146+
String s = Hints.getLogPrefix(hints) + "Encoding [" + formatValue(value, traceOn) + "]";
147+
if (traceOn) {
148+
logger.trace(s);
149+
}
150+
else {
151+
logger.debug(s);
152+
}
146153
}
147154

148155
JavaType javaType = getJavaType(elementType.getType(), null);

spring-web/src/main/java/org/springframework/http/codec/json/Jackson2CodecSupport.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,12 @@ protected MethodParameter getParameter(ResolvableType type) {
125125
@Nullable
126126
protected abstract <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType);
127127

128+
String formatValue(@Nullable Object value, boolean logFullValue) {
129+
if (value == null) {
130+
return "";
131+
}
132+
String s = value instanceof CharSequence ? "\"" + value + "\"" : value.toString();
133+
return logFullValue || s.length() < 100 ? s : s.substring(0, 100) + " (truncated)...";
134+
}
135+
128136
}

spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageReader.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,15 @@ public Mono<MultiValueMap<String, Part>> readMono(ResolvableType elementType,
9595
.collectMultimap(Part::name)
9696
.doOnNext(map -> {
9797
if (logger.isDebugEnabled()) {
98-
String details = isEnableLoggingRequestDetails() ?
99-
map.toString() : "parts " + map.keySet() + " (content masked)";
100-
logger.debug(Hints.getLogPrefix(hints) + "Parsed " + details);
98+
String s = Hints.getLogPrefix(hints) + "Parsed " +
99+
(isEnableLoggingRequestDetails() ?
100+
map.toString() : "parts " + map.keySet() + " (content masked)");
101+
if (logger.isTraceEnabled()) {
102+
logger.trace(s);
103+
}
104+
else {
105+
logger.debug(s);
106+
}
101107
}
102108
})
103109
.map(this::toMultiValueMap);

spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartHttpMessageWriter.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,15 @@ private Mono<Void> writeMultipart(
226226
outputMessage.getHeaders().setContentType(new MediaType(MediaType.MULTIPART_FORM_DATA, params));
227227

228228
if (logger.isDebugEnabled()) {
229-
String details = isEnableLoggingRequestDetails() ?
230-
map.toString() : "parts " + map.keySet() + " (content masked)";
231-
logger.debug(Hints.getLogPrefix(hints) + "Encoding " + details);
229+
String s = Hints.getLogPrefix(hints) + "Encoding " +
230+
(isEnableLoggingRequestDetails() ?
231+
map.toString() : "parts " + map.keySet() + " (content masked)");
232+
if (logger.isTraceEnabled()) {
233+
logger.trace(s);
234+
}
235+
else {
236+
logger.debug(s);
237+
}
232238
}
233239

234240
Flux<DataBuffer> body = Flux.fromIterable(map.entrySet())

0 commit comments

Comments
 (0)