You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by pk...@apache.org on 2024/04/04 21:50:49 UTC
(logging-log4j2) 11/14: Revert unit test change. Correct site changes
This is an automated email from the ASF dual-hosted git repository.
pkarwasz pushed a commit to branch ScopedContext-replace-with-interface
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit e972b5c72341ca2abf1f13d4954335ceef06fa09
Author: Ralph Goers <rg...@apache.org>
AuthorDate: Thu Apr 4 09:00:22 2024 -0700
Revert unit test change. Correct site changes
---
.../log4j/message/ParameterizedMapMessageTest.java | 77 ------
.../log4j/message/ParameterizedMessageTest.java | 303 +++++++++++++++++++++
src/site/asciidoc/manual/extending.adoc | 10 +-
3 files changed, 308 insertions(+), 82 deletions(-)
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMapMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMapMessageTest.java
deleted file mode 100644
index a560570846..0000000000
--- a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMapMessageTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * 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.logging.log4j.message;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.logging.log4j.test.ListStatusListener;
-import org.apache.logging.log4j.test.junit.UsingStatusListener;
-import org.junit.jupiter.api.Test;
-
-@UsingStatusListener
-class ParameterizedMapMessageTest {
-
- final ListStatusListener statusListener;
-
- ParameterizedMapMessageTest(ListStatusListener statusListener) {
- this.statusListener = statusListener;
- }
-
- @Test
- void testNoArgs() {
- final String testMsg = "Test message {}";
- ParameterizedMessage msg = new ParameterizedMessage(testMsg, (Object[]) null);
- String result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- final Object[] array = null;
- msg = new ParameterizedMessage(testMsg, array, null);
- result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- }
-
- @Test
- void testZeroLength() {
- final String testMsg = "";
- ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
- String result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- final Object[] array = null;
- msg = new ParameterizedMessage(testMsg, array, null);
- result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- }
-
- @Test
- void testOneCharLength() {
- final String testMsg = "d";
- ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
- String result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- final Object[] array = null;
- msg = new ParameterizedMessage(testMsg, array, null);
- result = msg.getFormattedMessage();
- assertThat(result).isEqualTo(testMsg);
- }
-
- @Test
- void testFormat3StringArgs() {
- final String testMsg = "Test message {}{} {}";
- final String[] args = {"a", "b", "c"};
- final String result = ParameterizedMessage.format(testMsg, args);
- assertThat(result).isEqualTo("Test message ab c");
- }
-}
diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
new file mode 100644
index 0000000000..4bd5df91be
--- /dev/null
+++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/message/ParameterizedMessageTest.java
@@ -0,0 +1,303 @@
+/*
+ * 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.logging.log4j.message;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.status.StatusData;
+import org.apache.logging.log4j.test.ListStatusListener;
+import org.apache.logging.log4j.test.junit.Mutable;
+import org.apache.logging.log4j.test.junit.SerialUtil;
+import org.apache.logging.log4j.test.junit.UsingStatusListener;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+@UsingStatusListener
+class ParameterizedMessageTest {
+
+ final ListStatusListener statusListener;
+
+ ParameterizedMessageTest(ListStatusListener statusListener) {
+ this.statusListener = statusListener;
+ }
+
+ @Test
+ void testNoArgs() {
+ final String testMsg = "Test message {}";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, (Object[]) null);
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testZeroLength() {
+ final String testMsg = "";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testOneCharLength() {
+ final String testMsg = "d";
+ ParameterizedMessage msg = new ParameterizedMessage(testMsg, new Object[] {"arg"});
+ String result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ final Object[] array = null;
+ msg = new ParameterizedMessage(testMsg, array, null);
+ result = msg.getFormattedMessage();
+ assertThat(result).isEqualTo(testMsg);
+ }
+
+ @Test
+ void testFormat3StringArgs() {
+ final String testMsg = "Test message {}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c");
+ }
+
+ @Test
+ void testFormatNullArgs() {
+ final String testMsg = "Test message {} {} {} {} {} {}";
+ final String[] args = {"a", null, "c", null, null, null};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message a null c null null null");
+ }
+
+ @Test
+ void testFormatStringArgsIgnoresSuperfluousArgs() {
+ final String testMsg = "Test message {}{} {}";
+ final String[] args = {"a", "b", "c", "unnecessary", "superfluous"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c");
+ }
+
+ @Test
+ void testFormatStringArgsWithEscape() {
+ final String testMsg = "Test message \\{}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message {}a b");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingEscape() {
+ final String testMsg = "Test message {}{} {}\\";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c\\");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingText() {
+ final String testMsg = "Test message {}{} {}Text";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab cText");
+ }
+
+ @Test
+ void testFormatStringArgsWithTrailingEscapedEscape() {
+ final String testMsg = "Test message {}{} {}\\\\";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message ab c\\");
+ }
+
+ @Test
+ void testFormatStringArgsWithEscapedEscape() {
+ final String testMsg = "Test message \\\\{}{} {}";
+ final String[] args = {"a", "b", "c"};
+ final String result = ParameterizedMessage.format(testMsg, args);
+ assertThat(result).isEqualTo("Test message \\ab c");
+ }
+
+ @Test
+ void testSafeWithMutableParams() { // LOG4J2-763
+ final String testMsg = "Test message {}";
+ final Mutable param = new Mutable().set("abc");
+ final ParameterizedMessage msg = new ParameterizedMessage(testMsg, param);
+
+ // modify parameter before calling msg.getFormattedMessage
+ param.set("XYZ");
+ final String actual = msg.getFormattedMessage();
+ assertThat(actual).isEqualTo("Test message XYZ").as("Should use current param value");
+
+ // modify parameter after calling msg.getFormattedMessage
+ param.set("000");
+ final String after = msg.getFormattedMessage();
+ assertThat(after).isEqualTo("Test message XYZ").as("Should not change after rendered once");
+ }
+
+ static Stream<Object> testSerializable() {
+ @SuppressWarnings("EqualsHashCode")
+ class NonSerializable {
+ @Override
+ public boolean equals(final Object other) {
+ return other instanceof NonSerializable; // a very lenient equals()
+ }
+ }
+ return Stream.of(
+ "World",
+ new NonSerializable(),
+ new BigDecimal("123.456"),
+ // LOG4J2-3680
+ new RuntimeException(),
+ null);
+ }
+
+ @ParameterizedTest
+ @MethodSource
+ void testSerializable(final Object arg) {
+ final Message expected = new ParameterizedMessage("Hello {}!", arg);
+ final Message actual = SerialUtil.deserialize(SerialUtil.serialize(expected));
+ assertThat(actual).isInstanceOf(ParameterizedMessage.class);
+ assertThat(actual.getFormattedMessage()).isEqualTo(expected.getFormattedMessage());
+ }
+
+ /**
+ * In this test cases, constructed the following scenarios: <br>
+ * <p>
+ * 1. The arguments contains an exception, and the count of placeholder is equal to arguments include exception. <br>
+ * 2. The arguments contains an exception, and the count of placeholder is equal to arguments except exception.<br>
+ * All of these should not logged in status logger.
+ * </p>
+ *
+ * @return Streams
+ */
+ static Stream<Object[]> testCasesWithExceptionArgsButNoWarn() {
+ return Stream.of(
+ new Object[] {
+ "with exception {} {}",
+ new Object[] {"a", new RuntimeException()},
+ "with exception a java.lang.RuntimeException"
+ },
+ new Object[] {
+ "with exception {} {}", new Object[] {"a", "b", new RuntimeException()}, "with exception a b"
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("testCasesWithExceptionArgsButNoWarn")
+ void formatToWithExceptionButNoWarn(final String pattern, final Object[] args, final String expected) {
+ final ParameterizedMessage message = new ParameterizedMessage(pattern, args);
+ final StringBuilder buffer = new StringBuilder();
+ message.formatTo(buffer);
+ assertThat(buffer.toString()).isEqualTo(expected);
+ final List<StatusData> statusDataList = statusListener.getStatusData().collect(Collectors.toList());
+ assertThat(statusDataList).hasSize(0);
+ }
+
+ @ParameterizedTest
+ @MethodSource("testCasesWithExceptionArgsButNoWarn")
+ void formatWithExceptionButNoWarn(final String pattern, final Object[] args, final String expected) {
+ final String message = ParameterizedMessage.format(pattern, args);
+ assertThat(message).isEqualTo(expected);
+ final List<StatusData> statusDataList = statusListener.getStatusData().collect(Collectors.toList());
+ assertThat(statusDataList).hasSize(0);
+ }
+
+ /**
+ * In this test cases, constructed the following scenarios: <br>
+ * <p>
+ * 1. The placeholders are greater than the count of arguments. <br>
+ * 2. The placeholders are less than the count of arguments. <br>
+ * 3. The arguments contains an exception, and the placeholder is greater than normal arguments. <br>
+ * 4. The arguments contains an exception, and the placeholder is less than the arguments.<br>
+ * All of these should logged in status logger with WARN level.
+ * </p>
+ *
+ * @return streams
+ */
+ static Stream<Object[]> testCasesForInsufficientFormatArgs() {
+ return Stream.of(
+ new Object[] {"more {} {}", 2, new Object[] {"a"}, "more a {}"},
+ new Object[] {"more {} {} {}", 3, new Object[] {"a"}, "more a {} {}"},
+ new Object[] {"less {}", 1, new Object[] {"a", "b"}, "less a"},
+ new Object[] {"less {} {}", 2, new Object[] {"a", "b", "c"}, "less a b"},
+ new Object[] {
+ "more throwable {} {} {}",
+ 3,
+ new Object[] {"a", new RuntimeException()},
+ "more throwable a java.lang.RuntimeException {}"
+ },
+ new Object[] {
+ "less throwable {}", 1, new Object[] {"a", "b", new RuntimeException()}, "less throwable a"
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("testCasesForInsufficientFormatArgs")
+ void formatToShouldWarnOnInsufficientArgs(
+ final String pattern, final int placeholderCount, final Object[] args, final String expected) {
+ final int argCount = args == null ? 0 : args.length;
+ verifyFormattingFailureOnInsufficientArgs(pattern, placeholderCount, argCount, expected, () -> {
+ final ParameterizedMessage message = new ParameterizedMessage(pattern, args);
+ final StringBuilder buffer = new StringBuilder();
+ message.formatTo(buffer);
+ return buffer.toString();
+ });
+ }
+
+ @ParameterizedTest
+ @MethodSource("testCasesForInsufficientFormatArgs")
+ void formatShouldWarnOnInsufficientArgs(
+ final String pattern, final int placeholderCount, final Object[] args, final String expected) {
+ final int argCount = args == null ? 0 : args.length;
+ verifyFormattingFailureOnInsufficientArgs(
+ pattern, placeholderCount, argCount, expected, () -> ParameterizedMessage.format(pattern, args));
+ }
+
+ private void verifyFormattingFailureOnInsufficientArgs(
+ final String pattern,
+ final int placeholderCount,
+ final int argCount,
+ final String expected,
+ final Supplier<String> formattedMessageSupplier) {
+
+ // Verify the formatted message
+ final String formattedMessage = formattedMessageSupplier.get();
+ assertThat(formattedMessage).isEqualTo(expected);
+
+ // Verify the status logger warn
+ final List<StatusData> statusDataList = statusListener.getStatusData().collect(Collectors.toList());
+ assertThat(statusDataList).hasSize(1);
+ final StatusData statusData = statusDataList.get(0);
+ assertThat(statusData.getLevel()).isEqualTo(Level.WARN);
+ assertThat(statusData.getMessage().getFormattedMessage())
+ .isEqualTo(
+ "found %d argument placeholders, but provided %d for pattern `%s`",
+ placeholderCount, argCount, pattern);
+ assertThat(statusData.getThrowable()).isNull();
+ }
+}
diff --git a/src/site/asciidoc/manual/extending.adoc b/src/site/asciidoc/manual/extending.adoc
index 03e84c26e7..53dd637a7e 100644
--- a/src/site/asciidoc/manual/extending.adoc
+++ b/src/site/asciidoc/manual/extending.adoc
@@ -587,14 +587,14 @@ ListAppender list2 = ListAppender.newBuilder().setName("List1").setEntryPerNewLi
[#Custom_ContextDataProvider]
== Custom ContextDataProvider
-The link:../log4j-api/apidocs/org/apache/logging/log4j/spi/ContextDataProvider.html[`ContextDataProvider`]
-(introduced in Log4j 2.13.2 and moved to log4j-api in 2.24.0) is an interface applications and libraries
+The link:../log4j-core/apidocs/org/apache/logging/log4j/core/util/ContextDataProvider.html[`ContextDataProvider`]
+(introduced in Log4j 2.13.2) is an interface applications and libraries
can use to inject additional key-value pairs into the LogEvent's context data.
Log4j itself adds the ThreadContext data to the LogEvent using
-`org.apache.logging.log4j.spi.ThreadContextDataProvider`. Custom implementations
-should implement the `org.apache.logging.log4j.spi.ContextDataProvider` interface and
+`org.apache.logging.log4j.core.impl.ThreadContextDataProvider`. Custom implementations
+should implement the `org.apache.logging.log4j.core.util.ContextDataProvider` interface and
declare it as a service by defining the implmentation class in a file named
-`META-INF/services/org.apache.logging.log4j.spi.ContextDataProvider`.
+`META-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider`.
== Custom ThreadContextMap implementations