diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java index b70a4c5e087..d1aaf8db0f7 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleLoggerContext.java @@ -17,11 +17,10 @@ package org.apache.logging.log4j.simple; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.PrintStream; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.message.MessageFactory; +import org.apache.logging.log4j.simple.internal.SimpleProvider; import org.apache.logging.log4j.spi.AbstractLogger; import org.apache.logging.log4j.spi.ExtendedLogger; import org.apache.logging.log4j.spi.LoggerContext; @@ -36,10 +35,6 @@ public class SimpleLoggerContext implements LoggerContext { /** Singleton instance. */ static final SimpleLoggerContext INSTANCE = new SimpleLoggerContext(); - private static final String SYSTEM_OUT = "system.out"; - - private static final String SYSTEM_ERR = "system.err"; - /** The default format to use when formatting dates */ protected static final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz"; @@ -79,34 +74,15 @@ public class SimpleLoggerContext implements LoggerContext { value = "PATH_TRAVERSAL_OUT", justification = "Opens a file retrieved from configuration (Log4j properties)") public SimpleLoggerContext() { - props = new PropertiesUtil("log4j2.simplelog.properties"); - - showContextMap = props.getBooleanProperty(SYSTEM_PREFIX + "showContextMap", false); - showLogName = props.getBooleanProperty(SYSTEM_PREFIX + "showlogname", false); - showShortName = props.getBooleanProperty(SYSTEM_PREFIX + "showShortLogname", true); - showDateTime = props.getBooleanProperty(SYSTEM_PREFIX + "showdatetime", false); - final String lvl = props.getStringProperty(SYSTEM_PREFIX + "level"); - defaultLevel = Level.toLevel(lvl, Level.ERROR); - - dateTimeFormat = showDateTime - ? props.getStringProperty( - SimpleLoggerContext.SYSTEM_PREFIX + "dateTimeFormat", DEFAULT_DATE_TIME_FORMAT) - : null; - - final String fileName = props.getStringProperty(SYSTEM_PREFIX + "logFile", SYSTEM_ERR); - PrintStream ps; - if (SYSTEM_ERR.equalsIgnoreCase(fileName)) { - ps = System.err; - } else if (SYSTEM_OUT.equalsIgnoreCase(fileName)) { - ps = System.out; - } else { - try { - ps = new PrintStream(new FileOutputStream(fileName)); - } catch (final FileNotFoundException fnfe) { - ps = System.err; - } - } - this.stream = ps; + final SimpleProvider.Config config = SimpleProvider.Config.INSTANCE; + props = config.props; + showContextMap = config.showContextMap; + showLogName = config.showLogName; + showShortName = config.showShortName; + showDateTime = config.showDateTime; + defaultLevel = config.defaultLevel; + dateTimeFormat = config.dateTimeFormat; + stream = config.stream; } @Override diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/simple/internal/SimpleProvider.java b/log4j-api/src/main/java/org/apache/logging/log4j/simple/internal/SimpleProvider.java new file mode 100644 index 00000000000..e62e2a5c27f --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/simple/internal/SimpleProvider.java @@ -0,0 +1,119 @@ +/* + * 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.simple.internal; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.simple.SimpleLoggerContext; +import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; +import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.spi.NoOpThreadContextMap; +import org.apache.logging.log4j.spi.Provider; +import org.apache.logging.log4j.spi.ThreadContextMap; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * A {@link Provider} implementation to use {@link SimpleLoggerContext}. + * + * @since 2.24.0 + */ +@NullMarked +public final class SimpleProvider extends Provider { + + private final ThreadContextMap threadContextMap; + + public SimpleProvider() { + super(null, CURRENT_VERSION); + this.threadContextMap = + Config.INSTANCE.showContextMap ? super.getThreadContextMapInstance() : NoOpThreadContextMap.INSTANCE; + } + + @Override + public LoggerContextFactory getLoggerContextFactory() { + return SimpleLoggerContextFactory.INSTANCE; + } + + @Override + public ThreadContextMap getThreadContextMapInstance() { + return threadContextMap; + } + + public static final class Config { + + /** The default format to use when formatting dates */ + private static final String DEFAULT_DATE_TIME_FORMAT = "yyyy/MM/dd HH:mm:ss:SSS zzz"; + + /** All system properties used by SimpleLog start with this */ + private static final String SYSTEM_PREFIX = "org.apache.logging.log4j.simplelog."; + + private static final String SYSTEM_OUT = "system.out"; + + private static final String SYSTEM_ERR = "system.err"; + + public static final Config INSTANCE = new Config(); + + public final PropertiesUtil props; + + public final boolean showContextMap; + + public final boolean showLogName; + + public final boolean showShortName; + + public final boolean showDateTime; + + public final Level defaultLevel; + + public final @Nullable String dateTimeFormat; + + public final PrintStream stream; + + private Config() { + props = new PropertiesUtil("log4j2.simplelog.properties"); + + showContextMap = props.getBooleanProperty(SYSTEM_PREFIX + "showContextMap", false); + showLogName = props.getBooleanProperty(SYSTEM_PREFIX + "showlogname", false); + showShortName = props.getBooleanProperty(SYSTEM_PREFIX + "showShortLogname", true); + showDateTime = props.getBooleanProperty(SYSTEM_PREFIX + "showdatetime", false); + final String lvl = props.getStringProperty(SYSTEM_PREFIX + "level"); + defaultLevel = Level.toLevel(lvl, Level.ERROR); + + dateTimeFormat = showDateTime + ? props.getStringProperty(SYSTEM_PREFIX + "dateTimeFormat", DEFAULT_DATE_TIME_FORMAT) + : null; + + final String fileName = props.getStringProperty(SYSTEM_PREFIX + "logFile", SYSTEM_ERR); + PrintStream ps; + if (SYSTEM_ERR.equalsIgnoreCase(fileName)) { + ps = System.err; + } else if (SYSTEM_OUT.equalsIgnoreCase(fileName)) { + ps = System.out; + } else { + try { + ps = new PrintStream(new FileOutputStream(fileName)); + } catch (final FileNotFoundException fnfe) { + ps = System.err; + } + } + this.stream = ps; + } + } +} diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java b/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java index 15801ad9d7a..1481df9462e 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/simple/package-info.java @@ -20,7 +20,7 @@ * Providers are able to be loaded at runtime. */ @Export -@Version("2.20.2") +@Version("2.24.0") package org.apache.logging.log4j.simple; import org.osgi.annotation.bundle.Export; diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java index c02526b4a0e..2b74606f081 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderUtil.java @@ -16,7 +16,6 @@ */ package org.apache.logging.log4j.util; -import static org.apache.logging.log4j.LogManager.FACTORY_PROPERTY_NAME; import static org.apache.logging.log4j.spi.Provider.PROVIDER_PROPERTY_NAME; import aQute.bnd.annotation.Cardinality; @@ -34,10 +33,10 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.simple.SimpleLoggerContextFactory; +import org.apache.logging.log4j.simple.internal.SimpleProvider; import org.apache.logging.log4j.spi.LoggerContextFactory; -import org.apache.logging.log4j.spi.NoOpThreadContextMap; import org.apache.logging.log4j.spi.Provider; import org.apache.logging.log4j.status.StatusLogger; @@ -72,19 +71,17 @@ public final class ProviderUtil { */ static final Lock STARTUP_LOCK = new ReentrantLock(); - private static final String API_VERSION = "Log4jAPIVersion"; private static final String[] COMPATIBLE_API_VERSIONS = {"2.6.0"}; private static final Logger LOGGER = StatusLogger.getLogger(); private static volatile Provider PROVIDER; - private static final Provider FALLBACK_PROVIDER = new SimpleProvider(); private ProviderUtil() {} static void addProvider(final Provider provider) { if (validVersion(provider.getVersions())) { PROVIDERS.add(provider); - LOGGER.debug("Loaded Provider {}", provider); + LOGGER.debug("Loaded provider:\n{}", provider); } else { LOGGER.warn("Ignoring provider for incompatible version {}:\n{}", provider.getVersions(), provider); } @@ -100,6 +97,7 @@ static void addProvider(final Provider provider) { @SuppressFBWarnings( value = "URLCONNECTION_SSRF_FD", justification = "Uses a fixed URL that ends in 'META-INF/log4j-provider.properties'.") + @SuppressWarnings("deprecation") static void loadProvider(final URL url, final ClassLoader cl) { try { final Properties props = PropertiesUtil.loadClose(url.openStream(), url); @@ -178,32 +176,37 @@ static void lazyInit() { /** * Used to test the public {@link #getProvider()} method. */ + @SuppressWarnings("deprecation") static Provider selectProvider( final PropertiesUtil properties, final Collection providers, final Logger statusLogger) { Provider selected = null; // 1. Select provider using "log4j.provider" property final String providerClass = properties.getStringProperty(PROVIDER_PROPERTY_NAME); if (providerClass != null) { - try { - selected = LoaderUtil.newInstanceOf(providerClass); - } catch (final Exception e) { - statusLogger.error( - "Unable to create provider {}.\nFalling back to default selection process.", PROVIDER, e); + if (SimpleProvider.class.getName().equals(providerClass)) { + selected = new SimpleProvider(); + } else { + try { + selected = LoaderUtil.newInstanceOf(providerClass); + } catch (final Exception e) { + statusLogger.error( + "Unable to create provider {}.\nFalling back to default selection process.", PROVIDER, e); + } } } // 2. Use deprecated "log4j2.loggerContextFactory" property to choose the provider - final String factoryClassName = properties.getStringProperty(FACTORY_PROPERTY_NAME); + final String factoryClassName = properties.getStringProperty(LogManager.FACTORY_PROPERTY_NAME); if (factoryClassName != null) { if (selected != null) { statusLogger.warn( "Ignoring {} system property, since {} was set.", - FACTORY_PROPERTY_NAME, + LogManager.FACTORY_PROPERTY_NAME, PROVIDER_PROPERTY_NAME); // 2a. Scan the known providers for one matching the logger context factory class name. } else { statusLogger.warn( "Usage of the {} property is deprecated. Use the {} property instead.", - FACTORY_PROPERTY_NAME, + LogManager.FACTORY_PROPERTY_NAME, PROVIDER_PROPERTY_NAME); for (final Provider provider : providers) { if (factoryClassName.equals(provider.getClassName())) { @@ -225,14 +228,14 @@ static Provider selectProvider( statusLogger.error( "Class {} specified in the {} system property does not extend {}", factoryClassName, - FACTORY_PROPERTY_NAME, + LogManager.FACTORY_PROPERTY_NAME, LoggerContextFactory.class.getName()); } } catch (final Exception e) { statusLogger.error( "Unable to create class {} specified in the {} system property", factoryClassName, - FACTORY_PROPERTY_NAME, + LogManager.FACTORY_PROPERTY_NAME, e); } } @@ -253,7 +256,7 @@ static Provider selectProvider( .collect(Collectors.joining("\n", "Log4j API found multiple logging providers:\n", ""))); break; } - selected = providers.stream().max(comparator).orElse(FALLBACK_PROVIDER); + selected = providers.stream().max(comparator).orElseGet(SimpleProvider::new); } statusLogger.info("Using provider:\n{}", selected); return selected; @@ -271,10 +274,4 @@ private static boolean validVersion(final String version) { } return false; } - - private static final class SimpleProvider extends Provider { - private SimpleProvider() { - super(null, CURRENT_VERSION, SimpleLoggerContextFactory.class, NoOpThreadContextMap.class); - } - } }