Skip to content

Commit 5a3db09

Browse files
committed
Add escaping to logging output
This aligns the other formatters with the JSON formatter
1 parent f2d843e commit 5a3db09

File tree

6 files changed

+167
-8
lines changed

6 files changed

+167
-8
lines changed

java/org/apache/juli/JdkLoggerFormatter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public String format(LogRecord record) {
9696
buf.append(" ".repeat(Math.max(0, 8 - buf.length())));
9797

9898
// Append the message
99-
buf.append(message);
99+
buf.append(LogUtil.escape(message));
100100

101101
// Append stack trace if not null
102102
if (t != null) {
@@ -106,7 +106,7 @@ public String format(LogRecord record) {
106106
java.io.PrintWriter pw = new java.io.PrintWriter(sw);
107107
t.printStackTrace(pw);
108108
pw.close();
109-
buf.append(sw);
109+
buf.append(LogUtil.escape(sw.toString()));
110110
}
111111

112112
buf.append(System.lineSeparator());

java/org/apache/juli/LogUtil.java

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.juli;
18+
19+
public class LogUtil {
20+
21+
private LogUtil() {
22+
// Utility class. Hide default constructor
23+
}
24+
25+
26+
/**
27+
* Escape a string so it can be displayed in a readable format. Characters that may not be printable in some/all of
28+
* the contexts in which log messages will be viewed will be escaped using Java \\uNNNN escaping.
29+
* <p>
30+
* All control characters are escaped apart from horizontal tab (\\u0009), new line (\\u000a) and carriage return
31+
* (\\u000d).
32+
*
33+
* @param input The string to escape
34+
*
35+
* @return The escaped form of the input string
36+
*/
37+
@SuppressWarnings("null") // sb is not null when used
38+
public static String escape(final String input) {
39+
final int len = input.length();
40+
int i = 0;
41+
int lastControl = -1;
42+
StringBuilder sb = null;
43+
while (i < len) {
44+
char c = input.charAt(i);
45+
if (Character.getType(c) == Character.CONTROL) {
46+
if (!(c == '\t' || c == '\n' || c == '\r')) {
47+
if (lastControl == -1) {
48+
sb = new StringBuilder(len + 20);
49+
}
50+
sb.append(input.substring(lastControl + 1, i));
51+
sb.append(String.format("\\u%1$04x", Integer.valueOf(c)));
52+
lastControl = i;
53+
}
54+
}
55+
i++;
56+
}
57+
if (lastControl == -1) {
58+
return input;
59+
} else {
60+
sb.append(input.substring(lastControl + 1, len));
61+
return sb.toString();
62+
}
63+
}
64+
}

java/org/apache/juli/OneLineFormatter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public String format(LogRecord record) {
147147

148148
// Message
149149
sb.append(' ');
150-
sb.append(formatMessage(record));
150+
sb.append(LogUtil.escape(formatMessage(record)));
151151

152152
// New line for next record
153153
sb.append(System.lineSeparator());
@@ -158,7 +158,7 @@ public String format(LogRecord record) {
158158
PrintWriter pw = new IndentingPrintWriter(sw);
159159
record.getThrown().printStackTrace(pw);
160160
pw.close();
161-
sb.append(sw.getBuffer());
161+
sb.append(LogUtil.escape(sw.toString()));
162162
}
163163

164164
return sb.toString();

java/org/apache/juli/VerbatimFormatter.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@
2020
import java.util.logging.LogRecord;
2121

2222
/**
23-
* Outputs just the log message with no additional elements. Stack traces are not logged. Log messages are separated by
24-
* <code>System.lineSeparator()</code>. This is intended for use by access logs and the like that need complete control
25-
* over the output format.
23+
* Outputs just the log message with no additional elements and no escaping. Stack traces are not logged. Log messages
24+
* are separated by <code>System.lineSeparator()</code>. This is intended for use by access logs and the like that need
25+
* complete control over the output format.
2626
*/
2727
public class VerbatimFormatter extends Formatter {
2828

@@ -31,5 +31,4 @@ public String format(LogRecord record) {
3131
// Timestamp + New line for next record
3232
return record.getMessage() + System.lineSeparator();
3333
}
34-
3534
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.juli;
18+
19+
import org.junit.Assert;
20+
import org.junit.Test;
21+
22+
public class TestLogUtil {
23+
24+
@Test
25+
public void testEscapeForLoggingEmptyString() {
26+
doTestEscapeForLogging("");
27+
}
28+
29+
30+
@Test
31+
public void testEscapeForLoggingNone() {
32+
doTestEscapeForLogging("No escaping");
33+
}
34+
35+
36+
@Test
37+
public void testEscapeForLoggingControlStart() {
38+
doTestEscapeForLogging("\u0006Text", "\\u0006Text");
39+
}
40+
41+
42+
@Test
43+
public void testEscapeForLoggingControlMiddle() {
44+
doTestEscapeForLogging("Text\u0006Text", "Text\\u0006Text");
45+
}
46+
47+
48+
@Test
49+
public void testEscapeForLoggingControlEnd() {
50+
doTestEscapeForLogging("Text\u0006", "Text\\u0006");
51+
}
52+
53+
54+
@Test
55+
public void testEscapeForLoggingControlOnly() {
56+
doTestEscapeForLogging("\u0006", "\\u0006");
57+
}
58+
59+
60+
@Test
61+
public void testEscapeForLoggingControlsStart() {
62+
doTestEscapeForLogging("\u0006\u0007Text", "\\u0006\\u0007Text");
63+
}
64+
65+
66+
@Test
67+
public void testEscapeForLoggingControlsMiddle() {
68+
doTestEscapeForLogging("Text\u0006\u0007Text", "Text\\u0006\\u0007Text");
69+
}
70+
71+
72+
@Test
73+
public void testEscapeForLoggingControlsEnd() {
74+
doTestEscapeForLogging("Text\u0006\u0007", "Text\\u0006\\u0007");
75+
}
76+
77+
78+
@Test
79+
public void testEscapeForLoggingControlsOnly() {
80+
doTestEscapeForLogging("\u0006\u0007", "\\u0006\\u0007");
81+
}
82+
83+
84+
private void doTestEscapeForLogging(String input) {
85+
doTestEscapeForLogging(input, input);
86+
}
87+
88+
89+
private void doTestEscapeForLogging(String input, String expected) {
90+
String result = LogUtil.escape(input);
91+
Assert.assertEquals(expected, result);
92+
}
93+
}

webapps/docs/changelog.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@
120120
by default rather then just the exception message when logging an error
121121
or warning in response to an exception. (markt)
122122
</scode>
123+
<add>
124+
Add escaping to log formatters to align with JSON formatter. (markt)
125+
</add>
123126
</changelog>
124127
</subsection>
125128
</section>

0 commit comments

Comments
 (0)