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/08/30 11:33:11 UTC
(logging-log4j2) 01/01: Improve GraalVM support of `SimpleLoggerContext`
This is an automated email from the ASF dual-hosted git repository.
pkarwasz pushed a commit to branch fix/2.x/simple-provider-graalvm
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit ba15860b99b147a00e814f740568905abb7226d6
Author: Piotr P. Karwasz <pi...@karwasz.org>
AuthorDate: Fri Aug 30 13:32:49 2024 +0200
Improve GraalVM support of `SimpleLoggerContext`
This PR allows to create GraalVM applications that only use
this `SimpleLoggerContext`.
For this purpose we create a `SimpleProvider` implementation of `Provider`,
which is **not** instantiated by reflection.
Part of #2830
---
.../logging/log4j/simple/SimpleLoggerContext.java | 43 ++------
.../logging/log4j/simple/SimpleProvider.java | 114 +++++++++++++++++++++
.../apache/logging/log4j/simple/package-info.java | 2 +-
.../apache/logging/log4j/util/ProviderUtil.java | 41 ++++----
4 files changed, 143 insertions(+), 57 deletions(-)
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 b70a4c5e08..57782f04aa 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,8 +17,6 @@
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;
@@ -36,10 +34,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 +73,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/SimpleProvider.java b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleProvider.java
new file mode 100644
index 0000000000..aa617acc35
--- /dev/null
+++ b/log4j-api/src/main/java/org/apache/logging/log4j/simple/SimpleProvider.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+import static org.apache.logging.log4j.simple.SimpleLoggerContext.DEFAULT_DATE_TIME_FORMAT;
+import static org.apache.logging.log4j.simple.SimpleLoggerContext.SYSTEM_PREFIX;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import org.apache.logging.log4j.Level;
+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;
+ }
+
+ static final class Config {
+
+ private static final String SYSTEM_OUT = "system.out";
+
+ private static final String SYSTEM_ERR = "system.err";
+
+ static final Config INSTANCE = new Config();
+
+ final PropertiesUtil props;
+
+ final boolean showContextMap;
+
+ final boolean showLogName;
+
+ final boolean showShortName;
+
+ final boolean showDateTime;
+
+ final Level defaultLevel;
+
+ final @Nullable String dateTimeFormat;
+
+ 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 15801ad9d7..1481df9462 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 c02526b4a0..14740b6818 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.ServiceLoader;
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.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,12 +71,10 @@ 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() {}
@@ -100,6 +97,7 @@ public final class ProviderUtil {
@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 @@ public final class ProviderUtil {
/**
* Used to test the public {@link #getProvider()} method.
*/
+ @SuppressWarnings("deprecation")
static Provider selectProvider(
final PropertiesUtil properties, final Collection<Provider> 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 @@ public final class ProviderUtil {
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 @@ public final class ProviderUtil {
.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 @@ public final class ProviderUtil {
}
return false;
}
-
- private static final class SimpleProvider extends Provider {
- private SimpleProvider() {
- super(null, CURRENT_VERSION, SimpleLoggerContextFactory.class, NoOpThreadContextMap.class);
- }
- }
}