This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push: new 5a3db09298 Add escaping to logging output 5a3db09298 is described below commit 5a3db092982c0c58d4855304167ee757fe5e79bb Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Aug 18 22:20:00 2025 +0100 Add escaping to logging output This aligns the other formatters with the JSON formatter --- java/org/apache/juli/JdkLoggerFormatter.java | 4 +- java/org/apache/juli/LogUtil.java | 64 +++++++++++++++++++ java/org/apache/juli/OneLineFormatter.java | 4 +- java/org/apache/juli/VerbatimFormatter.java | 7 +-- test/org/apache/juli/TestLogUtil.java | 93 ++++++++++++++++++++++++++++ webapps/docs/changelog.xml | 3 + 6 files changed, 167 insertions(+), 8 deletions(-) diff --git a/java/org/apache/juli/JdkLoggerFormatter.java b/java/org/apache/juli/JdkLoggerFormatter.java index 254ea2a4f3..10e343da43 100644 --- a/java/org/apache/juli/JdkLoggerFormatter.java +++ b/java/org/apache/juli/JdkLoggerFormatter.java @@ -96,7 +96,7 @@ public class JdkLoggerFormatter extends Formatter { buf.append(" ".repeat(Math.max(0, 8 - buf.length()))); // Append the message - buf.append(message); + buf.append(LogUtil.escape(message)); // Append stack trace if not null if (t != null) { @@ -106,7 +106,7 @@ public class JdkLoggerFormatter extends Formatter { java.io.PrintWriter pw = new java.io.PrintWriter(sw); t.printStackTrace(pw); pw.close(); - buf.append(sw); + buf.append(LogUtil.escape(sw.toString())); } buf.append(System.lineSeparator()); diff --git a/java/org/apache/juli/LogUtil.java b/java/org/apache/juli/LogUtil.java new file mode 100644 index 0000000000..c7eb098331 --- /dev/null +++ b/java/org/apache/juli/LogUtil.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juli; + +public class LogUtil { + + private LogUtil() { + // Utility class. Hide default constructor + } + + + /** + * Escape a string so it can be displayed in a readable format. Characters that may not be printable in some/all of + * the contexts in which log messages will be viewed will be escaped using Java \\uNNNN escaping. + * <p> + * All control characters are escaped apart from horizontal tab (\\u0009), new line (\\u000a) and carriage return + * (\\u000d). + * + * @param input The string to escape + * + * @return The escaped form of the input string + */ + @SuppressWarnings("null") // sb is not null when used + public static String escape(final String input) { + final int len = input.length(); + int i = 0; + int lastControl = -1; + StringBuilder sb = null; + while (i < len) { + char c = input.charAt(i); + if (Character.getType(c) == Character.CONTROL) { + if (!(c == '\t' || c == '\n' || c == '\r')) { + if (lastControl == -1) { + sb = new StringBuilder(len + 20); + } + sb.append(input.substring(lastControl + 1, i)); + sb.append(String.format("\\u%1$04x", Integer.valueOf(c))); + lastControl = i; + } + } + i++; + } + if (lastControl == -1) { + return input; + } else { + sb.append(input.substring(lastControl + 1, len)); + return sb.toString(); + } + } +} diff --git a/java/org/apache/juli/OneLineFormatter.java b/java/org/apache/juli/OneLineFormatter.java index 447a4304ce..b0089014b1 100644 --- a/java/org/apache/juli/OneLineFormatter.java +++ b/java/org/apache/juli/OneLineFormatter.java @@ -147,7 +147,7 @@ public class OneLineFormatter extends Formatter { // Message sb.append(' '); - sb.append(formatMessage(record)); + sb.append(LogUtil.escape(formatMessage(record))); // New line for next record sb.append(System.lineSeparator()); @@ -158,7 +158,7 @@ public class OneLineFormatter extends Formatter { PrintWriter pw = new IndentingPrintWriter(sw); record.getThrown().printStackTrace(pw); pw.close(); - sb.append(sw.getBuffer()); + sb.append(LogUtil.escape(sw.toString())); } return sb.toString(); diff --git a/java/org/apache/juli/VerbatimFormatter.java b/java/org/apache/juli/VerbatimFormatter.java index 88efa4ddf7..2653b180cf 100644 --- a/java/org/apache/juli/VerbatimFormatter.java +++ b/java/org/apache/juli/VerbatimFormatter.java @@ -20,9 +20,9 @@ import java.util.logging.Formatter; import java.util.logging.LogRecord; /** - * Outputs just the log message with no additional elements. Stack traces are not logged. Log messages are separated by - * <code>System.lineSeparator()</code>. This is intended for use by access logs and the like that need complete control - * over the output format. + * Outputs just the log message with no additional elements and no escaping. Stack traces are not logged. Log messages + * are separated by <code>System.lineSeparator()</code>. This is intended for use by access logs and the like that need + * complete control over the output format. */ public class VerbatimFormatter extends Formatter { @@ -31,5 +31,4 @@ public class VerbatimFormatter extends Formatter { // Timestamp + New line for next record return record.getMessage() + System.lineSeparator(); } - } diff --git a/test/org/apache/juli/TestLogUtil.java b/test/org/apache/juli/TestLogUtil.java new file mode 100644 index 0000000000..12360c2212 --- /dev/null +++ b/test/org/apache/juli/TestLogUtil.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.juli; + +import org.junit.Assert; +import org.junit.Test; + +public class TestLogUtil { + + @Test + public void testEscapeForLoggingEmptyString() { + doTestEscapeForLogging(""); + } + + + @Test + public void testEscapeForLoggingNone() { + doTestEscapeForLogging("No escaping"); + } + + + @Test + public void testEscapeForLoggingControlStart() { + doTestEscapeForLogging("\u0006Text", "\\u0006Text"); + } + + + @Test + public void testEscapeForLoggingControlMiddle() { + doTestEscapeForLogging("Text\u0006Text", "Text\\u0006Text"); + } + + + @Test + public void testEscapeForLoggingControlEnd() { + doTestEscapeForLogging("Text\u0006", "Text\\u0006"); + } + + + @Test + public void testEscapeForLoggingControlOnly() { + doTestEscapeForLogging("\u0006", "\\u0006"); + } + + + @Test + public void testEscapeForLoggingControlsStart() { + doTestEscapeForLogging("\u0006\u0007Text", "\\u0006\\u0007Text"); + } + + + @Test + public void testEscapeForLoggingControlsMiddle() { + doTestEscapeForLogging("Text\u0006\u0007Text", "Text\\u0006\\u0007Text"); + } + + + @Test + public void testEscapeForLoggingControlsEnd() { + doTestEscapeForLogging("Text\u0006\u0007", "Text\\u0006\\u0007"); + } + + + @Test + public void testEscapeForLoggingControlsOnly() { + doTestEscapeForLogging("\u0006\u0007", "\\u0006\\u0007"); + } + + + private void doTestEscapeForLogging(String input) { + doTestEscapeForLogging(input, input); + } + + + private void doTestEscapeForLogging(String input, String expected) { + String result = LogUtil.escape(input); + Assert.assertEquals(expected, result); + } +} \ No newline at end of file diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index b03d0fe7b7..475fed83cc 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -120,6 +120,9 @@ by default rather then just the exception message when logging an error or warning in response to an exception. (markt) </scode> + <add> + Add escaping to log formatters to align with JSON formatter. (markt) + </add> </changelog> </subsection> </section> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org