diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternFormatter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternFormatter.java index 8a35a4d44b7..e946b0d2029 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternFormatter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/PatternFormatter.java @@ -22,6 +22,12 @@ * */ public class PatternFormatter { + + /** + * The empty array. + */ + public static final PatternFormatter[] EMPTY_ARRAY = {}; + private final LogEventPatternConverter converter; private final FormattingInfo field; private final boolean skipFormattingInfo; diff --git a/log4j-jul/pom.xml b/log4j-jul/pom.xml index f780d52f450..bf99f999161 100644 --- a/log4j-jul/pom.xml +++ b/log4j-jul/pom.xml @@ -71,6 +71,11 @@ junit test + + org.apache.logging.log4j + log4j-core-test + test + diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java index 9bd92aa45e2..eee946fc8ce 100644 --- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java +++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/ApiLogger.java @@ -50,12 +50,9 @@ public class ApiLogger extends Logger { super(logger.getName(), null); final Level javaLevel = LevelTranslator.toJavaLevel(logger.getLevel()); // "java.util.logging.LoggingPermission" "control" - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Object run() { - ApiLogger.super.setLevel(javaLevel); - return null; - } + AccessController.doPrivileged((PrivilegedAction) () -> { + ApiLogger.super.setLevel(javaLevel); + return null; }); this.logger = new WrappedLogger(logger); } diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/Log4jBridgeHandler.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/Log4jBridgeHandler.java index 4ed1249e880..d5c44ecdc79 100644 --- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/Log4jBridgeHandler.java +++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/Log4jBridgeHandler.java @@ -17,7 +17,6 @@ package org.apache.logging.log4j.jul; // note: NO import of Logger, Level, LogManager to prevent conflicts JUL/log4j - import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.util.Enumeration; import java.util.HashSet; @@ -93,7 +92,7 @@ public class Log4jBridgeHandler extends java.util.logging.Handler implements Con private boolean doDebugOutput = false; private String julSuffixToAppend = null; - private LoggerContext context; + private volatile boolean installAsLevelPropagator = false; /** * Adds a new Log4jBridgeHandler instance to JUL's root logger. @@ -153,14 +152,7 @@ protected void init(boolean debugOutput, String suffixToAppend, boolean propagat } this.julSuffixToAppend = suffixToAppend; - this.context = LoggerContext.getContext(false); - - if (propagateLevels) { - context.addConfigurationStartedListener(this); - propagateLogLevels(context.getConfiguration()); - // note: java.util.logging.LogManager.addPropertyChangeListener() could also - // be set here, but a call of JUL.readConfiguration() will be done on purpose - } + this.installAsLevelPropagator = propagateLevels; SLOGGER.debug( "Log4jBridgeHandler init. with: suffix='{}', lvlProp={}, instance={}", @@ -173,10 +165,7 @@ protected void init(boolean debugOutput, String suffixToAppend, boolean propagat public void close() { // cleanup and remove listener and JUL logger references julLoggerRefs = null; - if (context != null) { - context.removeConfigurationStartedListener(this); - context = null; - } + LoggerContext.getContext(false).removeConfigurationStartedListener(this); if (doDebugOutput) { System.out.println("sysout: Log4jBridgeHandler close(): " + this); } @@ -188,6 +177,23 @@ public void publish(final LogRecord record) { return; } + // Only execute synchronized code if we really have to + if (this.installAsLevelPropagator) { + synchronized (this) { + // Check again to make sure we still have to propagate the levels at this point + if (this.installAsLevelPropagator) { + // no need to close the AutoCloseable ctx here + @SuppressWarnings("resource") + final LoggerContext context = LoggerContext.getContext(false); + context.addConfigurationStartedListener(this); + propagateLogLevels(context.getConfiguration()); + // note: java.util.logging.LogManager.addPropertyChangeListener() could also + // be set here, but a call of JUL.readConfiguration() will be done on purpose + this.installAsLevelPropagator = false; + } + } + } + final org.apache.logging.log4j.Logger log4jLogger = getLog4jLogger(record); final String msg = julFormatter.formatMessage(record); // use JUL's implementation to get real msg /* log4j allows nulls: diff --git a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java index d011edf1d68..fd66a1ac1bf 100644 --- a/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java +++ b/log4j-jul/src/main/java/org/apache/logging/log4j/jul/LogManager.java @@ -49,7 +49,6 @@ public class LogManager extends java.util.logging.LogManager { private final ThreadLocal> recursive = ThreadLocal.withInitial(HashSet::new); public LogManager() { - super(); AbstractLoggerAdapter adapter = null; final String overrideAdaptorClassName = PropertiesUtil.getProperties().getStringProperty(JulPropertyKey.LOGGER_ADAPTER); @@ -99,10 +98,9 @@ public Logger getLogger(final String name) { } finally { activeRequests.remove(name); } - } else { - LOGGER.warn("Recursive call to getLogger for {} ignored.", name); - return new NoOpLogger(name); } + LOGGER.warn("Recursive call to getLogger for {} ignored.", name); + return new NoOpLogger(name); } @Override diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AbstractLoggerTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AbstractLoggerTest.java index 06a8e4577d4..303ff62db33 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AbstractLoggerTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AbstractLoggerTest.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.impl.MementoLogEvent; +import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.jul.ApiLogger; import org.apache.logging.log4j.jul.LevelTranslator; import org.junit.Test; diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java index e4abd8e7dc6..d6c954af23c 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ApiLoggerTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThat; import java.util.logging.Logger; +import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.jul.ApiLoggerAdapter; import org.apache.logging.log4j.jul.JulPropertyKey; import org.apache.logging.log4j.jul.LogManager; diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AsyncLoggerThreadsTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AsyncLoggerThreadsTest.java index d92d88df134..3abe1f97432 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AsyncLoggerThreadsTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/AsyncLoggerThreadsTest.java @@ -23,13 +23,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.core.async.AsyncLoggerContextSelector; import org.apache.logging.log4j.core.impl.Log4jPropertyKey; +import org.apache.logging.log4j.core.test.categories.AsyncLoggers; import org.junit.AfterClass; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; +import org.junit.experimental.categories.Category; -// @Category(AsyncLoggers.class) -@Ignore("https://issues.apache.org/jira/browse/LOG4J2-3523") +@Category(AsyncLoggers.class) public class AsyncLoggerThreadsTest { @BeforeClass diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/BracketInNotInterpolatedMessageTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/BracketInNotInterpolatedMessageTest.java index 129663f5db8..adb106d2a54 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/BracketInNotInterpolatedMessageTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/BracketInNotInterpolatedMessageTest.java @@ -24,6 +24,7 @@ import java.util.logging.LogRecord; import java.util.logging.Logger; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.jul.LogManager; import org.junit.AfterClass; import org.junit.BeforeClass; diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java index ee1055d7d11..ff644a40d35 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CallerInformationTest.java @@ -16,17 +16,16 @@ */ package org.apache.logging.log4j.jul.test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; -import java.net.URI; import java.util.List; import java.util.logging.Logger; -import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.test.appender.ListAppender; +import org.apache.logging.log4j.core.test.junit.LoggerContextRule; import org.apache.logging.log4j.jul.LogManager; -import org.junit.After; import org.junit.AfterClass; -import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; public class CallerInformationTest { @@ -34,8 +33,8 @@ public class CallerInformationTest { // config from log4j-core test-jar private static final String CONFIG = "log4j2-calling-class.xml"; - private LoggerContext ctx; - private ListAppender app; + @Rule + public final LoggerContextRule ctx = new LoggerContextRule(CONFIG); @BeforeClass public static void setUpClass() { @@ -47,27 +46,9 @@ public static void tearDownClass() { System.clearProperty("java.util.logging.manager"); } - @Before - public void beforeEach() throws Exception { - final URI uri = this.getClass().getClassLoader().getResource(CONFIG).toURI(); - ctx = (LoggerContext) org.apache.logging.log4j.LogManager.getContext(null, false, uri); - assertNotNull("No LoggerContext", ctx); - } - - @After - public void afterEach() throws Exception { - if (ctx != null) { - ctx.stop(); - ctx = null; - app = null; - } - } - @Test public void testClassLogger() throws Exception { - app = ctx.getConfiguration().getAppender("Class"); - assertNotNull("No ListAppender", app); - app.clear(); + final ListAppender app = ctx.getListAppender("Class").clear(); final Logger logger = Logger.getLogger("ClassLogger"); logger.info("Ignored message contents."); logger.warning("Verifying the caller class is still correct."); @@ -81,9 +62,7 @@ public void testClassLogger() throws Exception { @Test public void testMethodLogger() throws Exception { - app = ctx.getConfiguration().getAppender("Method"); - assertNotNull("No ListAppender", app); - app.clear(); + final ListAppender app = ctx.getListAppender("Method").clear(); final Logger logger = Logger.getLogger("MethodLogger"); logger.info("More messages."); logger.warning("CATASTROPHE INCOMING!"); diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CoreLoggerTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CoreLoggerTest.java index 2b0096bf0f5..57d63b9372c 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CoreLoggerTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/CoreLoggerTest.java @@ -23,6 +23,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.jul.LogManager; import org.apache.logging.log4j.util.Strings; import org.junit.After; diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ListAppender.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ListAppender.java deleted file mode 100644 index 574576d1f4a..00000000000 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/ListAppender.java +++ /dev/null @@ -1,281 +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.jul.test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import org.apache.logging.log4j.core.Appender; -import org.apache.logging.log4j.core.Filter; -import org.apache.logging.log4j.core.Layout; -import org.apache.logging.log4j.core.LogEvent; -import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.appender.AbstractAppender; -import org.apache.logging.log4j.core.config.Property; -import org.apache.logging.log4j.plugins.Configurable; -import org.apache.logging.log4j.plugins.Plugin; -import org.apache.logging.log4j.plugins.PluginAttribute; -import org.apache.logging.log4j.plugins.PluginElement; -import org.apache.logging.log4j.plugins.PluginFactory; -import org.apache.logging.log4j.plugins.validation.constraints.Required; - -/** - * This appender is primarily used for testing. Use in a real environment is discouraged as the List could eventually - * grow to cause an OutOfMemoryError. - * - * This appender is not thread-safe. - * - * This appender will use {@link Layout#toByteArray(LogEvent)}. - * - */ -@Configurable(elementType = Appender.ELEMENT_TYPE, printObject = true) -@Plugin("List") -public class ListAppender extends AbstractAppender { - - // Use Collections.synchronizedList rather than CopyOnWriteArrayList because we expect - // more frequent writes than reads. - final List events = Collections.synchronizedList(new ArrayList<>()); - - private final List messages = Collections.synchronizedList(new ArrayList<>()); - - final List data = Collections.synchronizedList(new ArrayList<>()); - - private final boolean newLine; - - private final boolean raw; - - private static final String WINDOWS_LINE_SEP = "\r\n"; - - /** - * CountDownLatch for asynchronous logging tests. Example usage: - * - *
-     * @Rule
-     * public LoggerContextRule context = new LoggerContextRule("log4j-list.xml");
-     * private ListAppender listAppender;
-     *
-     * @Before
-     * public void before() throws Exception {
-     *     listAppender = context.getListAppender("List");
-     * }
-     *
-     * @Test
-     * public void testSomething() throws Exception {
-     *     listAppender.countDownLatch = new CountDownLatch(1);
-     *
-     *     Logger logger = LogManager.getLogger();
-     *     logger.info("log one event asynchronously");
-     *
-     *     // wait for the appender to finish processing this event (wait max 1 second)
-     *     listAppender.countDownLatch.await(1, TimeUnit.SECONDS);
-     *
-     *     // now assert something or do follow-up tests...
-     * }
-     * 
- */ - public volatile CountDownLatch countDownLatch = null; - - public ListAppender(final String name) { - super(name, null, null, true, Property.EMPTY_ARRAY); - newLine = false; - raw = false; - } - - public ListAppender( - final String name, final Filter filter, final Layout layout, final boolean newline, final boolean raw) { - super(name, filter, layout, true, Property.EMPTY_ARRAY); - this.newLine = newline; - this.raw = raw; - if (layout != null) { - final byte[] bytes = layout.getHeader(); - if (bytes != null) { - write(bytes); - } - } - } - - @Override - public void append(final LogEvent event) { - final Layout layout = getLayout(); - if (layout == null) { - events.add(event.toImmutable()); - } else { - write(layout.toByteArray(event)); - } - if (countDownLatch != null) { - countDownLatch.countDown(); - } - } - - void write(final byte[] bytes) { - if (raw) { - data.add(bytes); - return; - } - final String str = new String(bytes); - if (newLine) { - int index = 0; - while (index < str.length()) { - int end; - final int wend = str.indexOf(WINDOWS_LINE_SEP, index); - final int lend = str.indexOf('\n', index); - int length; - if (wend >= 0 && wend < lend) { - end = wend; - length = 2; - } else { - end = lend; - length = 1; - } - if (index == end) { - if (!messages.get(messages.size() - length).isEmpty()) { - messages.add(""); - } - } else if (end >= 0) { - messages.add(str.substring(index, end)); - } else { - messages.add(str.substring(index)); - break; - } - index = end + length; - } - } else { - messages.add(str); - } - } - - @Override - public boolean stop(final long timeout, final TimeUnit timeUnit) { - setStopping(); - super.stop(timeout, timeUnit, false); - final Layout layout = getLayout(); - if (layout != null) { - final byte[] bytes = layout.getFooter(); - if (bytes != null) { - write(bytes); - } - } - setStopped(); - return true; - } - - public ListAppender clear() { - events.clear(); - messages.clear(); - data.clear(); - return this; - } - - /** Returns an immutable snapshot of captured log events */ - public List getEvents() { - return Collections.unmodifiableList(new ArrayList<>(events)); - } - - /** Returns an immutable snapshot of captured messages */ - public List getMessages() { - return List.copyOf(messages); - } - - /** - * Polls the messages list for it to grow to a given minimum size at most timeout timeUnits and return a copy of - * what we have so far. - */ - public List getMessages(final int minSize, final long timeout, final TimeUnit timeUnit) - throws InterruptedException { - final long endMillis = System.currentTimeMillis() + timeUnit.toMillis(timeout); - while (messages.size() < minSize && System.currentTimeMillis() < endMillis) { - Thread.sleep(100); - } - return getMessages(); - } - - /** Returns an immutable snapshot of captured data */ - public List getData() { - return List.copyOf(data); - } - - public static ListAppender createAppender( - final String name, final boolean newLine, final boolean raw, final Layout layout, final Filter filter) { - return new ListAppender(name, filter, layout, newLine, raw); - } - - @PluginFactory - public static Builder newBuilder() { - return new Builder(); - } - - public static class Builder implements org.apache.logging.log4j.plugins.util.Builder { - - private String name; - private boolean entryPerNewLine; - private boolean raw; - private Layout layout; - private Filter filter; - - public Builder setName(@Required @PluginAttribute final String name) { - this.name = name; - return this; - } - - public Builder setEntryPerNewLine(@PluginAttribute final boolean entryPerNewLine) { - this.entryPerNewLine = entryPerNewLine; - return this; - } - - public Builder setRaw(@PluginAttribute final boolean raw) { - this.raw = raw; - return this; - } - - public Builder setLayout(@PluginElement final Layout layout) { - this.layout = layout; - return this; - } - - public Builder setFilter(@PluginElement final Filter filter) { - this.filter = filter; - return this; - } - - @Override - public ListAppender build() { - return new ListAppender(name, filter, layout, entryPerNewLine, raw); - } - } - - /** - * Gets the named ListAppender if it has been registered. - * - * @param name - * the name of the ListAppender - * @return the named ListAppender or {@code null} if it does not exist - * @see LoggerContextRule#getListAppender(String) - */ - public static ListAppender getListAppender(final String name) { - return (LoggerContext.getContext(false)).getConfiguration().getAppender(name); - } - - @Override - public String toString() { - return "ListAppender [events=" + events + ", messages=" + messages + ", data=" + data + ", newLine=" + newLine - + ", raw=" + raw + ", countDownLatch=" + countDownLatch + ", getHandler()=" + getHandler() - + ", getLayout()=" + getLayout() + ", getName()=" + getName() + ", ignoreExceptions()=" - + ignoreExceptions() + ", getFilter()=" + getFilter() + ", getState()=" + getState() + "]"; - } -} diff --git a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/Log4jBridgeHandlerTest.java b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/Log4jBridgeHandlerTest.java index 15144c293a2..86425c24125 100644 --- a/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/Log4jBridgeHandlerTest.java +++ b/log4j-jul/src/test/java/org/apache/logging/log4j/jul/test/Log4jBridgeHandlerTest.java @@ -17,7 +17,6 @@ package org.apache.logging.log4j.jul.test; // note: NO import of Logger, Level, LogManager to prevent conflicts JUL/log4j - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -319,6 +318,7 @@ public void test5LevelPropFromConfigFile() { assertLogLevel("log4j.Log4jBridgeHandlerTest.propagate2.nested.deeplyNested", java.util.logging.Level.INFO); // these are set in logging.properties but not in log4j2.xml: assertLogLevel("log4j.Log4jBridgeHandlerTest.propagate2.nested", null); + assertLogLevel("javax.mail", null); // these should not exist: assertLogLevel("log4j.Log4jBridgeHandlerTest", null); assertLogLevel("log4j.Log4jBridgeHandlerTest.propagate1.nested", null); diff --git a/log4j-jul/src/test/resources/logging-test.properties b/log4j-jul/src/test/resources/logging-test.properties index 169e8b2aa4f..9a0a4756c7f 100644 --- a/log4j-jul/src/test/resources/logging-test.properties +++ b/log4j-jul/src/test/resources/logging-test.properties @@ -38,6 +38,8 @@ java.util.logging.SimpleFormatter.format = JUL: %1$tT.%1$tL %4$s [%3$s: %2$s] # out-comment to use default of "INFO" - will be set by level propagation to DEBUG=FINE again #.level = FINE org.apache.logging.log4j.jul.Log4jBridgeHandlerTest.level = FINER +# do not log mail-init. (is done on INFO-level) because this would init. JUL before setErr() happens +javax.mail.level = WARNING # configure (initial) JUL levels differently to log4j-config (and use high levels here) log4j.Log4jBridgeHandlerTest.propagate1.nested1.level = SEVERE diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptManagerImpl.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptManagerImpl.java index 32acc88b15f..566ec2135e9 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptManagerImpl.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptManagerImpl.java @@ -16,13 +16,14 @@ */ package org.apache.logging.log4j.script; +import static org.apache.logging.log4j.util.Strings.toRootLowerCase; + import java.io.File; import java.nio.file.Path; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; -import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -86,7 +87,7 @@ public ScriptManagerImpl(final Configuration configuration, final WatchManager w this.watchManager = watchManager; final List factories = manager.getEngineFactories(); allowedLanguages = Arrays.stream(Strings.splitList(scriptLanguages)) - .map(String::toLowerCase) + .map(Strings::toRootLowerCase) .collect(Collectors.toSet()); if (logger.isDebugEnabled()) { final StringBuilder sb = new StringBuilder(); @@ -100,7 +101,7 @@ public ScriptManagerImpl(final Configuration configuration, final WatchManager w final StringBuilder names = new StringBuilder(); final List languageNames = factory.getNames(); for (final String name : languageNames) { - if (allowedLanguages.contains(name.toLowerCase(Locale.ROOT))) { + if (allowedLanguages.contains(toRootLowerCase(name))) { if (names.length() > 0) { names.append(", "); } @@ -132,7 +133,7 @@ public ScriptManagerImpl(final Configuration configuration, final WatchManager w final StringBuilder names = new StringBuilder(); for (final ScriptEngineFactory factory : factories) { for (final String name : factory.getNames()) { - if (allowedLanguages.contains(name.toLowerCase(Locale.ROOT))) { + if (allowedLanguages.contains(toRootLowerCase(name))) { if (names.length() > 0) { names.append(", "); } @@ -165,7 +166,7 @@ public void addScripts(final Node child) { } public boolean addScript(final Script script) { - if (allowedLanguages.contains(script.getLanguage().toLowerCase(Locale.ROOT))) { + if (allowedLanguages.contains(toRootLowerCase(script.getLanguage()))) { final ScriptEngine engine = manager.getEngineByName(script.getLanguage()); if (engine == null) { logger.error("No ScriptEngine found for language " + script.getLanguage() diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptPlugin.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptPlugin.java index 5ac7a3bcafb..6144f474ad5 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptPlugin.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/ScriptPlugin.java @@ -40,9 +40,9 @@ public ScriptPlugin(final String name, final String language, final String scrip @PluginFactory public static ScriptPlugin createScript( // @formatter:off - @PluginAttribute final String name, - @PluginAttribute String language, - @PluginValue final String scriptText) { + @PluginAttribute("name") final String name, + @PluginAttribute(ATTR_LANGUAGE) String language, + @PluginValue(ATTR_SCRIPT_TEXT) final String scriptText) { // @formatter:on if (language == null) { LOGGER.error("No '{}' attribute provided for {} plugin '{}'", ATTR_LANGUAGE, PLUGIN_NAME, name); diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/ScriptAppenderSelector.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/ScriptAppenderSelector.java index d46f7e3be88..1272c8b6715 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/ScriptAppenderSelector.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/ScriptAppenderSelector.java @@ -17,7 +17,6 @@ package org.apache.logging.log4j.script.appender; import java.util.Objects; -import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.Layout; @@ -65,19 +64,19 @@ public static final class Builder implements org.apache.logging.log4j.plugins.ut @Override public Appender build() { if (name == null) { - AbstractLifeCycle.LOGGER.error("Name missing."); + LOGGER.error("Name missing."); return null; } if (script == null) { - AbstractLifeCycle.LOGGER.error("Script missing for ScriptAppenderSelector appender {}", name); + LOGGER.error("Script missing for ScriptAppenderSelector appender {}", name); return null; } if (appenderSet == null) { - AbstractLifeCycle.LOGGER.error("AppenderSet missing for ScriptAppenderSelector appender {}", name); + LOGGER.error("AppenderSet missing for ScriptAppenderSelector appender {}", name); return null; } if (configuration == null) { - AbstractLifeCycle.LOGGER.error("Configuration missing for ScriptAppenderSelector appender {}", name); + LOGGER.error("Configuration missing for ScriptAppenderSelector appender {}", name); return null; } final ScriptManager scriptManager = configuration.getScriptManager(); @@ -89,7 +88,7 @@ public Appender build() { return null; } final ScriptBindings bindings = scriptManager.createBindings(script); - AbstractLifeCycle.LOGGER.debug( + LOGGER.debug( "ScriptAppenderSelector '{}' executing {} '{}': {}", name, script.getLanguage(), @@ -97,7 +96,7 @@ public Appender build() { script.getScriptText()); final Object object = scriptManager.execute(script.getName(), bindings); final String actualAppenderName = Objects.toString(object, null); - AbstractLifeCycle.LOGGER.debug("ScriptAppenderSelector '{}' selected '{}'", name, actualAppenderName); + LOGGER.debug("ScriptAppenderSelector '{}' selected '{}'", name, actualAppenderName); return appenderSet.createAppender(actualAppenderName, name); } @@ -143,7 +142,8 @@ public static Builder newBuilder() { return new Builder(); } - private ScriptAppenderSelector(final String name, final Filter filter, final Layout layout) { + private ScriptAppenderSelector( + final String name, final Filter filter, final Layout layout, final Property[] properties) { super(name, filter, layout, true, Property.EMPTY_ARRAY); } diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/rolling/action/ScriptCondition.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/rolling/action/ScriptCondition.java index adb1891be48..373682397df 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/rolling/action/ScriptCondition.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/appender/rolling/action/ScriptCondition.java @@ -62,9 +62,9 @@ public ScriptCondition(final Script script, final Configuration configuration) { /** * Executes the script * - * @param baseDir - * @param candidates - * @return + * @param basePath base directory for files to delete + * @param candidates a list of paths, that can be deleted by the script + * @return a list of paths selected to delete by the script execution */ @SuppressWarnings("unchecked") public List selectFilesToDelete( diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/config/arbiter/ScriptArbiter.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/config/arbiter/ScriptArbiter.java index 328fe9949ab..37b57f7c4fa 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/config/arbiter/ScriptArbiter.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/config/arbiter/ScriptArbiter.java @@ -20,14 +20,14 @@ import org.apache.logging.log4j.core.config.AbstractConfiguration; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.arbiters.Arbiter; -import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; -import org.apache.logging.log4j.core.config.plugins.PluginNode; import org.apache.logging.log4j.core.script.Script; import org.apache.logging.log4j.core.script.ScriptBindings; import org.apache.logging.log4j.plugins.Configurable; import org.apache.logging.log4j.plugins.Node; import org.apache.logging.log4j.plugins.Plugin; +import org.apache.logging.log4j.plugins.PluginFactory; +import org.apache.logging.log4j.plugins.PluginNode; import org.apache.logging.log4j.plugins.model.PluginType; import org.apache.logging.log4j.script.ScriptManagerImpl; import org.apache.logging.log4j.script.ScriptRef; @@ -60,7 +60,7 @@ public boolean isCondition() { return Boolean.parseBoolean(object.toString()); } - @PluginBuilderFactory + @PluginFactory public static Builder newBuilder() { return new Builder(); } diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/filter/ScriptFilter.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/filter/ScriptFilter.java index 65f8171f4c6..68734a84359 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/filter/ScriptFilter.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/filter/ScriptFilter.java @@ -18,12 +18,10 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.core.AbstractLifeCycle; import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.config.Configuration; -import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; import org.apache.logging.log4j.core.filter.AbstractFilter; import org.apache.logging.log4j.core.script.Script; import org.apache.logging.log4j.core.script.ScriptBindings; @@ -31,6 +29,7 @@ import org.apache.logging.log4j.message.ObjectMessage; import org.apache.logging.log4j.message.SimpleMessage; import org.apache.logging.log4j.plugins.Configurable; +import org.apache.logging.log4j.plugins.Inject; import org.apache.logging.log4j.plugins.Plugin; import org.apache.logging.log4j.plugins.PluginAttribute; import org.apache.logging.log4j.plugins.PluginElement; @@ -71,7 +70,7 @@ public Result filter( bindings.putAll(configuration.getProperties()); bindings.put("substitutor", configuration.getStrSubstitutor()); final Object object = configuration.getScriptManager().execute(script.getName(), bindings); - return !Boolean.TRUE.equals(object) ? onMismatch : onMatch; + return object == null || !Boolean.TRUE.equals(object) ? onMismatch : onMatch; } @Override @@ -132,15 +131,15 @@ public String toString() { */ // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder @PluginFactory + @Inject public static ScriptFilter createFilter( @PluginElement final Script script, @PluginAttribute final Result onMatch, @PluginAttribute final Result onMismatch, - @PluginConfiguration final Configuration configuration) { + final Configuration configuration) { if (script == null) { - AbstractLifeCycle.LOGGER.error( - "A Script, ScriptFile or ScriptRef element must be provided for this ScriptFilter"); + LOGGER.error("A Script, ScriptFile or ScriptRef element must be provided for this ScriptFilter"); return null; } if (configuration.getScriptManager() == null) { diff --git a/log4j-script/src/main/java/org/apache/logging/log4j/script/layout/ScriptPatternSelector.java b/log4j-script/src/main/java/org/apache/logging/log4j/script/layout/ScriptPatternSelector.java index 379e732db71..b85b4fa40a9 100644 --- a/log4j-script/src/main/java/org/apache/logging/log4j/script/layout/ScriptPatternSelector.java +++ b/log4j-script/src/main/java/org/apache/logging/log4j/script/layout/ScriptPatternSelector.java @@ -106,13 +106,13 @@ public ScriptPatternSelector build() { return null; } return new ScriptPatternSelector( + configuration, script, properties, defaultPattern, alwaysWriteExceptions, disableAnsi, - noConsoleNoAnsi, - configuration); + noConsoleNoAnsi); } public Builder setScript(final AbstractScript script) { @@ -165,13 +165,13 @@ public Builder setConfiguration(final Configuration config) { private final boolean requiresLocation; private ScriptPatternSelector( + final Configuration config, final AbstractScript script, final PatternMatch[] properties, final String defaultPattern, final boolean alwaysWriteExceptions, final boolean disableAnsi, - final boolean noConsoleNoAnsi, - final Configuration config) { + final boolean noConsoleNoAnsi) { this.script = script; this.configuration = config; final PatternParser parser = PatternLayout.createPatternParser(config); @@ -180,7 +180,7 @@ private ScriptPatternSelector( try { final List list = parser.parse(property.getPattern(), alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); - final PatternFormatter[] formatters = list.toArray(new PatternFormatter[list.size()]); + final PatternFormatter[] formatters = list.toArray(PatternFormatter.EMPTY_ARRAY); formatterMap.put(property.getKey(), formatters); patternMap.put(property.getKey(), property.getPattern()); for (int i = 0; !needsLocation && i < formatters.length; ++i) { @@ -193,7 +193,7 @@ private ScriptPatternSelector( try { final List list = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi, noConsoleNoAnsi); - defaultFormatters = list.toArray(new PatternFormatter[list.size()]); + defaultFormatters = list.toArray(PatternFormatter.EMPTY_ARRAY); this.defaultPattern = defaultPattern; for (int i = 0; !needsLocation && i < defaultFormatters.length; ++i) { needsLocation = defaultFormatters[i].requiresLocation(); diff --git a/log4j-slf4j-impl/pom.xml b/log4j-slf4j-impl/pom.xml index 2b973cf73d0..1e5778b989e 100644 --- a/log4j-slf4j-impl/pom.xml +++ b/log4j-slf4j-impl/pom.xml @@ -30,7 +30,21 @@ The Apache Log4j SLF4J API binding to Log4j 2 Core 1.7.36 + false + + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + org.osgi @@ -54,6 +68,12 @@ org.apache.logging.log4j log4j-api-test test + + + org.slf4j + slf4j-api + + org.apache.logging.log4j @@ -64,6 +84,12 @@ org.apache.logging.log4j log4j-to-slf4j test + + + org.slf4j + slf4j-api + + org.apache.commons @@ -75,11 +101,6 @@ commons-lang3 test - - org.hamcrest - hamcrest - test - org.junit.jupiter junit-jupiter-engine @@ -93,6 +114,7 @@ + org.apache.maven.plugins maven-surefire-plugin @@ -129,6 +151,7 @@ + diff --git a/log4j-slf4j-impl/src/assembly/slf4j.xml b/log4j-slf4j-impl/src/assembly/slf4j.xml deleted file mode 100644 index 966f702adaa..00000000000 --- a/log4j-slf4j-impl/src/assembly/slf4j.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - src - - zip - - / - - - ${project.build.outputDirectory} - /classes - - **/*.class - - - org/slf4j/impl/*.class - - - - diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/package-info.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/package-info.java index 511a8c77c3f..d847bc764bb 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/package-info.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/package-info.java @@ -21,7 +21,7 @@ */ @Export @Header(name = Constants.BUNDLE_ACTIVATIONPOLICY, value = Constants.ACTIVATION_LAZY) -@Version("2.21.0") +@Version("3.0.0") package org.apache.logging.slf4j; import org.osgi.annotation.bundle.Export; diff --git a/log4j-slf4j2-impl/pom.xml b/log4j-slf4j2-impl/pom.xml index 9f4530cf3bd..20d5a0b9001 100644 --- a/log4j-slf4j2-impl/pom.xml +++ b/log4j-slf4j2-impl/pom.xml @@ -33,6 +33,7 @@ + false diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java index 0f3eded7a82..e5940be3fe1 100644 --- a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java @@ -64,7 +64,7 @@ Log4jMarkerFactory getMarkerFactory() { private LoggerContext validateContext(final LoggerContext context) { if (TO_SLF4J_CONTEXT.equals(context.getClass().getName())) { - throw new LoggingException("log4j-slf4j-impl cannot be present with log4j-to-slf4j"); + throw new LoggingException("log4j-slf4j2-impl cannot be present with log4j-to-slf4j"); } return context; } diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java index 511a8c77c3f..d847bc764bb 100644 --- a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java @@ -21,7 +21,7 @@ */ @Export @Header(name = Constants.BUNDLE_ACTIVATIONPOLICY, value = Constants.ACTIVATION_LAZY) -@Version("2.21.0") +@Version("3.0.0") package org.apache.logging.slf4j; import org.osgi.annotation.bundle.Export; diff --git a/log4j-to-jul/pom.xml b/log4j-to-jul/pom.xml index 576366f795a..dabbf5da95f 100644 --- a/log4j-to-jul/pom.xml +++ b/log4j-to-jul/pom.xml @@ -29,9 +29,6 @@ Apache Log4j to JUL Bridge The Apache Log4j binding between Log4j 2 API and java.util.logging (JUL). 2022 - - ${basedir}/.. - org.apache.logging.log4j diff --git a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java index 3d391aa2427..11081071574 100644 --- a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java +++ b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java @@ -32,7 +32,7 @@ * * @author Michael Vorburger.ch for Google */ -public final class JULLogger extends AbstractLogger { +final class JULLogger extends AbstractLogger { private static final long serialVersionUID = 1L; private final Logger logger; @@ -162,7 +162,8 @@ private java.util.logging.Level getEffectiveJULLevel() { } // This is a safety fallback that is typically never reached, because usually the root Logger.getLogger("") has // a Level. - return Logger.getGlobal().getLevel(); + // Since JDK 8 the LogManager$RootLogger does not have a default level, just a default effective level of INFO. + return java.util.logging.Level.INFO; } private boolean isEnabledFor(final Level level, final Marker marker) { diff --git a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULProvider.java b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULProvider.java index cc2cb76ee79..1dd41a528d7 100644 --- a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULProvider.java +++ b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULProvider.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.tojul; -import aQute.bnd.annotation.Resolution; import aQute.bnd.annotation.spi.ServiceProvider; import org.apache.logging.log4j.spi.Provider; @@ -25,7 +24,7 @@ * * @author Michael Vorburger.ch for Google */ -@ServiceProvider(value = Provider.class, resolution = Resolution.OPTIONAL) +@ServiceProvider(value = Provider.class) public class JULProvider extends Provider { public JULProvider() { super(20, "3.0.0", JULLoggerContextFactory.class, null); diff --git a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java index f333fffe246..8e3755f4d73 100644 --- a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java +++ b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/package-info.java @@ -21,7 +21,7 @@ * @author Michael Vorburger.ch for Google */ @Export -@Version("2.20.1") +@Version("3.0.0") package org.apache.logging.log4j.tojul; import org.osgi.annotation.bundle.Export; diff --git a/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java new file mode 100644 index 00000000000..9f70a01b28b --- /dev/null +++ b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java @@ -0,0 +1,34 @@ +/* + * 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.tojul; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.logging.log4j.Level; +import org.junit.jupiter.api.Test; + +public class JULLoggerTest { + + @Test + public void testNotNullEffectiveLevel() { + // Emulates the root logger found in Tomcat, with a null level + // See: https://bz.apache.org/bugzilla/show_bug.cgi?id=66184 + final java.util.logging.Logger julLogger = new java.util.logging.Logger("", null) {}; + final JULLogger logger = new JULLogger("", julLogger); + assertEquals(Level.INFO, logger.getLevel()); + } +} diff --git a/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/test/LoggerTest.java b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/LoggerTest.java similarity index 99% rename from log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/test/LoggerTest.java rename to log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/LoggerTest.java index ac6777249dc..c2d65199aae 100644 --- a/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/test/LoggerTest.java +++ b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/LoggerTest.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.logging.log4j.tojul.test; +package org.apache.logging.log4j.tojul; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +24,6 @@ import java.util.logging.Level; import java.util.logging.LogRecord; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.tojul.JULLogger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/src/changelog/.3.x.x/workaround_coursier_bug.xml b/src/changelog/.3.x.x/workaround_coursier_bug.xml new file mode 100644 index 00000000000..7e78be130f6 --- /dev/null +++ b/src/changelog/.3.x.x/workaround_coursier_bug.xml @@ -0,0 +1,10 @@ + + + + + Workaround a Coursier/Ivy dependency resolution bug affecting `log4j-slf4j-impl` and `log4j-mongodb3`. + +