Skip to content

Commit bdc5fb5

Browse files
Liudmila Molkovaalzimmermsft
andauthored
Use sanitized message in logs (Azure#25433)
* Use a sanitized message in logs Co-authored-by: Alan Zimmer <48699787+alzimmermsft@users.noreply.github.com>
1 parent 11cc395 commit bdc5fb5

File tree

5 files changed

+119
-21
lines changed

5 files changed

+119
-21
lines changed

sdk/core/azure-core/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
--add-opens com.azure.core/com.azure.core.implementation.models.jsonflatten=ALL-UNNAMED
6565
--add-opens com.azure.core/com.azure.core.implementation.serializer=ALL-UNNAMED
6666
--add-opens com.azure.core/com.azure.core.implementation.jackson=ALL-UNNAMED
67+
--add-opens com.azure.core/com.azure.core.implementation.logging=ALL-UNNAMED
6768
--add-opens com.azure.core/com.azure.core.models=ALL-UNNAMED
6869
--add-opens com.azure.core/com.azure.core.util=ALL-UNNAMED
6970
--add-opens com.azure.core/com.azure.core.util.logging=ALL-UNNAMED
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.azure.core.implementation.logging;
5+
6+
import com.azure.core.util.CoreUtils;
7+
8+
/**
9+
* Contains utility methods for logging.
10+
*/
11+
public final class LoggingUtils {
12+
private static final char CR = '\r';
13+
private static final char LF = '\n';
14+
15+
private LoggingUtils() {
16+
}
17+
18+
/*
19+
* Removes CR, LF or CRLF pattern in the {@code logMessage}.
20+
*
21+
* @param logMessage The log message to sanitize.
22+
* @return The updated logMessage.
23+
*/
24+
public static String removeNewLinesFromLogMessage(String logMessage) {
25+
if (CoreUtils.isNullOrEmpty(logMessage)) {
26+
return logMessage;
27+
}
28+
29+
StringBuilder sb = null;
30+
int prevStart = 0;
31+
32+
for (int i = 0; i < logMessage.length(); i++) {
33+
if (logMessage.charAt(i) == CR || logMessage.charAt(i) == LF) {
34+
if (sb == null) {
35+
sb = new StringBuilder(logMessage.length());
36+
}
37+
38+
if (prevStart != i) {
39+
sb.append(logMessage, prevStart, i);
40+
}
41+
prevStart = i + 1;
42+
}
43+
}
44+
45+
if (sb == null) {
46+
return logMessage;
47+
}
48+
sb.append(logMessage, prevStart, logMessage.length());
49+
return sb.toString();
50+
}
51+
}

sdk/core/azure-core/src/main/java/com/azure/core/util/logging/ClientLogger.java

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
import java.util.Arrays;
1414
import java.util.Objects;
1515
import java.util.function.Supplier;
16-
import java.util.regex.Pattern;
16+
17+
import static com.azure.core.implementation.logging.LoggingUtils.removeNewLinesFromLogMessage;
1718

1819
/**
1920
* This is a fluent logger helper class that wraps a pluggable {@link Logger}.
@@ -37,7 +38,6 @@
3738
* @see Configuration
3839
*/
3940
public class ClientLogger {
40-
private static final Pattern CRLF_PATTERN = Pattern.compile("[\r\n]");
4141
private final Logger logger;
4242

4343
/**
@@ -124,7 +124,7 @@ public void log(LogLevel logLevel, Supplier<String> message, Throwable throwable
124124
*/
125125
public void verbose(String message) {
126126
if (logger.isDebugEnabled()) {
127-
logger.debug(sanitizeLogMessageInput(message));
127+
logger.debug(removeNewLinesFromLogMessage(message));
128128
}
129129
}
130130

@@ -168,7 +168,7 @@ public void verbose(String format, Object... args) {
168168
*/
169169
public void info(String message) {
170170
if (logger.isInfoEnabled()) {
171-
logger.info(sanitizeLogMessageInput(message));
171+
logger.info(removeNewLinesFromLogMessage(message));
172172
}
173173
}
174174

@@ -213,7 +213,7 @@ public void info(String format, Object... args) {
213213
*/
214214
public void warning(String message) {
215215
if (logger.isWarnEnabled()) {
216-
logger.warn(sanitizeLogMessageInput(message));
216+
logger.warn(removeNewLinesFromLogMessage(message));
217217
}
218218
}
219219

@@ -262,7 +262,7 @@ public void warning(String format, Object... args) {
262262
*/
263263
public void error(String message) {
264264
if (logger.isErrorEnabled()) {
265-
logger.error(sanitizeLogMessageInput(message));
265+
logger.error(removeNewLinesFromLogMessage(message));
266266
}
267267
}
268268

@@ -419,7 +419,7 @@ private void performLogging(LogLevel logLevel, boolean isExceptionLogging, Strin
419419
}
420420
}
421421

422-
sanitizeLogMessageInput(format);
422+
format = removeNewLinesFromLogMessage(format);
423423

424424
switch (logLevel) {
425425
case VERBOSE:
@@ -455,7 +455,7 @@ private void performLogging(LogLevel logLevel, boolean isExceptionLogging, Strin
455455
private void performDeferredLogging(LogLevel logLevel, Supplier<String> messageSupplier, Throwable throwable) {
456456
String throwableMessage = (throwable != null) ? throwable.getMessage() : "";
457457
String message = messageSupplier.get();
458-
sanitizeLogMessageInput(message);
458+
message = removeNewLinesFromLogMessage(message);
459459
switch (logLevel) {
460460
case VERBOSE:
461461
if (throwable != null) {
@@ -554,17 +554,4 @@ private boolean doesArgsHaveThrowable(Object... args) {
554554
private Object[] removeThrowable(Object... args) {
555555
return Arrays.copyOf(args, args.length - 1);
556556
}
557-
558-
/*
559-
* Removes CRLF pattern in the {@code logMessage}.
560-
*
561-
* @param logMessage The log message to sanitize.
562-
* @return The updated logMessage.
563-
*/
564-
private static String sanitizeLogMessageInput(String logMessage) {
565-
if (CoreUtils.isNullOrEmpty(logMessage)) {
566-
return logMessage;
567-
}
568-
return CRLF_PATTERN.matcher(logMessage).replaceAll("");
569-
}
570557
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.azure.core.implementation.logging;
5+
6+
import org.junit.jupiter.api.Test;
7+
import org.junit.jupiter.params.ParameterizedTest;
8+
import org.junit.jupiter.params.provider.MethodSource;
9+
import org.junit.jupiter.params.provider.NullAndEmptySource;
10+
import org.junit.jupiter.params.provider.ValueSource;
11+
12+
import java.util.stream.Stream;
13+
14+
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
16+
public class LoggingUtilsTests {
17+
private static final String NEW_LINE = System.lineSeparator();
18+
@ParameterizedTest
19+
@NullAndEmptySource
20+
@ValueSource(strings = {"hello", "hello world\t123"})
21+
public void noNewLine(String message) {
22+
assertEquals(message, LoggingUtils.removeNewLinesFromLogMessage(message));
23+
}
24+
25+
@ParameterizedTest
26+
@MethodSource("messagesWithNewLine")
27+
public void newLine(String message) {
28+
assertEquals("hello", LoggingUtils.removeNewLinesFromLogMessage(message));
29+
}
30+
31+
@Test
32+
public void newLineOnly() {
33+
assertEquals("", LoggingUtils.removeNewLinesFromLogMessage(NEW_LINE + NEW_LINE + NEW_LINE));
34+
}
35+
36+
@Test
37+
public void withCL() {
38+
assertEquals("\thelloworld", LoggingUtils.removeNewLinesFromLogMessage("\r\thello\r" + NEW_LINE + "world"));
39+
}
40+
41+
private static Stream<String> messagesWithNewLine() {
42+
return Stream.of(
43+
"hello" + NEW_LINE + NEW_LINE,
44+
NEW_LINE + "hello" + NEW_LINE,
45+
NEW_LINE + NEW_LINE + "hello",
46+
NEW_LINE + "he" + NEW_LINE + "l" + NEW_LINE + "lo");
47+
}
48+
}

sdk/core/azure-core/src/test/java/com/azure/core/util/logging/ClientLoggerTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,17 @@ public void logWithSupplier(LogLevel logLevel) {
269269
assertTrue(logValues.contains(supplier.get() + System.lineSeparator()));
270270
}
271271

272+
@Test
273+
public void logWithNewLine() {
274+
setupLogLevel(LogLevel.INFORMATIONAL.getLogLevel());
275+
String message = String.format("Param 1: %s%s, Param 2: %s%s, Param 3: %s", "test1", System.lineSeparator(), "test2", System.lineSeparator(), "test3");
276+
ClientLogger logger = new ClientLogger(ClientLoggerTests.class);
277+
logger.log(LogLevel.INFORMATIONAL, () -> message);
278+
279+
String logValues = byteArraySteamToString(logCaptureStream);
280+
assertTrue(logValues.contains("Param 1: test1, Param 2: test2, Param 3: test3"));
281+
}
282+
272283
@ParameterizedTest
273284
@MethodSource("provideLogLevels")
274285
public void logWithNullSupplier(LogLevel logLevel) {

0 commit comments

Comments
 (0)