This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 8a531b84435fdcf777e15b02516fa9564d4338cc Author: opencode <[email protected]> AuthorDate: Tue Oct 20 09:31:10 2026 +0200 Avoid NPE in juli formatters for records with no message A LogRecord with a null message is legal, and Formatter.formatMessage() then returns null. The message was passed unguarded to LogUtil.escape() by OneLineFormatter and JdkLoggerFormatter and to JsonFormatter.JSONFilter.escape() by JsonFormatter, so all three formatters threw a NullPointerException instead of formatting the record. When used with a handler that catches the exception, such as FileHandler, the record was silently lost. The formatters are public API that can be used with any handler, where the exception propagated out of the logging call of the application. Both escaping utilities now handle a null input as an empty string and the handling is documented in their Javadoc. Records with no message are formatted with an empty message, consistently in all three formatters. Added regression tests for the two utilities and for each formatter. --- java/org/apache/juli/JsonFormatter.java | 5 ++- java/org/apache/juli/LogUtil.java | 5 ++- test/org/apache/juli/TestJdkLoggerFormatter.java | 45 ++++++++++++++++++++++++ test/org/apache/juli/TestJsonFormatter.java | 16 +++++++++ test/org/apache/juli/TestLogUtil.java | 6 ++++ test/org/apache/juli/TestOneLineFormatter.java | 44 +++++++++++++++++++++++ 6 files changed, 119 insertions(+), 2 deletions(-) diff --git a/java/org/apache/juli/JsonFormatter.java b/java/org/apache/juli/JsonFormatter.java index 7e4e50284d..33e3d03b65 100644 --- a/java/org/apache/juli/JsonFormatter.java +++ b/java/org/apache/juli/JsonFormatter.java @@ -139,11 +139,14 @@ public class JsonFormatter extends OneLineFormatter { /** * Escape the given string. * - * @param input the string + * @param input the string. A null value is handled as an empty string. * * @return the escaped string */ public static String escape(String input) { + if (input == null) { + return ""; + } return escape(input, 0, input.length()).toString(); } diff --git a/java/org/apache/juli/LogUtil.java b/java/org/apache/juli/LogUtil.java index 6f14afc916..f0458baab6 100644 --- a/java/org/apache/juli/LogUtil.java +++ b/java/org/apache/juli/LogUtil.java @@ -33,12 +33,15 @@ public class LogUtil { * All control characters are escaped apart from horizontal tab (\\u0009), new line (\\u000a) and carriage return * (\\u000d). * - * @param input The string to escape + * @param input The string to escape. A null value is handled as an empty string. * * @return The escaped form of the input string */ @SuppressWarnings("null") // sb is not null when used public static String escape(final String input) { + if (input == null) { + return ""; + } final int len = input.length(); int i = 0; int lastControl = -1; diff --git a/test/org/apache/juli/TestJdkLoggerFormatter.java b/test/org/apache/juli/TestJdkLoggerFormatter.java new file mode 100644 index 0000000000..06de6f8a00 --- /dev/null +++ b/test/org/apache/juli/TestJdkLoggerFormatter.java @@ -0,0 +1,45 @@ +/* + * 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 java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.Assert; +import org.junit.Test; + +public class TestJdkLoggerFormatter { + + /* + * A record with no message is legal. The formatter must render it, with the message as an empty string, rather + * than throw a NullPointerException. + */ + @Test + public void testNullMessage() { + JdkLoggerFormatter formatter = new JdkLoggerFormatter(); + LogRecord logRecord = new LogRecord(Level.FINE, null); + logRecord.setLoggerName("test"); + + String result = formatter.format(logRecord); + + // With an empty message the line has nothing after the padding of the logger name, in particular the + // string "null" must not appear in place of the message + Assert.assertTrue("Unexpected result [" + result + "]", + result.endsWith(System.lineSeparator())); + Assert.assertFalse("Unexpected result [" + result + "]", result.contains("null")); + } +} diff --git a/test/org/apache/juli/TestJsonFormatter.java b/test/org/apache/juli/TestJsonFormatter.java index c82f1c260e..8b52ee2afe 100644 --- a/test/org/apache/juli/TestJsonFormatter.java +++ b/test/org/apache/juli/TestJsonFormatter.java @@ -55,6 +55,22 @@ public class TestJsonFormatter { Assert.assertEquals(trace.get(0), "java.lang.IllegalStateException: Bad state"); } + @Test + public void testNullMessage() throws Exception { + + Formatter formatter = new JsonFormatter(); + LogRecord logRecord = new LogRecord(Level.FINE, null); + logRecord.setLoggerName("test"); + + String result = formatter.format(logRecord); + + // Verify JSON content + Assert.assertTrue(result.startsWith("{")); + JSONParser parser = new JSONParser(new StringReader(result)); + LinkedHashMap<String,Object> json = parser.object(); + Assert.assertEquals("", json.get("message")); + } + @Test public void testCircularCause() throws Exception { diff --git a/test/org/apache/juli/TestLogUtil.java b/test/org/apache/juli/TestLogUtil.java index 12360c2212..7296092896 100644 --- a/test/org/apache/juli/TestLogUtil.java +++ b/test/org/apache/juli/TestLogUtil.java @@ -21,6 +21,12 @@ import org.junit.Test; public class TestLogUtil { + @Test + public void testEscapeForLoggingNull() { + doTestEscapeForLogging(null, ""); + } + + @Test public void testEscapeForLoggingEmptyString() { doTestEscapeForLogging(""); diff --git a/test/org/apache/juli/TestOneLineFormatter.java b/test/org/apache/juli/TestOneLineFormatter.java new file mode 100644 index 0000000000..2b093d8ed5 --- /dev/null +++ b/test/org/apache/juli/TestOneLineFormatter.java @@ -0,0 +1,44 @@ +/* + * 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 java.util.logging.Level; +import java.util.logging.LogRecord; + +import org.junit.Assert; +import org.junit.Test; + +public class TestOneLineFormatter { + + /* + * A record with no message is legal. The formatter must render it, with the message as an empty string, rather + * than throw a NullPointerException. + */ + @Test + public void testNullMessage() { + OneLineFormatter formatter = new OneLineFormatter(); + LogRecord logRecord = new LogRecord(Level.FINE, null); + logRecord.setLoggerName("test"); + + String result = formatter.format(logRecord); + + // With an empty message the output ends with the separator between the source and the message followed by + // the line separator for the next record + Assert.assertTrue("Unexpected result [" + result + "]", + result.endsWith(" " + System.lineSeparator())); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
