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 ba5e77835dfdba885277fa4f3dc099f93736eb21 Author: Ralph Goers <[email protected]> AuthorDate: Fri Mar 29 07:49:49 2024 -0700 ResourceLogger uses ScopedContext --- .../org/apache/logging/log4j/test/TestLogger.java | 10 +++++-- .../apache/logging/log4j/ResourceLoggerTest.java | 4 +-- .../org/apache/logging/log4j/ResourceLogger.java | 35 +++++++++++++++++----- .../org/apache/logging/log4j/ScopedContext.java | 10 +++---- .../apache/logging/log4j/ResourceLoggerTest.java | 30 ++++++++++--------- src/site/asciidoc/manual/resource-logger.adoc | 7 ++--- 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java index ff9c6f01c0..d90258e5a2 100644 --- a/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java +++ b/log4j-api-test/src/main/java/org/apache/logging/log4j/test/TestLogger.java @@ -20,10 +20,12 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.ScopedContext; import org.apache.logging.log4j.ThreadContext; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.MessageFactory; @@ -80,14 +82,18 @@ public class TestLogger extends AbstractLogger { sb.append(' '); } sb.append(message.getFormattedMessage()); - final Map<String, String> mdc = ThreadContext.getImmutableContext(); + Map<String, ScopedContext.Renderable> contextMap = ScopedContext.getContextMap(); + final Map<String, String> mdc = new HashMap<>(ThreadContext.getImmutableContext()); + if (contextMap != null && !contextMap.isEmpty()) { + contextMap.forEach((key, value) -> mdc.put(key, value.render())); + } if (!mdc.isEmpty()) { sb.append(' '); sb.append(mdc); sb.append(' '); } if (message instanceof ParameterizedMapMessage) { - sb.append(" Resource data: "); + sb.append(" Map data: "); sb.append(((ParameterizedMapMessage) message).getData().toString()); sb.append(' '); } diff --git a/log4j-api-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java b/log4j-api-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java index f706b0a7dd..c9a8ae691e 100644 --- a/log4j-api-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java +++ b/log4j-api-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java @@ -78,7 +78,7 @@ public class ResourceLoggerTest { events.clear(); } - private static class MapSupplier implements Supplier<Map<String, String>> { + private static class MapSupplier implements Supplier<Map<String, ?>> { private final Connection connection; @@ -87,7 +87,7 @@ public class ResourceLoggerTest { } @Override - public Map<String, String> get() { + public Map<String, ?> get() { Map<String, String> map = new HashMap<>(); map.put("Name", connection.name); map.put("Type", connection.type); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/ResourceLogger.java b/log4j-api/src/main/java/org/apache/logging/log4j/ResourceLogger.java index bac943751c..6df94e9334 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/ResourceLogger.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/ResourceLogger.java @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.Map; import java.util.function.Supplier; import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.ParameterizedMapMessageFactory; +import org.apache.logging.log4j.message.MessageFactory; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.status.StatusLogger; @@ -40,6 +40,7 @@ import org.apache.logging.log4j.util.Strings; public final class ResourceLogger extends AbstractLogger { private static final long serialVersionUID = -5837924138744974513L; private final ExtendedLogger logger; + private final Supplier<Map<String, ?>> supplier; public static ResourceLoggerBuilder newBuilder() { return new ResourceLoggerBuilder(); @@ -49,9 +50,11 @@ public final class ResourceLogger extends AbstractLogger { * Pass our MessageFactory with its Supplier to AbstractLogger. This will be used to create * the Messages prior to them being passed to the "real" Logger. */ - private ResourceLogger(final ExtendedLogger logger, final Supplier<Map<String, String>> supplier) { - super(logger.getName(), new ParameterizedMapMessageFactory(supplier)); + private ResourceLogger( + final ExtendedLogger logger, final Supplier<Map<String, ?>> supplier, MessageFactory messageFactory) { + super(logger.getName(), messageFactory); this.logger = logger; + this.supplier = supplier; } @Override @@ -197,7 +200,11 @@ public final class ResourceLogger extends AbstractLogger { @Override public void logMessage(String fqcn, Level level, Marker marker, Message message, Throwable t) { - logger.logMessage(fqcn, level, marker, message, t); + if (supplier != null) { + ScopedContext.runWhere(supplier.get(), () -> logger.logMessage(fqcn, level, marker, message, t)); + } else { + logger.logMessage(fqcn, level, marker, message, t); + } } /** @@ -207,7 +214,8 @@ public final class ResourceLogger extends AbstractLogger { private static final Logger LOGGER = StatusLogger.getLogger(); private ExtendedLogger logger; private String name; - private Supplier<Map<String, String>> supplier; + private Supplier<Map<String, ?>> supplier; + private MessageFactory messageFactory; /** * Create the builder. @@ -252,11 +260,22 @@ public final class ResourceLogger extends AbstractLogger { * @param supplier the method that provides the Map of resource data to include in logs. * @return the ResourceLoggerBuilder. */ - public ResourceLoggerBuilder withSupplier(Supplier<Map<String, String>> supplier) { + public ResourceLoggerBuilder withSupplier(Supplier<Map<String, ?>> supplier) { this.supplier = supplier; return this; } + /** + * Adds a MessageFactory. + * @param messageFactory the MessageFactory to use to build messages. If a MessageFactory + * is not specified the default MessageFactory will be used. + * @return the ResourceLoggerBuilder. + */ + public ResourceLoggerBuilder withMessageFactory(MessageFactory messageFactory) { + this.messageFactory = messageFactory; + return this; + } + /** * Construct the ResourceLogger. * @return the ResourceLogger. @@ -269,8 +288,8 @@ public final class ResourceLogger extends AbstractLogger { } this.logger = (ExtendedLogger) LogManager.getLogger(name); } - Supplier<Map<String, String>> mapSupplier = this.supplier != null ? this.supplier : Collections::emptyMap; - return new ResourceLogger(logger, mapSupplier); + Supplier<Map<String, ?>> mapSupplier = this.supplier != null ? this.supplier : Collections::emptyMap; + return new ResourceLogger(logger, mapSupplier, messageFactory); } } } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/ScopedContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/ScopedContext.java index 0806f820c5..b105e3be1d 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/ScopedContext.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/ScopedContext.java @@ -124,7 +124,7 @@ public class ScopedContext { * @param map the Map. * @return the ScopedContext Instance constructed. */ - public static Instance where(Map<String, Object> map) { + public static Instance where(Map<String, ?> map) { if (map != null && !map.isEmpty()) { Map<String, Renderable> renderableMap = new HashMap<>(); if (current().isPresent()) { @@ -212,11 +212,11 @@ public class ScopedContext { * @param map the Map. * @param op the Runnable to call. */ - public static void runWhere(Map<String, Object> map, Runnable op) { + public static void runWhere(Map<String, ?> map, Runnable op) { if (map != null && !map.isEmpty()) { Map<String, Renderable> renderableMap = new HashMap<>(); if (current().isPresent()) { - map.putAll(current().get().contextMap); + renderableMap.putAll(current().get().contextMap); } map.forEach((key, value) -> { renderableMap.put(key, value instanceof Renderable ? (Renderable) value : new ObjectRenderable(value)); @@ -296,11 +296,11 @@ public class ScopedContext { * @param map the Map. * @param op the Runnable to call. */ - public static <R> R callWhere(Map<String, Object> map, Callable<R> op) throws Exception { + public static <R> R callWhere(Map<String, ?> map, Callable<R> op) throws Exception { if (map != null && !map.isEmpty()) { Map<String, Renderable> renderableMap = new HashMap<>(); if (current().isPresent()) { - map.putAll(current().get().contextMap); + renderableMap.putAll(current().get().contextMap); } map.forEach((key, value) -> { renderableMap.put(key, value instanceof Renderable ? (Renderable) value : new ObjectRenderable(value)); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java index b62784b4c7..a66f4459ac 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java @@ -18,9 +18,10 @@ package org.apache.logging.log4j.message; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.HashMap; import java.util.List; @@ -33,6 +34,7 @@ import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.core.test.junit.LoggerContextSource; import org.apache.logging.log4j.core.test.junit.Named; +import org.apache.logging.log4j.util.ReadOnlyStringMap; import org.junit.jupiter.api.Test; /** @@ -59,14 +61,14 @@ public class ResourceLoggerTest { logger.debug("Hello, {}", "World"); List<LogEvent> events = app.getEvents(); assertThat(events, hasSize(1)); - Message message = events.get(0).getMessage(); - assertTrue(message instanceof ParameterizedMapMessage); - Map<String, String> data = ((ParameterizedMapMessage) message).getData(); - assertThat(data, aMapWithSize(3)); + ReadOnlyStringMap map = events.get(0).getContextData(); + assertNotNull(map); + Map<String, String> data = map.toMap(); + assertThat(data.size(), equalTo(3)); assertEquals("Test", data.get("Name")); assertEquals("dummy", data.get("Type")); assertEquals("1", data.get("Count")); - assertEquals("Hello, World", message.getFormattedMessage()); + assertEquals("Hello, World", events.get(0).getMessage().getFormattedMessage()); assertEquals(this.getClass().getName(), events.get(0).getLoggerName()); assertEquals(this.getClass().getName(), events.get(0).getSource().getClassName()); app.clear(); @@ -74,9 +76,9 @@ public class ResourceLoggerTest { logger.debug("Used the connection"); events = app.getEvents(); assertThat(events, hasSize(1)); - message = events.get(0).getMessage(); - assertTrue(message instanceof ParameterizedMapMessage); - data = ((ParameterizedMapMessage) message).getData(); + map = events.get(0).getContextData(); + assertNotNull(map); + data = map.toMap(); assertThat(data, aMapWithSize(3)); assertEquals("2", data.get("Count")); app.clear(); @@ -87,20 +89,20 @@ public class ResourceLoggerTest { logger.debug("Connection: {}", "NewConnection"); events = app.getEvents(); assertThat(events, hasSize(1)); - message = events.get(0).getMessage(); - assertTrue(message instanceof ParameterizedMapMessage); - data = ((ParameterizedMapMessage) message).getData(); + map = events.get(0).getContextData(); + assertNotNull(map); + data = map.toMap(); assertThat(data, aMapWithSize(3)); assertEquals("NewConnection", data.get("Name")); assertEquals("fiber", data.get("Type")); assertEquals("1", data.get("Count")); - assertEquals("Connection: NewConnection", message.getFormattedMessage()); + assertEquals("Connection: NewConnection", events.get(0).getMessage().getFormattedMessage()); assertEquals(this.getClass().getName(), events.get(0).getLoggerName()); assertEquals(this.getClass().getName(), events.get(0).getSource().getClassName()); app.clear(); } - private static class MapSupplier implements Supplier<Map<String, String>> { + private static class MapSupplier implements Supplier<Map<String, ?>> { private final Connection connection; diff --git a/src/site/asciidoc/manual/resource-logger.adoc b/src/site/asciidoc/manual/resource-logger.adoc index 615e6f6e92..289b69a344 100644 --- a/src/site/asciidoc/manual/resource-logger.adoc +++ b/src/site/asciidoc/manual/resource-logger.adoc @@ -78,7 +78,7 @@ inside the Resource Logger. With the PatternLayout configured with a pattern of ---- -%K %m%n +%X %m%n ---- and a loginId of testUser and a role of Admin, after a successful login would result in a log message of @@ -87,7 +87,4 @@ and a loginId of testUser and a role of Admin, after a successful login would re {LoginId=testUser, Role=Admin, Count=1} Login succeeded ---- -ResourceLoggers always create ParameterizedMapMessages for every log event. A ParameterizedMapMessage is similar to a ParameterizedMessage but with a Map attached. Since ParameterizedMapMessage is a MapMessage all the tooling available -in Layouts, Filters, and Lookups may be used. - -The supplier configured on the ResourceLogger is called when generating every log event. This allows values, such as counters, to be updated and the log event will contain the actual value at the time the event was logged. +Every logging call is wrapped in a ScopedContext and populated by the supplier configured on the ResourceLogger, which is called when generating every log event. This allows values, such as counters, to be updated and the log event will contain the actual value at the time the event was logged.
