diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java index c0f8e73fcb7..c9f66300a1c 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationConverter.java @@ -32,7 +32,6 @@ import javax.xml.transform.stream.StreamSource; import org.apache.logging.log4j.core.config.ConfigurationException; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; import org.apache.logging.log4j.core.tools.BasicCommandLineArguments; import org.apache.logging.log4j.core.tools.picocli.CommandLine; @@ -160,8 +159,7 @@ private Log4j1ConfigurationConverter(final CommandLineArguments cla) { } protected void convert(final InputStream input, final OutputStream output) throws IOException { - final ConfigurationBuilder builder = - new Log4j1ConfigurationParser().buildConfigurationBuilder(input); + final ConfigurationBuilder builder = new Log4j1ConfigurationParser().buildConfigurationBuilder(input); builder.writeXmlConfiguration(output); } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java index c75524811c6..81b854e069e 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationFactory.java @@ -24,7 +24,6 @@ import org.apache.logging.log4j.core.config.ConfigurationFactory; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; /** * Experimental ConfigurationFactory for Log4j 1.2 properties configuration files. @@ -40,7 +39,7 @@ public class Log4j1ConfigurationFactory extends ConfigurationFactory { @Override public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { - final ConfigurationBuilder builder; + final ConfigurationBuilder builder; try (final InputStream configStream = source.getInputStream()) { builder = new Log4j1ConfigurationParser().buildConfigurationBuilder(configStream); } catch (final IOException e) { diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java index 4e8ea68650c..9556902a429 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/config/Log4j1ConfigurationParser.java @@ -38,6 +38,7 @@ import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; +import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.status.StatusLogger; @@ -108,7 +109,7 @@ public ConfigurationBuilder buildConfigurationBuilder(final if (threshold != null) { final Level level = OptionConverter.convertLevel(threshold.trim(), Level.ALL); builder.add(builder.newFilter("ThresholdFilter", Result.NEUTRAL, Result.DENY) - .addAttribute("level", level)); + .setAttribute("level", level)); } // Root buildRootLogger(getLog4jValue(ROOTCATEGORY)); @@ -134,7 +135,9 @@ private void buildProperties() { for (final Map.Entry entry : new TreeMap<>(properties).entrySet()) { final String key = entry.getKey().toString(); if (!key.startsWith("log4j.") && !key.equals(ROOTCATEGORY) && !key.equals(ROOTLOGGER)) { - builder.addProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY)); + PropertyComponentBuilder propertyComponentBuilder = + builder.newProperty(key, Objects.toString(entry.getValue(), Strings.EMPTY)); + builder.add(propertyComponentBuilder); } } } @@ -204,7 +207,7 @@ private void buildConsoleAppender(final String appenderName) { target = null; } if (target != null) { - appenderBuilder.addAttribute("target", target); + appenderBuilder.setAttribute("target", target); } } buildAttribute(appenderName, appenderBuilder, "Follow", "follow"); @@ -236,13 +239,13 @@ private void buildDailyRollingFileAppender(final String appenderName) { buildFileAppender(appenderName, appenderBuilder); final String fileName = getLog4jAppenderValue(appenderName, "File"); final String datePattern = getLog4jAppenderValue(appenderName, "DatePattern", ".yyyy-MM-dd"); - appenderBuilder.addAttribute("filePattern", fileName + "%d{" + datePattern + "}"); + appenderBuilder.setAttribute("filePattern", fileName + "%d{" + datePattern + "}"); final ComponentBuilder triggeringPolicy = builder.newComponent("Policies") - .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").addAttribute("modulate", true)); + .addComponent(builder.newComponent("TimeBasedTriggeringPolicy").setAttribute("modulate", true)); appenderBuilder.addComponent(triggeringPolicy); appenderBuilder.addComponent(builder.newComponent("DefaultRolloverStrategy") - .addAttribute("max", Integer.MAX_VALUE) - .addAttribute("fileIndex", "min")); + .setAttribute("max", Integer.MAX_VALUE) + .setAttribute("fileIndex", "min")); builder.add(appenderBuilder); } @@ -251,16 +254,16 @@ private void buildRollingFileAppender(final String appenderName) { builder.newAppender(appenderName, RollingFileAppender.PLUGIN_NAME); buildFileAppender(appenderName, appenderBuilder); final String fileName = getLog4jAppenderValue(appenderName, "File"); - appenderBuilder.addAttribute("filePattern", fileName + ".%i"); + appenderBuilder.setAttribute("filePattern", fileName + ".%i"); final String maxFileSizeString = getLog4jAppenderValue(appenderName, "MaxFileSize", "10485760"); final String maxBackupIndexString = getLog4jAppenderValue(appenderName, "MaxBackupIndex", "1"); final ComponentBuilder triggeringPolicy = builder.newComponent("Policies") .addComponent( - builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", maxFileSizeString)); + builder.newComponent("SizeBasedTriggeringPolicy").setAttribute("size", maxFileSizeString)); appenderBuilder.addComponent(triggeringPolicy); appenderBuilder.addComponent(builder.newComponent("DefaultRolloverStrategy") - .addAttribute("max", maxBackupIndexString) - .addAttribute("fileIndex", "min")); + .setAttribute("max", maxBackupIndexString) + .setAttribute("fileIndex", "min")); builder.add(appenderBuilder); } @@ -271,7 +274,7 @@ private void buildAttribute( final String targetAttributeName) { final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); if (attributeValue != null) { - componentBuilder.addAttribute(targetAttributeName, attributeValue); + componentBuilder.setAttribute(targetAttributeName, attributeValue); } } @@ -282,7 +285,7 @@ private void buildMandatoryAttribute( final String targetAttributeName) { final String attributeValue = getLog4jAppenderValue(componentName, sourceAttributeName); if (attributeValue != null) { - componentBuilder.addAttribute(targetAttributeName, attributeValue); + componentBuilder.setAttribute(targetAttributeName, attributeValue); } else { reportWarning("Missing " + sourceAttributeName + " for " + componentName); } @@ -358,8 +361,8 @@ private void buildAppenderLayout(final String name, final AppenderComponentBuild } case "org.apache.log4j.HTMLLayout": { final LayoutComponentBuilder htmlLayout = builder.newLayout("HtmlLayout"); - htmlLayout.addAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages")); - htmlLayout.addAttribute( + htmlLayout.setAttribute("title", getLog4jAppenderValue(name, "layout.Title", "Log4J Log Messages")); + htmlLayout.setAttribute( "locationInfo", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); appenderBuilder.add(htmlLayout); @@ -367,10 +370,10 @@ private void buildAppenderLayout(final String name, final AppenderComponentBuild } case "org.apache.log4j.xml.XMLLayout": { final LayoutComponentBuilder xmlLayout = builder.newLayout("Log4j1XmlLayout"); - xmlLayout.addAttribute( + xmlLayout.setAttribute( "locationInfo", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.LocationInfo", FALSE))); - xmlLayout.addAttribute( + xmlLayout.setAttribute( "properties", Boolean.parseBoolean(getLog4jAppenderValue(name, "layout.Properties", FALSE))); appenderBuilder.add(xmlLayout); @@ -385,7 +388,7 @@ private void buildAppenderLayout(final String name, final AppenderComponentBuild private LayoutComponentBuilder newPatternLayout(final String pattern) { final LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout"); if (pattern != null) { - layoutBuilder.addAttribute("pattern", pattern); + layoutBuilder.setAttribute("pattern", pattern); } return layoutBuilder; } diff --git a/log4j-core-fuzz-test/src/main/java/org/apache/logging/log4j/core/fuzz/PatternLayoutFuzzer.java b/log4j-core-fuzz-test/src/main/java/org/apache/logging/log4j/core/fuzz/PatternLayoutFuzzer.java index a2419911260..a68dbfd4cd0 100644 --- a/log4j-core-fuzz-test/src/main/java/org/apache/logging/log4j/core/fuzz/PatternLayoutFuzzer.java +++ b/log4j-core-fuzz-test/src/main/java/org/apache/logging/log4j/core/fuzz/PatternLayoutFuzzer.java @@ -34,7 +34,7 @@ public static void fuzzerTestOneInput(final FuzzedDataProvider dataProvider) { createLoggerContext(loggerContextName, EncodingAppender.PLUGIN_NAME, configBuilder -> configBuilder .newLayout("PatternLayout") // Enforce using a single message-based converter, i.e., `MessagePatternConverter` - .addAttribute("pattern", "%m"))) { + .setAttribute("pattern", "%m"))) { final ExtendedLogger logger = loggerContext.getLogger(PatternLayoutFuzzer.class); final LoggerFacade loggerFacade = new Log4jLoggerFacade(logger); fuzzLogger(loggerFacade, dataProvider); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java index ff08d2b2cf8..e2b2918d6cf 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/EventParameterMemoryLeakTest.java @@ -28,7 +28,6 @@ import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -88,10 +87,9 @@ private static LoggerContext createLoggerContext( @SuppressWarnings("SameParameterValue") private static Configuration createConfiguration(final String appenderName) { - final ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder configBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); final LayoutComponentBuilder layoutComponentBuilder = - configBuilder.newLayout("PatternLayout").addAttribute("pattern", "%m"); + configBuilder.newLayout("PatternLayout").setAttribute("pattern", "%m"); final AppenderComponentBuilder appenderComponentBuilder = configBuilder.newAppender(appenderName, "List").add(layoutComponentBuilder); final RootLoggerComponentBuilder loggerComponentBuilder = diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java index 688ac302044..e8e8e2d2c2a 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java @@ -27,9 +27,7 @@ import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.layout.PatternLayout; -import org.apache.logging.log4j.core.util.Builder; import org.junit.jupiter.api.Test; class ReconfigureAppenderTest { @@ -127,8 +125,7 @@ private void removeAppender() { } private void createAndAddAppender() { - final ConfigurationBuilder config_builder = - ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder config_builder = ConfigurationBuilderFactory.newConfigurationBuilder(); // All loggers must have a root logger. The default root logger logs ERRORs to the console. // Override this with a root logger that does not log anywhere as we leave it up the @@ -141,10 +138,10 @@ private void createAndAddAppender() { // Retrieve the logger. final Logger logger = (Logger) LogManager.getLogger(this.getClass()); - final Builder pattern_builder = + final PatternLayout.Builder pattern_builder = PatternLayout.newBuilder().withPattern("[%d{dd-MM-yy HH:mm:ss}] %p %m %throwable %n"); - final PatternLayout pattern_layout = (PatternLayout) pattern_builder.build(); + final PatternLayout pattern_layout = pattern_builder.build(); appender = RollingFileAppender.newBuilder() .withLayout(pattern_layout) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderReconnectTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderReconnectTest.java index 619749eacf2..5550995be68 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderReconnectTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderReconnectTest.java @@ -42,7 +42,6 @@ import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.net.TcpSocketManager; import org.apache.logging.log4j.core.net.TcpSocketManager.HostResolver; import org.apache.logging.log4j.core.net.ssl.SslKeyStoreConstants; @@ -219,24 +218,21 @@ void key_store_changes_should_be_detected_at_reconfigure( final int server1Port = server1.getServerSocket().getLocalPort(); // Create the configuration transformer to add the ``, ``, and `` elements - final BiFunction< - ConfigurationBuilder, - AppenderComponentBuilder, - AppenderComponentBuilder> + final BiFunction, AppenderComponentBuilder, AppenderComponentBuilder> appenderComponentBuilderTransformer = (configBuilder, appenderComponentBuilder) -> { final ComponentBuilder keyStoreComponentBuilder = configBuilder .newComponent("KeyStore") - .addAttribute("type", keyStoreType) - .addAttribute("location", keyStoreFilePath.toString()) - .addAttribute("passwordFile", keyStorePasswordFilePath); + .setAttribute("type", keyStoreType) + .setAttribute("location", keyStoreFilePath.toString()) + .setAttribute("passwordFile", keyStorePasswordFilePath); final ComponentBuilder trustStoreComponentBuilder = configBuilder .newComponent("TrustStore") - .addAttribute("type", trustStoreType) - .addAttribute("location", trustStoreFilePath.toString()) - .addAttribute("passwordFile", trustStorePasswordFilePath); + .setAttribute("type", trustStoreType) + .setAttribute("location", trustStoreFilePath.toString()) + .setAttribute("passwordFile", trustStorePasswordFilePath); return appenderComponentBuilder.addComponent(configBuilder .newComponent("Ssl") - .addAttribute("protocol", "TLS") + .setAttribute("protocol", "TLS") .addComponent(keyStoreComponentBuilder) .addComponent(trustStoreComponentBuilder)); }; @@ -301,27 +297,23 @@ private static Configuration createConfiguration( final String host, final int port, @Nullable - final BiFunction< - ConfigurationBuilder, - AppenderComponentBuilder, - AppenderComponentBuilder> + final BiFunction, AppenderComponentBuilder, AppenderComponentBuilder> appenderComponentBuilderTransformer) { // Create the configuration builder - final ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder() - .setStatusLevel(Level.ERROR) - .setConfigurationName(SocketAppenderReconnectTest.class.getSimpleName()); + final ConfigurationBuilder configBuilder = ConfigurationBuilderFactory.newConfigurationBuilder() + .setStatusLevel(Level.ERROR) + .setConfigurationName(SocketAppenderReconnectTest.class.getSimpleName()); // Create the appender configuration final AppenderComponentBuilder appenderComponentBuilder = configBuilder .newAppender(APPENDER_NAME, "Socket") - .addAttribute("host", host) - .addAttribute("port", port) - .addAttribute("ignoreExceptions", false) - .addAttribute("reconnectionDelayMillis", 10) - .addAttribute("immediateFlush", true) - .add(configBuilder.newLayout("PatternLayout").addAttribute("pattern", "%m%n")); + .setAttribute("host", host) + .setAttribute("port", port) + .setAttribute("ignoreExceptions", false) + .setAttribute("reconnectionDelayMillis", 10) + .setAttribute("immediateFlush", true) + .add(configBuilder.newLayout("PatternLayout").setAttribute("pattern", "%m%n")); final AppenderComponentBuilder transformedAppenderComponentBuilder = appenderComponentBuilderTransformer != null ? appenderComponentBuilderTransformer.apply(configBuilder, appenderComponentBuilder) : appenderComponentBuilder; @@ -406,7 +398,7 @@ private static void verifyLoggingFailure( // Verify the failure for (int i = 0; i < retryCount; i++) { try { - logger.info("should fail #" + i); + logger.info("should fail #{}", i); fail("should have failed #" + i); } catch (final AppenderLoggingException ignored) { assertThat(errorHandler.getBuffer()).hasSize(2 * (i + 1)); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSslSocketOptionsTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSslSocketOptionsTest.java index 0167194c7b1..af240d6d8ac 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSslSocketOptionsTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/SocketAppenderSslSocketOptionsTest.java @@ -30,7 +30,6 @@ import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.net.Rfc1349TrafficClass; import org.apache.logging.log4j.core.net.SocketOptions; import org.apache.logging.log4j.core.net.SocketPerformancePreferences; @@ -134,10 +133,9 @@ void socket_options_should_be_injected(@TempDir(cleanup = CleanupMode.ON_SUCCESS private static Configuration createConfiguration(final Path tempDir, final int port) throws Exception { // Create the configuration builder - final ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder() - .setStatusLevel(Level.ERROR) - .setConfigurationName(SocketAppenderReconnectTest.class.getSimpleName()); + final ConfigurationBuilder configBuilder = ConfigurationBuilderFactory.newConfigurationBuilder() + .setStatusLevel(Level.ERROR) + .setConfigurationName(SocketAppenderReconnectTest.class.getSimpleName()); // Stage the key store password file final Path keyStorePasswordFilePath = tempDir.resolve("keyStorePassword"); @@ -150,47 +148,47 @@ private static Configuration createConfiguration(final Path tempDir, final int p // Create the `SocketOptions` element final ComponentBuilder socketPerformancePreferencesComponentBuilder = configBuilder .newComponent("SocketPerformancePreferences") - .addAttribute("bandwidth", SOCKET_PERFORMANCE_PREFERENCE_BANDWIDTH) - .addAttribute("connectionTime", SOCKET_PERFORMANCE_PREFERENCE_CONNECTION_TIME) - .addAttribute("latency", SOCKET_PERFORMANCE_PREFERENCE_LATENCY); + .setAttribute("bandwidth", SOCKET_PERFORMANCE_PREFERENCE_BANDWIDTH) + .setAttribute("connectionTime", SOCKET_PERFORMANCE_PREFERENCE_CONNECTION_TIME) + .setAttribute("latency", SOCKET_PERFORMANCE_PREFERENCE_LATENCY); final ComponentBuilder socketOptionsComponentBuilder = configBuilder .newComponent("SocketOptions") - .addAttribute("keepAlive", SOCKET_OPTION_KEEP_ALIVE) - .addAttribute("receiveBufferSize", SOCKET_OPTION_RECEIVE_BUFFER_SIZE) - .addAttribute("reuseAddress", SOCKET_OPTION_REUSE_ADDRESS) - .addAttribute("rfc1349TrafficClass", SOCKET_OPTION_RFC1349_TRAFFIC_CLASS) - .addAttribute("sendBufferSize", SOCKET_OPTION_SEND_BUFFER_SIZE) - .addAttribute("soLinger", SOCKET_OPTION_LINGER) - .addAttribute("soTimeout", SOCKET_OPTION_TIMEOUT) - .addAttribute("tcpNoDelay", SOCKET_OPTION_TCP_NO_DELAY) + .setAttribute("keepAlive", SOCKET_OPTION_KEEP_ALIVE) + .setAttribute("receiveBufferSize", SOCKET_OPTION_RECEIVE_BUFFER_SIZE) + .setAttribute("reuseAddress", SOCKET_OPTION_REUSE_ADDRESS) + .setAttribute("rfc1349TrafficClass", SOCKET_OPTION_RFC1349_TRAFFIC_CLASS) + .setAttribute("sendBufferSize", SOCKET_OPTION_SEND_BUFFER_SIZE) + .setAttribute("soLinger", SOCKET_OPTION_LINGER) + .setAttribute("soTimeout", SOCKET_OPTION_TIMEOUT) + .setAttribute("tcpNoDelay", SOCKET_OPTION_TCP_NO_DELAY) .addComponent(socketPerformancePreferencesComponentBuilder); // Create the `Ssl` element final ComponentBuilder keyStoreComponentBuilder = configBuilder .newComponent("KeyStore") - .addAttribute("type", KEYSTORE_TYPE) - .addAttribute("location", KEYSTORE_LOCATION) - .addAttribute("passwordFile", keyStorePasswordFilePath); + .setAttribute("type", KEYSTORE_TYPE) + .setAttribute("location", KEYSTORE_LOCATION) + .setAttribute("passwordFile", keyStorePasswordFilePath); final ComponentBuilder trustStoreComponentBuilder = configBuilder .newComponent("TrustStore") - .addAttribute("type", TRUSTSTORE_TYPE) - .addAttribute("location", TRUSTSTORE_LOCATION) - .addAttribute("passwordFile", trustStorePasswordFilePath); + .setAttribute("type", TRUSTSTORE_TYPE) + .setAttribute("location", TRUSTSTORE_LOCATION) + .setAttribute("passwordFile", trustStorePasswordFilePath); final ComponentBuilder sslComponentBuilder = configBuilder .newComponent("Ssl") - .addAttribute("protocol", "TLS") + .setAttribute("protocol", "TLS") .addComponent(keyStoreComponentBuilder) .addComponent(trustStoreComponentBuilder); // Create the `Socket` element final AppenderComponentBuilder appenderComponentBuilder = configBuilder .newAppender(APPENDER_NAME, "Socket") - .addAttribute("host", "localhost") - .addAttribute("port", port) - .addAttribute("ignoreExceptions", false) - .addAttribute("reconnectionDelayMillis", 10) - .addAttribute("immediateFlush", true) - .add(configBuilder.newLayout("PatternLayout").addAttribute("pattern", "%m%n")) + .setAttribute("host", "localhost") + .setAttribute("port", port) + .setAttribute("ignoreExceptions", false) + .setAttribute("reconnectionDelayMillis", 10) + .setAttribute("immediateFlush", true) + .add(configBuilder.newLayout("PatternLayout").setAttribute("pattern", "%m%n")) .addComponent(socketOptionsComponentBuilder) .addComponent(sslComponentBuilder); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderInterruptedThreadTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderInterruptedThreadTest.java index 6a568a66fe2..bab55e5bce5 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderInterruptedThreadTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderInterruptedThreadTest.java @@ -30,7 +30,6 @@ import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.test.junit.CleanFolders; import org.junit.After; import org.junit.Before; @@ -38,7 +37,7 @@ import org.junit.Test; /** - * Tests https://issues.apache.org/jira/browse/LOG4J2-1798 + * Tests . */ public class RollingFileAppenderInterruptedThreadTest { @@ -52,17 +51,17 @@ public class RollingFileAppenderInterruptedThreadTest { @Before public void setUp() { - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setConfigurationName("LOG4J2-1798 test"); builder.add( - builder.newAppender("consoleLog", "Console").addAttribute("target", ConsoleAppender.Target.SYSTEM_ERR)); + builder.newAppender("consoleLog", "Console").setAttribute("target", ConsoleAppender.Target.SYSTEM_ERR)); builder.add(builder.newAppender("fileAppender", "RollingFile") - .addAttribute("filePattern", ROLLING_APPENDER_FILES_DIR + "/file-%i.log") - .add(builder.newLayout("PatternLayout").addAttribute("pattern", "%msg%n")) + .setAttribute("filePattern", ROLLING_APPENDER_FILES_DIR + "/file-%i.log") + .add(builder.newLayout("PatternLayout").setAttribute("pattern", "%msg%n")) .addComponent(builder.newComponent("SizeBasedTriggeringPolicy") - .addAttribute("size", "20B"))); // relatively small amount to trigger rotation quickly + .setAttribute("size", "20B"))); // relatively small amount to trigger rotation quickly builder.add(builder.newRootLogger(Level.INFO) .add(builder.newAppenderRef("consoleLog")) diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderUpdateDataTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderUpdateDataTest.java index 2f63c19e34e..415f1c4d46e 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderUpdateDataTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingFileAppenderUpdateDataTest.java @@ -28,7 +28,6 @@ import org.apache.logging.log4j.core.config.Configurator; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -37,26 +36,26 @@ */ class RollingFileAppenderUpdateDataTest { - private ConfigurationBuilder buildConfigA() { + private ConfigurationBuilder buildConfigA() { return buildConfigurationBuilder("target/rolling-update-date/foo.log.%i"); } // rebuild config with date based rollover - private ConfigurationBuilder buildConfigB() { + private ConfigurationBuilder buildConfigB() { return buildConfigurationBuilder("target/rolling-update-date/foo.log.%d{yyyy-MM-dd-HH:mm:ss}.%i"); } - private ConfigurationBuilder buildConfigurationBuilder(final String filePattern) { - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + private ConfigurationBuilder buildConfigurationBuilder(final String filePattern) { + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setConfigurationName("LOG4J2-1964 demo"); builder.setStatusLevel(Level.ERROR); // @formatter:off builder.add( - builder.newAppender("consoleLog", "Console").addAttribute("target", ConsoleAppender.Target.SYSTEM_ERR)); + builder.newAppender("consoleLog", "Console").setAttribute("target", ConsoleAppender.Target.SYSTEM_ERR)); builder.add(builder.newAppender("fooAppender", "RollingFile") - .addAttribute("fileName", "target/rolling-update-date/foo.log") - .addAttribute("filePattern", filePattern) - .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "10MB"))); + .setAttribute("fileName", "target/rolling-update-date/foo.log") + .setAttribute("filePattern", filePattern) + .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").setAttribute("size", "10MB"))); builder.add(builder.newRootLogger(Level.INFO) .add(builder.newAppenderRef("consoleLog")) .add(builder.newAppenderRef("fooAppender"))); @@ -82,15 +81,15 @@ void after() { @Test void testClosingLoggerContext() { // initial config with indexed rollover - try (final LoggerContext loggerContext1 = + try (final LoggerContext tLoggerContext1 = Configurator.initialize(buildConfigA().build())) { - validateAppender(loggerContext1, "target/rolling-update-date/foo.log.%i"); + validateAppender(tLoggerContext1, "target/rolling-update-date/foo.log.%i"); } // rebuild config with date based rollover - try (final LoggerContext loggerContext2 = + try (final LoggerContext tLoggerContext2 = Configurator.initialize(buildConfigB().build())) { - validateAppender(loggerContext2, "target/rolling-update-date/foo.log.%d{yyyy-MM-dd-HH:mm:ss}.%i"); + validateAppender(tLoggerContext2, "target/rolling-update-date/foo.log.%d{yyyy-MM-dd-HH:mm:ss}.%i"); } } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java index e46d4cb3ae7..f3460127f41 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java @@ -55,7 +55,6 @@ import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.filter.CompositeFilter; import org.apache.logging.log4j.core.layout.PatternLayout; import org.apache.logging.log4j.core.util.Constants; @@ -291,7 +290,7 @@ void testReconfiguration() throws Exception { assertTrue(file.setLastModified(System.currentTimeMillis()), "setLastModified should have succeeded."); TimeUnit.SECONDS.sleep(config.getWatchManager().getIntervalSeconds() + 1); for (int i = 0; i < 100; ++i) { - logger.debug("Test message " + i); + logger.debug("Test message {}", i); } // Sleep and check @@ -405,21 +404,21 @@ void testBadFileName() { @Test void testBuilder() { - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setStatusLevel(Level.ERROR); builder.setConfigurationName("BuilderTest"); builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL) - .addAttribute("level", Level.DEBUG)); + .setAttribute("level", Level.DEBUG)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add( - builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); + builder.newLayout("PatternLayout").setAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addAttribute("marker", "FLOW")); + .setAttribute("marker", "FLOW")); builder.add(appenderBuilder); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); ctx = Configurator.initialize(builder.build()); final Configuration config = ctx.getConfiguration(); @@ -430,25 +429,25 @@ void testBuilder() { @Test void testRolling() { - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setStatusLevel(Level.ERROR); builder.setConfigurationName("RollingBuilder"); // create the console appender AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add( - builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); + builder.newLayout("PatternLayout").setAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); builder.add(appenderBuilder); final LayoutComponentBuilder layoutBuilder = - builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n"); - final ComponentBuilder triggeringPolicy = builder.newComponent("Policies") - .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?")) - .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M")); + builder.newLayout("PatternLayout").setAttribute("pattern", "%d [%t] %-5level: %msg%n"); + final ComponentBuilder triggeringPolicy = builder.newComponent("Policies") + .addComponent(builder.newComponent("CronTriggeringPolicy").setAttribute("schedule", "0 0 0 * * ?")) + .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").setAttribute("size", "100M")); appenderBuilder = builder.newAppender("rolling", "RollingFile") - .addAttribute("fileName", "target/rolling.log") - .addAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz") + .setAttribute("fileName", "target/rolling.log") + .setAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz") .add(layoutBuilder) .addComponent(triggeringPolicy); builder.add(appenderBuilder); @@ -456,7 +455,7 @@ void testRolling() { // create the new logger builder.add(builder.newLogger("TestLogger", Level.DEBUG) .add(builder.newAppenderRef("rolling")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.DEBUG).add(builder.newAppenderRef("rolling"))); final Configuration config = builder.build(); @@ -464,8 +463,8 @@ void testRolling() { assertNotNull(config.getAppender("rolling"), "No rolling file appender"); assertEquals("RollingBuilder", config.getName(), "Unexpected Configuration"); // Initialize the new configuration - final LoggerContext ctx = Configurator.initialize(config); - Configurator.shutdown(ctx); + final LoggerContext tCtx = Configurator.initialize(config); + Configurator.shutdown(tCtx); } @Test @@ -477,30 +476,30 @@ void testBuilderWithScripts() { + " } else {\n" + " return null;\n" + " }"; - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setStatusLevel(Level.ERROR); builder.setConfigurationName("BuilderTest"); builder.add(builder.newScriptFile("filter.groovy", "target/test-classes/scripts/filter.groovy") - .addIsWatched(true)); + .setIsWatchedAttribute(true)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add(builder.newLayout("PatternLayout") .addComponent(builder.newComponent("ScriptPatternSelector") - .addAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") + .setAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "NoLocation") - .addAttribute("pattern", "[%-5level] %c{1.} %msg%n")) + .setAttribute("key", "NoLocation") + .setAttribute("pattern", "[%-5level] %c{1.} %msg%n")) .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "FLOW") - .addAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) + .setAttribute("key", "FLOW") + .setAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) .addComponent(builder.newComponent("selectorScript", "Script", script) - .addAttribute("language", "beanshell")))); + .setAttribute("language", "beanshell")))); appenderBuilder.add(builder.newFilter("ScriptFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addComponent(builder.newComponent("ScriptRef").addAttribute("ref", "filter.groovy"))); + .addComponent(builder.newComponent("ScriptRef").setAttribute("ref", "filter.groovy"))); builder.add(appenderBuilder); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); ctx = Configurator.initialize(builder.build()); final Configuration config = ctx.getConfiguration(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/MissingLanguageTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/MissingLanguageTest.java index 721c0e3090d..c4850b67f49 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/MissingLanguageTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/MissingLanguageTest.java @@ -26,7 +26,6 @@ import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.util.Constants; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Tag; @@ -56,30 +55,30 @@ void testBuilderWithScripts() { + " } else {\n" + " return null;\n" + " }"; - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setStatusLevel(Level.ERROR); builder.setConfigurationName("BuilderTest"); builder.add(builder.newScriptFile("filter.groovy", "target/test-classes/scripts/filter.groovy") - .addIsWatched(true)); + .setIsWatchedAttribute(true)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add(builder.newLayout("PatternLayout") .addComponent(builder.newComponent("ScriptPatternSelector") - .addAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") + .setAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "NoLocation") - .addAttribute("pattern", "[%-5level] %c{1.} %msg%n")) + .setAttribute("key", "NoLocation") + .setAttribute("pattern", "[%-5level] %c{1.} %msg%n")) .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "FLOW") - .addAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) + .setAttribute("key", "FLOW") + .setAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) .addComponent(builder.newComponent("selectorScript", "Script", script) - .addAttribute("language", "beanshell")))); + .setAttribute("language", "beanshell")))); appenderBuilder.add(builder.newFilter("ScriptFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addComponent(builder.newComponent("ScriptRef").addAttribute("ref", "filter.groovy"))); + .addComponent(builder.newComponent("ScriptRef").setAttribute("ref", "filter.groovy"))); builder.add(appenderBuilder); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); ctx = Configurator.initialize(builder.build()); final Configuration config = ctx.getConfiguration(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/NoLanguagesTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/NoLanguagesTest.java index 26b69be6d56..7ece2894b0d 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/NoLanguagesTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/NoLanguagesTest.java @@ -25,7 +25,6 @@ import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; @@ -53,30 +52,30 @@ void testBuilderWithScripts() { + " } else {\n" + " return null;\n" + " }"; - final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); builder.setStatusLevel(Level.ERROR); builder.setConfigurationName("BuilderTest"); builder.add(builder.newScriptFile("filter.groovy", "target/test-classes/scripts/filter.groovy") - .addIsWatched(true)); + .setIsWatchedAttribute(true)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add(builder.newLayout("PatternLayout") .addComponent(builder.newComponent("ScriptPatternSelector") - .addAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") + .setAttribute("defaultPattern", "[%-5level] %c{1.} %C{1.}.%M.%L %msg%n") .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "NoLocation") - .addAttribute("pattern", "[%-5level] %c{1.} %msg%n")) + .setAttribute("key", "NoLocation") + .setAttribute("pattern", "[%-5level] %c{1.} %msg%n")) .addComponent(builder.newComponent("PatternMatch") - .addAttribute("key", "FLOW") - .addAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) + .setAttribute("key", "FLOW") + .setAttribute("pattern", "[%-5level] %c{1.} ====== %C{1.}.%M:%L %msg ======%n")) .addComponent(builder.newComponent("selectorScript", "Script", script) - .addAttribute("language", "beanshell")))); + .setAttribute("language", "beanshell")))); appenderBuilder.add(builder.newFilter("ScriptFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addComponent(builder.newComponent("ScriptRef").addAttribute("ref", "filter.groovy"))); + .addComponent(builder.newComponent("ScriptRef").setAttribute("ref", "filter.groovy"))); builder.add(appenderBuilder); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); ctx = Configurator.initialize(builder.build()); final Configuration config = ctx.getConfiguration(); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationAssemblerTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationAssemblerTest.java index 0d6b74f33e6..48ee3bf6b96 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationAssemblerTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationAssemblerTest.java @@ -41,7 +41,6 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.filter.ThresholdFilter; import org.apache.logging.log4j.core.layout.GelfLayout; import org.apache.logging.log4j.core.layout.PatternLayout; @@ -55,8 +54,7 @@ void testBuildConfiguration() { try { System.setProperty( Constants.LOG4J_CONTEXT_SELECTOR, "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"); - final ConfigurationBuilder builder = - ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); CustomConfigurationFactory.addTestFixtures("config name", builder); final Configuration configuration = builder.build(); try (final LoggerContext ctx = Configurator.initialize(configuration)) { diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java index f2dbdf0bc4f..61a60d37240 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/ConfigurationBuilderTest.java @@ -25,7 +25,6 @@ import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.junit.jupiter.api.Test; class ConfigurationBuilderTest { @@ -33,38 +32,37 @@ class ConfigurationBuilderTest { private static final String INDENT = " "; private static final String EOL = System.lineSeparator(); - private void addTestFixtures(final String name, final ConfigurationBuilder builder) { + private void addTestFixtures(final String name, final ConfigurationBuilder builder) { builder.setConfigurationName(name); builder.setStatusLevel(Level.ERROR); builder.setShutdownTimeout(5000, TimeUnit.MILLISECONDS); builder.add(builder.newScriptFile("target/test-classes/scripts/filter.groovy") - .addIsWatched(true)); + .setIsWatchedAttribute(true)); builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL) - .addAttribute("level", Level.DEBUG)); + .setAttribute("level", Level.DEBUG)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add( - builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); + builder.newLayout("PatternLayout").setAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addAttribute("marker", "FLOW")); + .setAttribute("marker", "FLOW")); builder.add(appenderBuilder); final AppenderComponentBuilder appenderBuilder2 = - builder.newAppender("Kafka", "Kafka").addAttribute("topic", "my-topic"); + builder.newAppender("Kafka", "Kafka").setAttribute("topic", "my-topic"); appenderBuilder2.addComponent(builder.newProperty("bootstrap.servers", "localhost:9092")); appenderBuilder2.add(builder.newLayout("GelfLayout") - .addAttribute("host", "my-host") + .setAttribute("host", "my-host") .addComponent(builder.newKeyValuePair("extraField", "extraValue"))); builder.add(appenderBuilder2); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG, true) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newLogger("org.apache.logging.log4j.core").add(builder.newAppenderRef("Stdout"))); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); - - builder.addProperty("MyKey", "MyValue"); + builder.add(builder.newProperty("MyKey", "MyValue")); builder.add(builder.newCustomLevel("Panic", 17)); builder.setPackages("foo,bar"); } @@ -113,7 +111,7 @@ private void addTestFixtures(final String name, final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); + final ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); addTestFixtures("config name", builder); final String xmlConfiguration = builder.toXmlConfiguration(); assertEquals(expectedXml, xmlConfiguration); diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java index 737c2181e77..99656081ac4 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/CustomConfigurationFactory.java @@ -26,7 +26,6 @@ import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; /** * Normally this would be a plugin. However, we don't want it used for everything so it will be defined @@ -36,33 +35,33 @@ // @Order(50) public class CustomConfigurationFactory extends ConfigurationFactory { - static Configuration addTestFixtures(final String name, final ConfigurationBuilder builder) { + static Configuration addTestFixtures(final String name, final ConfigurationBuilder builder) { builder.setConfigurationName(name); builder.setStatusLevel(Level.ERROR); builder.add(builder.newScriptFile("target/test-classes/scripts/filter.groovy") - .addIsWatched(true)); + .setIsWatchedAttribute(true)); builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL) - .addAttribute("level", Level.DEBUG)); + .setAttribute("level", Level.DEBUG)); final AppenderComponentBuilder appenderBuilder = - builder.newAppender("Stdout", "CONSOLE").addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); + builder.newAppender("Stdout", "CONSOLE").setAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); appenderBuilder.add( - builder.newLayout("PatternLayout").addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); + builder.newLayout("PatternLayout").setAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addAttribute("marker", "FLOW")); + .setAttribute("marker", "FLOW")); builder.add(appenderBuilder); final AppenderComponentBuilder appenderBuilder2 = - builder.newAppender("Kafka", "Kafka").addAttribute("topic", "my-topic"); + builder.newAppender("Kafka", "Kafka").setAttribute("topic", "my-topic"); appenderBuilder2.addComponent(builder.newProperty("bootstrap.servers", "localhost:9092")); appenderBuilder2.add(builder.newLayout("GelfLayout") - .addAttribute("host", "my-host") + .setAttribute("host", "my-host") .addComponent(builder.newKeyValuePair("extraField", "extraValue"))); builder.add(appenderBuilder2); builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG, true) .add(builder.newAppenderRef("Stdout")) - .addAttribute("additivity", false)); + .setAdditivityAttribute(false)); builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); builder.add(builder.newCustomLevel("Panic", 17)); @@ -78,7 +77,7 @@ public Configuration getConfiguration(final LoggerContext loggerContext, final C @Override public Configuration getConfiguration( final LoggerContext loggerContext, final String name, final URI configLocation) { - final ConfigurationBuilder builder = newConfigurationBuilder(); + final ConfigurationBuilder builder = newConfigurationBuilder(); return addTestFixtures(name, builder); } diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilderTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilderTest.java new file mode 100644 index 00000000000..7b087fcce01 --- /dev/null +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilderTest.java @@ -0,0 +1,266 @@ +/* + * 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.core.config.builder.impl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.LifeCycle.State; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests: {@link DefaultComponentBuilder}. + */ +@SuppressWarnings({"DataFlowIssue", "RedundantCast"}) +class DefaultComponentBuilderTest { + + private final DefaultComponentBuilder builder = + new DefaultComponentBuilder<>(ConfigurationBuilderFactory.newConfigurationBuilder(), "test"); + + @BeforeEach + void setup() { + builder.clear(); + } + + @Test + void testAddAttribute_DuplicateKey() { + + final String key = "k1"; + final String lastValue = "foobar"; + + builder.setAttribute(key, Level.ERROR); + builder.setAttribute(key, State.INITIALIZING); + builder.setAttribute(key, lastValue); + + assertEquals( + lastValue, + builder.getAttribute(key), + "addAttribute(String, String) should have set the attribute value."); + assertEquals( + 1, + builder.getAttributes().size(), + "addAttribute(String, String) should have added the attribute only once."); + } + + @Test + void testAddAttribute_Bool_OK() { + + final String key = "k1"; + final boolean booleanValue = true; + + builder.setAttribute(key, booleanValue); + + assertEquals( + Boolean.toString(booleanValue), + builder.getAttribute(key), + "addAttribute(String, boolean) should have set the attribute value."); + } + + @Test + void testAddAttribute_Bool_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, true), + "addAttribute(String, boolean) should throw an exception with null key"); + } + + @Test + void testAddAttribute_Int_OK() { + + final String key = "k1"; + final int value = 5; + + builder.setAttribute(key, value); + + assertEquals( + Integer.toString(value), + builder.getAttribute(key), + "addAttribute(String, int) should have set the attribute value."); + } + + @Test + void testAddAttribute_Int_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, 5), + "addAttribute(String, int) should throw an exception with null key"); + } + + @Test + void testAddAttribute_Enum_OK() { + + final String key = "k1"; + final State enumValue = State.INITIALIZING; + + builder.setAttribute(key, enumValue); + + assertEquals( + enumValue.name(), + builder.getAttribute(key), + "addAttribute(String, Enum) should have set the attribute value."); + } + + @Test + void testAddAttribute_Enum_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, State.INITIALIZING), + "addAttribute(String, Enum) should throw an exception with null key"); + } + + @Test + void testAddAttribute_Enum_NullValue() { + + final String key = "k1"; + + builder.setAttribute(key, "foobar"); + builder.setAttribute(key, (State) null); + + assertFalse( + builder.getAttributes().containsKey(key), + "addAttribute(String, Enum) should remove the attribute with a null value"); + } + + @Test + void testAddAttribute_Level_OK() { + + final String key = "k1"; + final Level levelValue = Level.ERROR; + + builder.setAttribute(key, levelValue); + + assertEquals( + levelValue.toString(), + builder.getAttribute(key), + "addAttribute(String, Level) should have set the attribute value."); + } + + @Test + void testAddAttribute_Level_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, Level.ERROR), + "addAttribute(String, Level) should throw an exception with null key"); + } + + @Test + void testAddAttribute_Level_NullValue() { + + final String key = "k1"; + + builder.setAttribute(key, "foobar"); + builder.setAttribute(key, (Level) null); + + assertFalse( + builder.getAttributes().containsKey(key), + "addAttribute(String, Level) should remove the attribute with a null value"); + } + + @Test + void testAddAttribute_Object_OK() { + + final String key = "k1"; + final Long objectValue = 50L; + + builder.setAttribute(key, objectValue); + + assertEquals( + objectValue.toString(), + builder.getAttribute(key), + "addAttribute(String, Object) should have set the attribute value."); + } + + @Test + void testAddAttribute_Object_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, 50L), + "addAttribute(String, Object) should throw an exception with null key"); + } + + @Test + void testAddAttribute_Object_NullValue() { + + final String key = "k1"; + + builder.setAttribute(key, "foobar"); + builder.setAttribute(key, (Long) null); + + assertFalse( + builder.getAttributes().containsKey(key), + "addAttribute(String, Object) should remove the attribute with a null value"); + } + + @Test + void testAddAttribute_String_OK() { + + final String key = "k1"; + final String stringValue = "foobar"; + + builder.setAttribute(key, stringValue); + + assertEquals( + stringValue, + builder.getAttribute(key), + "addAttribute(String, String) should have set the attribute value."); + } + + @Test + void testAddAttribute_String_NullKey() { + + assertThrows( + NullPointerException.class, + () -> builder.setAttribute((String) null, "foobar"), + "addAttribute(String, Enum) should throw an exception with null key"); + } + + @Test + void testAddAttribute_String_NullValue() { + + final String key = "k1"; + + builder.setAttribute(key, "foobar"); + builder.setAttribute(key, (String) null); + + assertFalse( + builder.getAttributes().containsKey(key), + "addAttribute(String, Enum) should remove the attribute with a null value"); + } + + @Test + void testConstructor_NullBuilder() { + assertThrows(NullPointerException.class, () -> new DefaultComponentBuilder<>(null, "test")); + } + + @Test + void testConstructor_NullType() { + + ConfigurationBuilder configurationBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); + + assertThrows(NullPointerException.class, () -> new DefaultComponentBuilder<>(configurationBuilder, null)); + } +} diff --git a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java index ba58e2ad3d2..ea4129a2d85 100644 --- a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java +++ b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/pattern/MessagePatternConverterTest.java @@ -22,7 +22,8 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.DefaultConfiguration; -import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; @@ -80,8 +81,9 @@ void testPatternAndParameterizedMessageDateLookup() { @Test void testDefaultDisabledLookup() { - final Configuration config = - new DefaultConfigurationBuilder().addProperty("foo", "bar").build(true); + final ConfigurationBuilder configurationBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); + configurationBuilder.add(configurationBuilder.newProperty("foo", "bar")); + final Configuration config = configurationBuilder.build(true); final MessagePatternConverter converter = MessagePatternConverter.newInstance(config, null); final Message msg = new ParameterizedMessage("${foo}"); final LogEvent event = Log4jLogEvent.newBuilder() // @@ -96,8 +98,9 @@ void testDefaultDisabledLookup() { @Test void testDisabledLookup() { - final Configuration config = - new DefaultConfigurationBuilder().addProperty("foo", "bar").build(true); + final ConfigurationBuilder configurationBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); + configurationBuilder.add(configurationBuilder.newProperty("foo", "bar")); + final Configuration config = configurationBuilder.build(true); final MessagePatternConverter converter = MessagePatternConverter.newInstance(config, new String[] {"nolookups"}); final Message msg = new ParameterizedMessage("${foo}"); @@ -113,8 +116,9 @@ void testDisabledLookup() { @Test void testLookup() { - final Configuration config = - new DefaultConfigurationBuilder().addProperty("foo", "bar").build(true); + final ConfigurationBuilder configurationBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); + configurationBuilder.add(configurationBuilder.newProperty("foo", "bar")); + final Configuration config = configurationBuilder.build(true); final MessagePatternConverter converter = MessagePatternConverter.newInstance(config, new String[] {"lookups"}); final Message msg = new ParameterizedMessage("${foo}"); final LogEvent event = Log4jLogEvent.newBuilder() // diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderComponentBuilder.java index 26da03d3958..dd46dabddb6 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderComponentBuilder.java @@ -16,23 +16,33 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.Appender; +import org.osgi.annotation.versioning.ProviderType; + /** - * Builder for constructing Appender Components. + * A builder interface for constructing and configuring {@link Appender} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ +@ProviderType public interface AppenderComponentBuilder extends FilterableComponentBuilder { /** - * Adds a Layout to the Appender component. - * @param builder The LayoutComponentBuilder with all of its attributes set. - * @return this builder. + * Adds a {@link LayoutComponentBuilder} to this Appender component builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code LayoutComponentBuilder} with all of its attributes set. + * @return this component builder (for chaining) + * @throws NullPointerException if the given {@code builder} argument is {@code null} */ AppenderComponentBuilder add(LayoutComponentBuilder builder); - - /** - * Returns the name of the Appender. - * @return the name of the Appender. - */ - @Override - String getName(); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderRefComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderRefComponentBuilder.java index 26953666c27..64334927211 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderRefComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/AppenderRefComponentBuilder.java @@ -16,8 +16,58 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.jspecify.annotations.Nullable; + /** - * Assembler for constructing AppenderRef Components. + * A builder interface for constructing and configuring {@link AppenderRef} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ -public interface AppenderRefComponentBuilder extends FilterableComponentBuilder {} +public interface AppenderRefComponentBuilder extends FilterableComponentBuilder { + + /** + * Sets the "{@code level}" attribute on the appender-reference component. + *

+ * If the given {@code level} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param level the level + * @return this builder (for chaining) + */ + default AppenderRefComponentBuilder setLevelAttribute(@Nullable String level) { + return setAttribute("level", level); + } + + /** + * Sets the "{@code level}" attribute on the appender reference component. + *

+ * If the given {@code level} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param level the level + * @return this builder (for chaining) + */ + default AppenderRefComponentBuilder setLevelAttribute(@Nullable Level level) { + return setAttribute("level", level); + } + + /** + * Sets the "{@code ref}" attribute on the appender reference component. + *

+ * If the given {@code refAppenderName} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param refAppenderName the name of the appender being referenced + * @return this builder (for chaining) + */ + default AppenderRefComponentBuilder setRefAttribute(@Nullable String refAppenderName) { + return setAttribute("ref", refAppenderName); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/Component.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/Component.java index d6c5934b2b6..438e38ba070 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/Component.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/Component.java @@ -20,45 +20,65 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** * Container for building Configurations. This class is not normally directly manipulated by users * of the Assembler API. * @since 2.4 */ +@ProviderType public class Component { private final Map attributes = new LinkedHashMap<>(); private final List components = new ArrayList<>(); private final String pluginType; - private final String value; + private final @Nullable String value; + + /** + * Default constructor. + * + * @deprecated - use {@link Component(String)} - a non-{@code null} '{@code pluginType}' must be specified + */ + @Deprecated + public Component() { + this.pluginType = ""; + this.value = null; + } public Component(final String pluginType) { this(pluginType, null, null); } - public Component(final String pluginType, final String name) { + public Component(final String pluginType, final @Nullable String name) { this(pluginType, name, null); } - public Component(final String pluginType, final String name, final String value) { + public Component(final String pluginType, @Nullable final String name, @Nullable final String value) { this.pluginType = pluginType; this.value = value; - if (name != null && name.length() > 0) { + if (name != null && !name.isEmpty()) { attributes.put("name", name); } } - public Component() { - this.pluginType = null; - this.value = null; - } - - public String addAttribute(final String key, final String newValue) { - return attributes.put(key, newValue); + /** + * Puts the given key/value pair to the attribute map. + *

+ * If the new value is {@code null}, than any existing entry with the given {@code key} is ejected from the map. + *

+ * @param key the key + * @param newValue the new value + * @return the previous value or {@code null} if none was set + */ + public @Nullable String addAttribute(final String key, final @Nullable String newValue) { + return putAttribute(key, newValue); } public void addComponent(final Component component) { + Objects.requireNonNull(component, "The 'component' argument cannot be null."); components.add(component); } @@ -74,7 +94,27 @@ public String getPluginType() { return pluginType; } - public String getValue() { + public @Nullable String getValue() { return value; } + + /** + * Puts the given key/value pair to the attribute map. + *

+ * If the new value is {@code null}, than any existing entry with the given {@code key} is ejected from the map. + *

+ * @param key the key + * @param newValue the new value + * @return the previous value or {@code null} if none was set + */ + protected @Nullable String putAttribute(final String key, final @Nullable String newValue) { + + Objects.requireNonNull(key, "The 'key' argument cannot be null."); + + if (newValue == null) { + return attributes.remove(key); + } else { + return attributes.put(key, newValue); + } + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ComponentBuilder.java index 595c9062a5e..f946d5030e8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ComponentBuilder.java @@ -19,78 +19,199 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.util.Builder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** * Builds arbitrary components and is the base type for the provided components. * @param The ComponentBuilder's own type for fluent APIs. * @since 2.4 */ +@ProviderType public interface ComponentBuilder> extends Builder { /** - * Adds a String attribute. + * Returns the name of the component. + * @return The component's name or {@code null} if it doesn't have one. + */ + @Nullable + String getName(); + + /** + * Retrieves the {@code ConfigurationBuilder}. + * @return The ConfigurationBuilder. + */ + ConfigurationBuilder getBuilder(); + + /** + * Adds a sub component. + * @param builder The Assembler for the subcomponent with all of its attributes and sub-components set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} + */ + T addComponent(ComponentBuilder builder); + + /** + * Sets a String attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

* @param key The attribute key. * @param value The value of the attribute. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, String value); + T setAttribute(String key, @Nullable String value); /** - * Adds a logging Level attribute. + * Sets a logging Level attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

* @param key The attribute key. * @param level The logging Level. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, Level level); + T setAttribute(String key, @Nullable Level level); /** - * Adds an enumeration attribute. + * Sets an enumeration attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

* @param key The attribute key. * @param value The enumeration. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, Enum value); + T setAttribute(String key, @Nullable Enum value); /** - * Adds an integer attribute. + * Sets an integer attribute. * @param key The attribute key. * @param value The integer value. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, int value); + T setAttribute(String key, int value); /** - * Adds a boolean attribute. + * Sets a boolean attribute. * @param key The attribute key. * @param value The boolean value. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, boolean value); + T setAttribute(String key, boolean value); /** - * Adds an Object attribute. + * Sets an Object attribute. + *

+ * If the given {@code value} is {@code null}, the component attribute with the given key + * will be removed (if present). + *

* @param key The attribute key. * @param value The object value. - * @return This ComponentBuilder. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} */ - T addAttribute(String key, Object value); + T setAttribute(String key, @Nullable Object value); /** - * Adds a sub component. - * @param builder The Assembler for the subcomponent with all of its attributes and sub-components set. - * @return This ComponentBuilder (not the argument). + * Adds a String attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

+ * @param key The attribute key. + * @param value The value of the attribute. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, int)} */ - T addComponent(ComponentBuilder builder); + @Deprecated + default T addAttribute(String key, @Nullable String value) { + return setAttribute(key, value); + } /** - * Returns the name of the component, if any. - * @return The component's name or null if it doesn't have one. + * Adds a logging Level attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

+ * @param key The attribute key. + * @param level The logging Level. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, Level)} */ - String getName(); + @Deprecated + default T addAttribute(String key, @Nullable Level level) { + return setAttribute(key, level); + } /** - * Retrieves the ConfigurationBuilder. - * @return The ConfigurationBuilder. + * Adds an enumeration attribute. + *

+ * If the given {@code level} value is {@code null}, the component attribute with the given key + * will be removed (if present). + *

+ * @param key The attribute key. + * @param value The enumeration. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, Enum)} */ - ConfigurationBuilder getBuilder(); + @Deprecated + default T addAttribute(String key, @Nullable Enum value) { + return setAttribute(key, value); + } + + /** + * Adds an integer attribute. + * @param key The attribute key. + * @param value The integer value. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, int)} + */ + @Deprecated + default T addAttribute(String key, int value) { + return setAttribute(key, value); + } + + /** + * Adds a boolean attribute. + * @param key The attribute key. + * @param value The boolean value. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, boolean)} + */ + @Deprecated + default T addAttribute(String key, boolean value) { + return setAttribute(key, value); + } + + /** + * Adds an Object attribute. + *

+ * If the given {@code value} is {@code null}, the component attribute with the given key + * will be removed (if present). + *

+ * @param key The attribute key. + * @param value The object value. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} is {@code null} + * @deprecated use {@link #setAttribute(String, Object)} + */ + @Deprecated + default T addAttribute(String key, @Nullable Object value) { + return setAttribute(key, value); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CompositeFilterComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CompositeFilterComponentBuilder.java index 3c2eb091307..cf9d3ff0898 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CompositeFilterComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CompositeFilterComponentBuilder.java @@ -16,9 +16,71 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.Filter.Result; +import org.apache.logging.log4j.core.filter.CompositeFilter; +import org.jspecify.annotations.Nullable; + /** - * Wraps multiple Filter Component builders. + * A builder interface for constructing and configuring {@link CompositeFilter} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

* * @since 2.4 */ -public interface CompositeFilterComponentBuilder extends FilterableComponentBuilder {} +public interface CompositeFilterComponentBuilder extends FilterableComponentBuilder { + + /** + * Adds the 'onMatch' attribute to the filter component. + *

+ * If the given {@code onMatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMatch the attribute value + * @return this builder (for chaining) + */ + default CompositeFilterComponentBuilder setOnMatchAttribute(@Nullable String onMatch) { + return setAttribute("onMatch", onMatch); + } + + /** + * Sets the 'onMatch' attribute on the filter component. + *

+ * If the given {@code onMatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMatch the attribute value + * @return this builder (for chaining) + */ + default CompositeFilterComponentBuilder setOnMatchAttribute(@Nullable Result onMatch) { + return setAttribute("onMatch", onMatch); + } + + /** + * Sets the 'onMismatch' attribute on the filter component. + *

+ * If the given {@code onMismatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMismatch the attribute value + * @return this builder (for chaining) + */ + default CompositeFilterComponentBuilder setOnMismatchAttribute(@Nullable String onMismatch) { + return setAttribute("onMismatch", onMismatch); + } + + /** + * Sets the 'onMismatch' attribute on the filter component. + *

+ * If the given {@code onMismatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMismatch the attribute value + * @return this builder (for chaining) + */ + default CompositeFilterComponentBuilder setOnMismatchAttribute(@Nullable Result onMismatch) { + return setAttribute("onMismatch", onMismatch); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java index 3b9fc489614..f511417b3fd 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.java @@ -16,430 +16,587 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import aQute.bnd.annotation.baseline.BaselineIgnore; import java.io.IOException; import java.io.OutputStream; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Filter.Result; +import org.apache.logging.log4j.core.Layout; +import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.async.AsyncLogger; +import org.apache.logging.log4j.core.config.AppenderRef; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.CustomLevels; +import org.apache.logging.log4j.core.config.LoggerConfig.RootLogger; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.core.script.Script; +import org.apache.logging.log4j.core.script.ScriptFile; import org.apache.logging.log4j.core.util.Builder; +import org.apache.logging.log4j.core.util.KeyValuePair; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** * Interface for building logging configurations. * @param The Configuration type created by this builder. * @since 2.4 */ +@ProviderType public interface ConfigurationBuilder extends Builder { /** - * Adds a ScriptComponent. - * @param builder The ScriptComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link Script} component built by the given {@code ScriptComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code ScriptComponentBuilder} to add with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(ScriptComponentBuilder builder); /** - * Adds a ScriptFileComponent. - * @param builder The ScriptFileComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link ScriptFile} component built by the given {@code ScriptFileComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code ScriptFileComponentBuilder} to add with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(ScriptFileComponentBuilder builder); /** - * Adds an AppenderComponent. - * @param builder The AppenderComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link Appender} component built by the given {@code AppenderComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code AppenderComponentBuilder} to add with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(AppenderComponentBuilder builder); /** - * Adds a CustomLevel component. - * @param builder The CustomLevelComponentBuilder with all of its attributes set. - * @return this builder instance. + * Adds the {@link CustomLevels} component built by the given {@code CustomLevelComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code CustomLevelComponentBuilder} with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(CustomLevelComponentBuilder builder); /** - * Adds a Filter component. - * @param builder the FilterComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link Filter} component built by the given {@code FilterComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code FilterComponentBuilder} with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(FilterComponentBuilder builder); /** - * Adds a Logger component. - * @param builder The LoggerComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link Logger} component built by the given {@code LoggerComponentBuilder} to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code LoggerComponentBuilder} with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(LoggerComponentBuilder builder); /** - * Adds the root Logger component. - * @param builder The RootLoggerComponentBuilder with all of its attributes and sub components set. - * @return this builder instance. + * Adds the {@link RootLogger} component built by the given {@code RootLoggerComponentBuilder} + * to this builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code RootLoggerComponentBuilder} with all of its attributes and subcomponents set. + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} */ ConfigurationBuilder add(RootLoggerComponentBuilder builder); /** - * Adds a Property key and value. - * @param key The property key. - * @param value The property value. - * @return this builder instance. + * Adds a the {@link Property} component built by the given {@link PropertyComponentBuilder}. + * @param builder the {@code PropertyComponentBuilder} to add + * @return this builder (for chaining) */ - ConfigurationBuilder addProperty(String key, String value); + ConfigurationBuilder add(PropertyComponentBuilder builder); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @param language The script language - * @param text The script to execute. - * @return A new ScriptComponentBuilder. + * Adds a {@link Property} component with the given {@code key} and {@code value}. + * + * @param name the property name + * @param value the property value + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code builder} is {@code null} + * @deprecated use {@link #add(PropertyComponentBuilder)} */ - ScriptComponentBuilder newScript(String name, String language, String text); + @Deprecated + ConfigurationBuilder addProperty(@Nullable String name, @Nullable String value); /** - * Returns a builder for creating Async Loggers. - * @param path The location of the script file. - * @return A new ScriptFileComponentBuilder. + * Returns a {@link ScriptComponentBuilder} for creating a {@link Script} component. + * @param name the script name + * @param language the script language + * @param text the script to execute + * @return the new component builder instance */ - ScriptFileComponentBuilder newScriptFile(String path); + ScriptComponentBuilder newScript(@Nullable String name, @Nullable String language, @Nullable String text); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the script file. - * @param path The location of the script file. - * @return A new ScriptFileComponentBuilder. + * Returns a {@link ScriptFileComponentBuilder} for creating a {@link ScriptFile} component. + * @param path the script file path + * @return the new component builder instance */ - ScriptFileComponentBuilder newScriptFile(String name, String path); + ScriptFileComponentBuilder newScriptFile(@Nullable String path); /** - * Returns a builder for creating Appenders. - * @param name The name of the Appender. - * @param pluginName The Plugin type of the Appender. - * @return A new AppenderComponentBuilder. + * Returns a {@link ScriptFileComponentBuilder} for creating a {@link ScriptFile} component. + * @param name the script file name + * @param path the script file path + * @return the new component builder instance */ - AppenderComponentBuilder newAppender(String name, String pluginName); + ScriptFileComponentBuilder newScriptFile(@Nullable String name, @Nullable String path); /** - * Returns a builder for creating AppenderRefs. - * @param ref The name of the Appender being referenced. - * @return A new AppenderRefComponentBuilder. + * Returns a {@link AppenderComponentBuilder} for creating an {@link Appender} component. + * @param name the appender name + * @param pluginName the appender plugin-type + * @return the new component builder instance */ - AppenderRefComponentBuilder newAppenderRef(String ref); + AppenderComponentBuilder newAppender(@Nullable String name, String pluginName); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @return A new LoggerComponentBuilder. + * Returns a {@link AppenderRefComponentBuilder} for creating an {@link AppenderRef} component. + * @param ref the name of the {@code Appender} being referenced + * @return the new component builder instance */ - LoggerComponentBuilder newAsyncLogger(String name); + AppenderRefComponentBuilder newAppenderRef(@Nullable String ref); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @param includeLocation If true include location information. - * @return A new LoggerComponentBuilder. + * Returns a {@link LoggerComponentBuilder} builder for creating an {@link AsyncLogger} component. + * @param name the logger name + * @return the new component builder instance */ - LoggerComponentBuilder newAsyncLogger(String name, boolean includeLocation); + LoggerComponentBuilder newAsyncLogger(@Nullable String name); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @return A new LoggerComponentBuilder. + * Returns a {@link LoggerComponentBuilder} for creating an {@link AsyncLogger} component. + * @param name the logger name + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - LoggerComponentBuilder newAsyncLogger(String name, Level level); + LoggerComponentBuilder newAsyncLogger(@Nullable String name, boolean includeLocation); /** - * Returns a builder for creating Async Loggers. + * Returns a {@link LoggerComponentBuilder} for creating an {@link AsyncLogger} component. * @param name The name of the Logger. * @param level The logging Level to be assigned to the Logger. - * @param includeLocation If true include location information. * @return A new LoggerComponentBuilder. */ - LoggerComponentBuilder newAsyncLogger(String name, Level level, boolean includeLocation); + LoggerComponentBuilder newAsyncLogger(@Nullable String name, @Nullable Level level); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @return A new LoggerComponentBuilder. + * Returns a {@link LoggerComponentBuilder} for creating an {@link AsyncLogger} component. + * @param name the logger name + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - LoggerComponentBuilder newAsyncLogger(String name, String level); + LoggerComponentBuilder newAsyncLogger(@Nullable String name, @Nullable Level level, boolean includeLocation); /** - * Returns a builder for creating Async Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @param includeLocation If true include location information. - * @return A new LoggerComponentBuilder. + * Returns a {@link LoggerComponentBuilder} for creating an {@link AsyncLogger} component + * @param name the logger name + * @param level the logger level + * @return the new component builder instance */ - LoggerComponentBuilder newAsyncLogger(String name, String level, boolean includeLocation); + LoggerComponentBuilder newAsyncLogger(@Nullable String name, @Nullable String level); /** - * Returns a builder for creating the async root Logger. - * @return A new RootLoggerComponentBuilder. + * Returns a {@link LoggerComponentBuilder} for creating an {@link AsyncLogger} component. + * @param name the logger name + * @param level the logger level + * @param includeLocation {@code true} to include location inforrmation; otherwise, {@code false} + * @return the new component builder instance + */ + LoggerComponentBuilder newAsyncLogger(@Nullable String name, @Nullable String level, boolean includeLocation); + + /** + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @return the new component builder instance */ RootLoggerComponentBuilder newAsyncRootLogger(); /** - * Returns a builder for creating the async root Logger. - * @param includeLocation If true include location information. - * @return A new RootLoggerComponentBuilder. + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ RootLoggerComponentBuilder newAsyncRootLogger(boolean includeLocation); /** - * Returns a builder for creating the async root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @return A new RootLoggerComponentBuilder. + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @param level the logger level + * @return the new component builder instance + */ + RootLoggerComponentBuilder newAsyncRootLogger(@Nullable Level level); + + /** + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - RootLoggerComponentBuilder newAsyncRootLogger(Level level); + RootLoggerComponentBuilder newAsyncRootLogger(@Nullable Level level, boolean includeLocation); /** - * Returns a builder for creating the async root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @param includeLocation If true include location information. - * @return A new RootLoggerComponentBuilder. + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @param level the logger level + * @return the new component builder instance */ - RootLoggerComponentBuilder newAsyncRootLogger(Level level, boolean includeLocation); + RootLoggerComponentBuilder newAsyncRootLogger(@Nullable String level); /** - * Returns a builder for creating the async root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @return A new RootLoggerComponentBuilder. + * Returns a {@link RootLoggerComponentBuilder} for creating a root {@link AsyncLogger} component. + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - RootLoggerComponentBuilder newAsyncRootLogger(String level); + RootLoggerComponentBuilder newAsyncRootLogger(@Nullable String level, boolean includeLocation); /** - * Returns a builder for creating the async root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @param includeLocation If true include location information. - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link ComponentBuilder} for creating a generic {@link Component}. + * @param the {@code ComponentBuilder} target type + * @param pluginType the component plugin type + * @return the new component builder instance */ - RootLoggerComponentBuilder newAsyncRootLogger(String level, boolean includeLocation); + > ComponentBuilder newComponent(String pluginType); /** - * Returns a builder for creating generic components. - * @param ComponentBuilder target type - * @param pluginName The Plugin type of the component. - * @return A new ComponentBuilder. + * Returns a new {@link ComponentBuilder} for creating a generic {@link Component}. + * @param the {@code ComponentBuilder} target type + * @param name the component name + * @param pluginType the component plugin type + * @return the new component builder instance */ - > ComponentBuilder newComponent(String pluginName); + > ComponentBuilder newComponent(@Nullable String name, String pluginType); /** - * Returns a builder for creating generic components. - * @param ComponentBuilder target type - * @param name The name of the component (may be null). - * @param pluginName The Plugin type of the component. - * @return A new ComponentBuilder. + * Returns a new {@link ComponentBuilder} for creating a generic {@link Component}. + * @param the {@code ComponentBuilder} target type + * @param name the component name + * @param pluginType the component plugin type + * @param value the component value + * @return the new component builder instance */ - > ComponentBuilder newComponent(String name, String pluginName); + > ComponentBuilder newComponent( + @Nullable String name, String pluginType, @Nullable String value); /** - * Returns a builder for creating generic components. - * @param ComponentBuilder target type - * @param name The name of the component (may be null). - * @param pluginName The Plugin type of the component. - * @param value The value of the component. - * @return A new ComponentBuilder. + * Returns a new {@link PropertyComponentBuilder} for creating a {@link Property} component. + * @param name the property name + * @param value the property value + * @return the new component builder instance */ - > ComponentBuilder newComponent(String name, String pluginName, String value); + PropertyComponentBuilder newProperty(@Nullable String name, @Nullable String value); /** - * Returns a builder for creating Property:s - * @param name The name of the property. - * @param value The value of the component. - * @return A new PropertyComponentBuilder. + * Returns a new {@link PropertyComponentBuilder} for creating a {@link KeyValuePair} component. + * @param key the key + * @param value the value + * @return the new component builder instance */ - PropertyComponentBuilder newProperty(String name, String value); + KeyValuePairComponentBuilder newKeyValuePair(@Nullable String key, @Nullable String value); /** - * Returns a builder for creating KeyValuePair:s - * @param key The name - * @param value The value - * @return A new KeyValuePairComponentBuilder. + * Returns a new {@link CustomLevelComponentBuilder} for creating a {@link CustomLevels} component. + * @param name the custom level name + * @param intLevel the integer value to be assigned to the level + * @return the new component builder instance */ - KeyValuePairComponentBuilder newKeyValuePair(String key, String value); + CustomLevelComponentBuilder newCustomLevel(@Nullable String name, int intLevel); /** - * Returns a builder for creating CustomLevels - * @param name The name of the custom level. - * @param level The integer value to be assigned to the level. - * @return A new CustomLevelComponentBuilder. + * Returns a new {@link FilterComponentBuilder} for creating a {@link Filter} component. + * @param pluginType the plugin type of the filter + * @return the new component builder instance */ - CustomLevelComponentBuilder newCustomLevel(String name, int level); + FilterComponentBuilder newFilter(String pluginType); /** - * Returns a builder for creating Filters. - * @param pluginName The Plugin type of the Filter. + * Returns a new {@link FilterComponentBuilder} for creating a {@code Filter} component. + * @param pluginType The Plugin type of the Filter. * @param onMatch "ACCEPT", "DENY", or "NEUTRAL" * @param onMismatch "ACCEPT", "DENY", or "NEUTRAL" - * @return A new FilterComponentBuilder. + * @return the new component builder instance */ - FilterComponentBuilder newFilter(String pluginName, Filter.Result onMatch, Filter.Result onMismatch); + FilterComponentBuilder newFilter(String pluginType, @Nullable Result onMatch, @Nullable Result onMismatch); /** - * Returns a builder for creating Filters. - * @param pluginName The Plugin type of the Filter. + * Returns a new {@link FilterComponentBuilder} for creating a {@link Filter} component. + * @param pluginType the plugin type of the filter * @param onMatch "ACCEPT", "DENY", or "NEUTRAL" * @param onMismatch "ACCEPT", "DENY", or "NEUTRAL" - * @return A new FilterComponentBuilder. + * @return the new component builder instance */ - FilterComponentBuilder newFilter(String pluginName, String onMatch, String onMismatch); + FilterComponentBuilder newFilter(String pluginType, @Nullable String onMatch, @Nullable String onMismatch); /** - * Returns a builder for creating Layouts. - * @param pluginName The Plugin type of the Layout. - * @return A new LayoutComponentBuilder. + * Returns a new {@link LayoutComponentBuilder} for creating a {@link Layout} component. + * @param pluginType the plugin type of the layout + * @return the new component builder instance */ - LayoutComponentBuilder newLayout(String pluginName); + LayoutComponentBuilder newLayout(String pluginType); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LayoutComponentBuilder} for creating a {@link Layout} component. + * @param name the logger name + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name); + LoggerComponentBuilder newLogger(@Nullable String name); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @param includeLocation If true include location information. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LoggerComponentBuilder} for creating a {@link Logger} component. + * @param name the logger name + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name, boolean includeLocation); + LoggerComponentBuilder newLogger(@Nullable String name, boolean includeLocation); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LoggerComponentBuilder} for creating a {@link Logger} component. + * @param name the logger name + * @param level the logger level + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name, Level level); + LoggerComponentBuilder newLogger(@Nullable String name, @Nullable Level level); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @param includeLocation If true include location information. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LoggerComponentBuilder} for creating a {@link Logger} component. + * @param name the logger name + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name, Level level, boolean includeLocation); + LoggerComponentBuilder newLogger(@Nullable String name, @Nullable Level level, boolean includeLocation); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LoggerComponentBuilder} for creating a {@link Logger} component. + * @param name the logger name + * @param level the logger level + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name, String level); + LoggerComponentBuilder newLogger(@Nullable String name, @Nullable String level); /** - * Returns a builder for creating Loggers. - * @param name The name of the Logger. - * @param level The logging Level to be assigned to the Logger. - * @param includeLocation If true include location information. - * @return A new LoggerComponentBuilder. + * Returns a new {@link LoggerComponentBuilder} for creating a {@link Logger} component. + * @param name the logger name + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - LoggerComponentBuilder newLogger(String name, String level, boolean includeLocation); + LoggerComponentBuilder newLogger(@Nullable String name, @Nullable String level, boolean includeLocation); /** - * Returns a builder for creating the root Logger. - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @return the new component builder instance */ RootLoggerComponentBuilder newRootLogger(); /** - * Returns a builder for creating the root Logger. - * @param includeLocation If true include location information. - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instancec */ RootLoggerComponentBuilder newRootLogger(boolean includeLocation); /** - * Returns a builder for creating the root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @param level the logger level + * @return the new component builder instance */ - RootLoggerComponentBuilder newRootLogger(Level level); + RootLoggerComponentBuilder newRootLogger(@Nullable Level level); /** - * Returns a builder for creating the root Logger. - * @param level The logging Level to be assigned to the root Logger. - * @param includeLocation If true include location information. - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - RootLoggerComponentBuilder newRootLogger(Level level, boolean includeLocation); + RootLoggerComponentBuilder newRootLogger(@Nullable Level level, boolean includeLocation); /** - * Returns a builder for creating the root Logger. - * @param level The logging Level to be assigned to the root Logger. - * - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @param level the logger level + * @return the new component builder instance */ - RootLoggerComponentBuilder newRootLogger(String level); + RootLoggerComponentBuilder newRootLogger(@Nullable String level); /** - * Returns a builder for creating the root Logger. - * @param level The logging Level to be assigned to the root Logger. - * - * @return A new RootLoggerComponentBuilder. + * Returns a new {@link RootLoggerComponentBuilder} for creating a {@link RootLogger} component. + * @param level the logger level + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return the new component builder instance */ - RootLoggerComponentBuilder newRootLogger(String level, boolean includeLocation); + RootLoggerComponentBuilder newRootLogger(@Nullable String level, boolean includeLocation); /** * Set the Advertiser Plugin name. * @param advertiser The Advertiser Plugin name. * @return this builder instance. */ - ConfigurationBuilder setAdvertiser(String advertiser); + ConfigurationBuilder setAdvertiser(@Nullable String advertiser); /** * Sets the name of the configuration. * @param name the name of the {@link Configuration}. By default is {@code "Constructed"}. * @return this builder instance. */ - ConfigurationBuilder setConfigurationName(String name); + ConfigurationBuilder setConfigurationName(@Nullable String name); /** - * Sets the configuration source, if one exists. - * @param configurationSource the ConfigurationSource. - * @return this builder instance. + * Sets the configuration source. + * @param configurationSource the configuration source + * @return this builder instance (for chaining) */ - ConfigurationBuilder setConfigurationSource(ConfigurationSource configurationSource); + ConfigurationBuilder setConfigurationSource(@Nullable ConfigurationSource configurationSource); /** - * Sets the interval at which the configuration file should be checked for changes. - * @param intervalSeconds The number of seconds that should pass between checks of the configuration file. + * Specifies the destination for StatusLogger events. This can be {@code out} (default) for using + * {@link System#out standard out}, {@code err} for using {@link System#err standard error}, or a file URI to + * which log events will be written. If the provided URI is invalid, then the default destination of standard + * out will be used. + * + * @param destination where status log messages should be output. * @return this builder instance. */ - ConfigurationBuilder setMonitorInterval(String intervalSeconds); + ConfigurationBuilder setDestination(@Nullable String destination); /** - * Sets the list of packages to search for plugins. - * @param packages The comma separated list of packages. - * @return this builder instance. + * Sets the logger context. + * @param loggerContext the logger context. */ - ConfigurationBuilder setPackages(String packages); + @BaselineIgnore("2.25.0") + ConfigurationBuilder setLoggerContext(@Nullable LoggerContext loggerContext); /** - * Sets whether the shutdown hook should be disabled. - * @param flag "disable" will prevent the shutdown hook from being set. - * @return this builder instance. + * Sets the configuration's "monitorInterval" attribute. + *

+ * The monitor interval specifies the number of seconds between checks for changes to the configuration source. + *

+ * @param intervalSeconds the number of seconds that should pass between checks of the configuration source + * @return this builder instance */ - ConfigurationBuilder setShutdownHook(String flag); + ConfigurationBuilder setMonitorInterval(int intervalSeconds); /** - * How long appenders and background tasks will get to shutdown when the JVM shuts down. - * Default is zero which mean that each appender uses its default timeout, and don't wait for background - * tasks. Not all appenders will honor this, it is a hint and not an absolute guarantee that the shutdown - * procedure will not take longer. Setting this too low increase the risk of losing outstanding log events - * not yet written to the final destination. (Not used if {@link #setShutdownHook(String)} is set to "disable".) - * @return this builder instance. + * Sets the configuration's "monitorInterval" attribute. + *

+ * The monitor interval specifies the number of seconds between checks for changes to the configuration source. + *

+ * @param intervalSeconds the number of seconds that should pass between checks of the configuration source + * @return this builder instance + * @throws NumberFormatException if the {@code intervalSeconds} argument is not a valid integer representation + */ + ConfigurationBuilder setMonitorInterval(String intervalSeconds); + + /** + * Sets the configuration's list of packages to search for Log4j plugins. + * @param packages a comma separated list of packages + * @return this builder (for chaining) + */ + ConfigurationBuilder setPackages(@Nullable String packages); + + /** + * Sets the configuration's "shutdownHook" attribute. + * @param flag "disable" will prevent the shutdown hook from being set. + * @return this builder (for chaining) + */ + ConfigurationBuilder setShutdownHook(@Nullable String flag); + + /** + * Sets the configuration's "shutdownTimeout" attribute. + *

+ * The shutdown-timeout specifies how long appenders and background tasks will get to shut down when the + * JVM is shutting down. + *

+ *

+ * The default is zero which means that each appender uses its default timeout, and doesn't wait for background + * tasks. Not all appenders will honor this, it is a hint and not an absolute guarantee that the shutdown + * procedure will not take longer. + *

+ *

+ * Setting the shutdown-timeout too low increase the risk of losing outstanding log events that have not yet + * been written to the final destination. + *

+ *

+ * This setting is ignored if {@link #setShutdownHook(String)} has been set to "disable". + *

+ * + * @param shutdownTimeoutMillis the shutdown timeout in milliseconds + * @return this builder (for chaining) + * @throws IllegalArgumentException if the {@code shutdownTimeoutMillis} argument is less than 0 + * @see LoggerContext#stop(long, TimeUnit) + */ + ConfigurationBuilder setShutdownTimeout(long shutdownTimeoutMillis); + + /** + * Sets the configuration's "shutdownTimeout" attribute. + *

+ * The shutdown-timeout specifies how long appenders and background tasks will get to shut down when the + * JVM is shutting down. + *

+ *

+ * The default is zero which means that each appender uses its default timeout, and doesn't wait for background + * tasks. Not all appenders will honor this, it is a hint and not an absolute guarantee that the shutdown + * procedure will not take longer. + *

+ *

+ * Setting the shutdown-timeout too low increase the risk of losing outstanding log events that have not yet + * been written to the final destination. + *

+ *

+ * This setting is ignored if {@link #setShutdownHook(String)} has been set to "disable". + *

* + * @param timeout the timeout (in the given {@code TimeUnit} + * @param timeUnit the time-unit used to convert the timeout to milliseconds + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code timeUnit} argument is {@code null} * @see LoggerContext#stop(long, TimeUnit) */ ConfigurationBuilder setShutdownTimeout(long timeout, TimeUnit timeUnit); @@ -452,35 +609,29 @@ public interface ConfigurationBuilder extends Builder setStatusLevel(Level level); /** - * Sets whether the logging should include constructing Plugins. - * @param verbosity "disable" will hide messages from plugin construction. - * @return this builder instance. + * Set a property with the given {@code key} and {@code value} on the root node. + * @param key the property key + * @param value the property value + * @return this builder (for chaining) + * @throws NullPointerException if the {@code key} argument is {@code null} */ - ConfigurationBuilder setVerbosity(String verbosity); + ConfigurationBuilder setRootProperty(String key, @Nullable String value); /** - * Specifies the destination for StatusLogger events. This can be {@code out} (default) for using - * {@link System#out standard out}, {@code err} for using {@link System#err standard error}, or a file URI to - * which log events will be written. If the provided URI is invalid, then the default destination of standard - * out will be used. - * - * @param destination where status log messages should be output. + * Sets whether the logging should include constructing Plugins. + * @param verbosity "disable" will hide messages from plugin construction. * @return this builder instance. */ - ConfigurationBuilder setDestination(String destination); + ConfigurationBuilder setVerbosity(@Nullable String verbosity); /** - * Sets the logger context. - * @param loggerContext the logger context. - */ - void setLoggerContext(LoggerContext loggerContext); - - /** - * Add the properties for the root node. - * @param key The property key. - * @param value The property value. - * @return this builder instance. + * Add a property with the given key and value to the configuration's root node. + * @param key the property key + * @param value the property value + * @return this builder (for chaining) + * @deprecated use {@link #setRootProperty(String, String)} */ + @Deprecated ConfigurationBuilder addRootProperty(String key, String value); /** diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.java index 735c3ce0906..f4c4380b278 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.java @@ -20,19 +20,31 @@ import org.apache.logging.log4j.core.config.builder.impl.DefaultConfigurationBuilder; /** - * Provides methods to create ConfigurationBuilders. + * A {@link ConfigurationBuilder} factory which generates configuration-builders that build + * a {@link BuiltConfiguration} or a derivative thereof. + * * @since 2.4 */ public abstract class ConfigurationBuilderFactory { /** - * Returns a new default ConfigurationBuilder to construct Log4j configurations. - * @return A new ConfigurationBuilder. + * Returns a new default {@link ConfigurationBuilder} of type {@link BuiltConfiguration} + * to construct Log4j configurations. + * + * @return the new configuration builder instance */ public static ConfigurationBuilder newConfigurationBuilder() { return new DefaultConfigurationBuilder<>(); } + /** + * Returns a new default {@link ConfigurationBuilder} which builds a specific implementation of + * {@link BuiltConfiguration}. + * + * @param the {@code BuiltConfiguration} implementation type + * @param clazz the class of the built-configuration implementation which should be built + * @return the new configuration builder instance + */ public static ConfigurationBuilder newConfigurationBuilder(final Class clazz) { return new DefaultConfigurationBuilder<>(clazz); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CustomLevelComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CustomLevelComponentBuilder.java index 77a56875cb3..f372a95fc29 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CustomLevelComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/CustomLevelComponentBuilder.java @@ -16,8 +16,30 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.config.CustomLevels; + /** - * Assembler for constructing CustomLevel Components. + * A builder interface for constructing and configuring {@link CustomLevels} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ -public interface CustomLevelComponentBuilder extends ComponentBuilder {} +public interface CustomLevelComponentBuilder extends ComponentBuilder { + + /** + * Sets the 'intLevel' attribute on the custom-level component. + *

+ * If the given {@code onMismatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param intLevel the integer level value + * @return this builder (for chaining) + */ + default CustomLevelComponentBuilder setIntLevelAttribute(int intLevel) { + return setAttribute("intLevel", intLevel); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterComponentBuilder.java index 9d3bebb0853..db3ff58edd2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterComponentBuilder.java @@ -16,8 +16,71 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Filter.Result; +import org.jspecify.annotations.Nullable; + /** - * Assembler for constructing Filter Components. + * A builder interface for constructing and configuring {@link Filter} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ -public interface FilterComponentBuilder extends ComponentBuilder {} +public interface FilterComponentBuilder extends ComponentBuilder { + + /** + * Sets the 'onMatch' attribute on the filter component. + *

+ * If the given {@code onMatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMatch the attribute value + * @return this builder (for chaining) + */ + default FilterComponentBuilder setOnMatchAttribute(@Nullable String onMatch) { + return setAttribute("onMatch", onMatch); + } + + /** + * Sets the 'onMatch' attribute on the filter component. + *

+ * If the given {@code onMatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMatch the attribute value + * @return this builder (for chaining) + */ + default FilterComponentBuilder setOnMatchAttribute(@Nullable Result onMatch) { + return setAttribute("onMatch", onMatch); + } + + /** + * Sets the 'onMismatch' attribute on the filter component. + *

+ * If the given {@code onMismatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMismatch the attribute value + * @return this builder (for chaining) + */ + default FilterComponentBuilder setOnMismatchAttribute(@Nullable String onMismatch) { + return setAttribute("onMismatch", onMismatch); + } + + /** + * Sets the 'onMismatch' attribute on the filter component. + *

+ * If the given {@code onMismatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param onMismatch the attribute value + * @return this builder (for chaining) + */ + default FilterComponentBuilder setOnMismatchAttribute(@Nullable Result onMismatch) { + return setAttribute("onMismatch", onMismatch); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterableComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterableComponentBuilder.java index 0bea40df8ad..87ed9f829a7 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterableComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/FilterableComponentBuilder.java @@ -25,9 +25,15 @@ public interface FilterableComponentBuilder> exten /** * Adds a Filter to the component. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

* - * @param assembler The FilterComponentBuilder with all of its attributes and sub components set. - * @return this Assembler. + * @param builder The {@code FilterComponentBuilder} with all of its attributes and subcomponents set. + * @return this component builder (for chaining) + * @throws NullPointerException if the {@code builder} argument is {@code null} */ - T add(FilterComponentBuilder assembler); + T add(FilterComponentBuilder builder); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/KeyValuePairComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/KeyValuePairComponentBuilder.java index 9724e9d56ee..5685f88c303 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/KeyValuePairComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/KeyValuePairComponentBuilder.java @@ -16,8 +16,44 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.util.KeyValuePair; +import org.jspecify.annotations.Nullable; + /** - * Assembler for constructing KeyValuePair Components. + * A builder interface for constructing and configuring {@link KeyValuePair} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.9 */ -public interface KeyValuePairComponentBuilder extends ComponentBuilder {} +public interface KeyValuePairComponentBuilder extends ComponentBuilder { + + /** + * Sets the 'key' attribute to the key-value pair component. + *

+ * If the given {@code key} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param key the attribute value + * @return this builder (for chaining) + */ + default KeyValuePairComponentBuilder setKeyAttribute(final @Nullable String key) { + return setAttribute("key", key); + } + + /** + * Sets the 'value' attribute to the key-value pair component. + *

+ * If the given {@code value} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param value the attribute value + * @return this builder (for chaining) + */ + default KeyValuePairComponentBuilder setValueAttribute(final @Nullable String value) { + return setAttribute("value", value); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LayoutComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LayoutComponentBuilder.java index 443aa143f72..69ade06be8d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LayoutComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LayoutComponentBuilder.java @@ -16,8 +16,16 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.Layout; + /** - * Assembler for constructing Layout Components. + * A builder interface for constructing and configuring {@link Layout} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ public interface LayoutComponentBuilder extends ComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggableComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggableComponentBuilder.java index d8dfd700e3a..17e63d8cb47 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggableComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggableComponentBuilder.java @@ -16,17 +16,97 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.config.LoggerConfig.RootLogger; +import org.jspecify.annotations.Nullable; + /** - * Common component builder for Logger and RootLogger elements. + * A common builder interface for constructing and configuring {@link Logger} and {@link RootLogger} + * components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

* * @since 2.6 */ public interface LoggableComponentBuilder> extends FilterableComponentBuilder { + + /** + * Add an {@link AppenderRefComponentBuilder} to this logger component builder. + *

+ * Note: the provided {@code builder} will be built by this method; therefore, it must be fully configured + * before calling this method. Changes to the builder after calling this method will not have + * any effect. + *

+ * + * @param builder The {@code AppenderRefComponentBuilder} with all of its attributes and subcomponents set. + * @return this component builder (for chaining) + * @throws NullPointerException if the given {@code builder} argument is {@code null} + */ + T add(AppenderRefComponentBuilder builder); + + /** + * Sets the "{@code additivity}" attribute on the logger component. + * + * @param additivity {@code true} if additive; otherwise, {@code false} + * @return this builder (for chaining) + */ + default T setAdditivityAttribute(boolean additivity) { + return setAttribute("additivity", additivity); + } + + /** + * Sets the "{@code additivity}" attribute on the logger component. + *

+ * If the given {@code additivity} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param additivity "{@code true}" if additive; otherwise, {@code false} + * @return this builder (for chaining) + */ + default T setAdditivityAttribute(String additivity) { + return setAttribute("additivity", additivity); + } + + /** + * Sets the "{@code includeLocation}" attribute on the loggable component. + *

+ * If the given {@code includeLocation} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param includeLocation {@code true} to include location information; otherwise, {@code false} + * @return this builder (for chaining) + */ + default T setIncludeLocationAttribute(boolean includeLocation) { + return setAttribute("includeLocation", includeLocation); + } + + /** + * Sets the "{@code level}" attribute on the loggable component. + *

+ * If the given {@code level} is {@code null}, the attribute will be removed from the component. + *

+ * + * @param level the level + * @return this builder (for chaining) + */ + default T setLevelAttribute(@Nullable String level) { + return setAttribute("level", level); + } + /** - * Add an Appender reference to the Logger component. + * Sets the "{@code level}" attribute on the loggable component. + *

+ * If the given {@code level} is {@code null}, the attribute will be removed from the component. + *

* - * @param assembler The AppenderRefComponentBuilder with all of its attributes and sub-components set. - * @return this Assembler. + * @param level the level + * @return this builder (for chaining) */ - T add(AppenderRefComponentBuilder assembler); + default T setLevelAttribute(@Nullable Level level) { + return setAttribute("level", level); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggerComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggerComponentBuilder.java index c5e94b76264..d8d313d1ca1 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggerComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/LoggerComponentBuilder.java @@ -16,8 +16,16 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.Logger; + /** - * Assembler for constructing Logger Components. + * A builder interface for constructing and configuring {@link Logger} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ public interface LoggerComponentBuilder extends LoggableComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/PropertyComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/PropertyComponentBuilder.java index 38bddb60358..a2f16a7fac6 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/PropertyComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/PropertyComponentBuilder.java @@ -16,8 +16,16 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.config.Property; + /** - * Assembler for constructing Property Components. + * A builder interface for constructing and configuring {@link Property} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.9 */ public interface PropertyComponentBuilder extends ComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/RootLoggerComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/RootLoggerComponentBuilder.java index 045aaa8dac4..4f850b2501d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/RootLoggerComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/RootLoggerComponentBuilder.java @@ -16,8 +16,17 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.config.LoggerConfig.RootLogger; + /** - * Assembler for constructing the root Logger Components. + * A builder interface for constructing and configuring {@link RootLogger} components in a Log4j + * configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.4 */ public interface RootLoggerComponentBuilder extends LoggableComponentBuilder {} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptComponentBuilder.java index f48910bdb68..ff94dacda2f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptComponentBuilder.java @@ -16,8 +16,44 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.script.Script; +import org.jspecify.annotations.Nullable; + /** - * Assembler for constructing Layout Components. + * A builder interface for constructing and configuring {@link Script} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.5 */ -public interface ScriptComponentBuilder extends ComponentBuilder {} +public interface ScriptComponentBuilder extends ComponentBuilder { + + /** + * Sets the 'language' attribute on the script component. + *

+ * If the given {@code language} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param language the script language + * @return this builder (for chaining) + */ + default ScriptComponentBuilder setLanguageAttribute(final @Nullable String language) { + return setAttribute("language", language); + } + + /** + * Sets the 'test' attribute on the script component. + *

+ * If the given {@code onMatch} argument is {@code} the attribute will be removed (if present). + *

+ * + * @param text the script text + * @return this builder (for chaining) + */ + default ScriptComponentBuilder setTextAttribute(final @Nullable String text) { + return setAttribute("text", text); + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptFileComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptFileComponentBuilder.java index fffc91744e3..c745d6dd971 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptFileComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/ScriptFileComponentBuilder.java @@ -16,17 +16,131 @@ */ package org.apache.logging.log4j.core.config.builder.api; +import org.apache.logging.log4j.core.script.ScriptFile; +import org.jspecify.annotations.Nullable; + /** - * Assembler for constructing ScriptFile Components. + * A builder interface for constructing and configuring {@link ScriptFile} components in a Log4j configuration. + * + *

+ * Instances of this builder are designed for single-threaded use and are not thread-safe. Developers + * should avoid sharing instances between threads. + *

+ * * @since 2.5 */ public interface ScriptFileComponentBuilder extends ComponentBuilder { - ScriptFileComponentBuilder addLanguage(String language); + /** + * Sets the '{@code language}' attribute for the script-file component. + *

+ * If the {@code language} argument is {@code null} the attribute will be removed. + *

+ * @param language the language + * @return this builder (for chaining) + */ + default ScriptFileComponentBuilder setLanguageAttribute(@Nullable String language) { + return setAttribute("language", language); + } + + /** + * Sets the '{@code isWatched}' attribute for the script-file component. + * @param isWatched {@code true} to watch the script file; otherwise, {@code false} + * @return this builder (for chaining) + */ + default ScriptFileComponentBuilder setIsWatchedAttribute(boolean isWatched) { + return setAttribute("isWatched", isWatched); + } + + /** + * Sets the '{@code isWatched}' attribute for the script-file component. + *

+ * If the {@code isWatched} argument is {@code null} the attribute will be removed. + *

+ * @param isWatched the string value of the flag + * @return this builder (for chaining) + */ + default ScriptFileComponentBuilder setIsWatchedAttribute(@Nullable String isWatched) { + return setAttribute("isWatched", isWatched); + } + + /** + * Sets the '{@code charset}' attribute for the script-file component. + *

+ * If the {@code charset} argument is {@code null} the attribute will be removed. + *

+ * @param charset the charset + * @return this builder (for chaining) + */ + default ScriptFileComponentBuilder setCharsetAttribute(@Nullable String charset) { + return setAttribute("charset", charset); + } + + /** + * Sets the '{@code path}' attribute for the script-file component. + *

+ * If the {@code path} argument is {@code null} the attribute will be removed. + *

+ * @param path the script file path + * @return this builder (for chaining) + */ + default ScriptFileComponentBuilder setPathAttribute(@Nullable String path) { + return setAttribute("path", path); + } + + /** + * Adds the '{@code language}' attribute for the script-file component. + *

+ * If the {@code language} argument is {@code null} the attribute will be removed. + *

+ * @param language the language + * @return this builder (for chaining) + * @deprecated use {@link #setLanguageAttribute(String)} + */ + @Deprecated + default ScriptFileComponentBuilder addLanguage(@Nullable String language) { + return setLanguageAttribute(language); + } - ScriptFileComponentBuilder addIsWatched(boolean isWatched); + /** + * Adds the '{@code isWatched}' attribute for the script-file component. + *

+ * If the {@code isWatched} argument is {@code null} the attribute will be removed. + *

+ * @param isWatched the string value of the flag + * @return this builder (for chaining) + * @deprecated use {@link #setIsWatchedAttribute(boolean)} (String)} + */ + @Deprecated + default ScriptFileComponentBuilder addIsWatched(boolean isWatched) { + return setIsWatchedAttribute(isWatched); + } - ScriptFileComponentBuilder addIsWatched(String isWatched); + /** + * Adds the '{@code isWatched}' attribute for the script-file component. + *

+ * If the {@code isWatched} argument is {@code null} the attribute will be removed. + *

+ * @param isWatched the string value of the flag + * @return this builder (for chaining) + * @deprecated use {@link #setIsWatchedAttribute(String)} + */ + @Deprecated + default ScriptFileComponentBuilder addIsWatched(String isWatched) { + return setIsWatchedAttribute(isWatched); + } - ScriptFileComponentBuilder addCharset(String charset); + /** + * Sets the '{@code charset}' attribute for the script-file component. + *

+ * If the {@code charset} argument is {@code null} the attribute will be removed. + *

+ * @param charset the charset + * @return this builder (for chaining) + * @deprecated use {@link #setCharsetAttribute(String)} + */ + @Deprecated + default ScriptFileComponentBuilder addCharset(String charset) { + return setCharsetAttribute(charset); + } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java index 273906a5d48..eb0da67a41f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/api/package-info.java @@ -20,8 +20,10 @@ * @since 2.4 */ @Export -@Version("2.20.1") +@NullMarked +@Version("2.25.0") package org.apache.logging.log4j.core.config.builder.api; +import org.jspecify.annotations.NullMarked; import org.osgi.annotation.bundle.Export; import org.osgi.annotation.versioning.Version; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java index f1ec35e06a0..c8cec5bd701 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/BuiltConfiguration.java @@ -20,38 +20,64 @@ import java.io.InputStream; import java.util.Arrays; import java.util.List; +import java.util.Objects; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.AbstractConfiguration; +import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.Node; import org.apache.logging.log4j.core.config.Reconfigurable; import org.apache.logging.log4j.core.config.builder.api.Component; -import org.apache.logging.log4j.core.config.plugins.util.PluginManager; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.plugins.util.PluginType; import org.apache.logging.log4j.core.config.status.StatusConfiguration; import org.apache.logging.log4j.core.util.Patterns; +import org.jspecify.annotations.Nullable; /** - * This is the general version of the Configuration created by the Builder. It may be extended to - * enhance its functionality. + * A {@link Configuration} implementation that is built using a {@link ConfigurationBuilder} implementaion. + *

+ * This class may be extended to enhance its functionality. + *

* * @since 2.4 */ public class BuiltConfiguration extends AbstractConfiguration { + + private static final String DEFAULT_CONTENT_TYPE = "text"; + private final StatusConfiguration statusConfig; - protected Component rootComponent; - private Component loggersComponent; - private Component appendersComponent; - private Component filtersComponent; - private Component propertiesComponent; - private Component customLevelsComponent; - private Component scriptsComponent; - private String contentType = "text"; + private String contentType = DEFAULT_CONTENT_TYPE; + + protected @Nullable Component rootComponent; + private @Nullable Component loggersComponent = null; + private @Nullable Component appendersComponent = null; + private @Nullable Component filtersComponent = null; + private @Nullable Component propertiesComponent = null; + private @Nullable Component customLevelsComponent = null; + private @Nullable Component scriptsComponent; + /** + * Constructs a new built-configuration instance. + *

+ * Important: this constructor is invoked via reflection in {@link DefaultConfigurationBuilder}. + * Do not change its signature! + *

+ * @param loggerContext the logger-context + * @param source the configuration-source + * @param rootComponent the root component to build the configuration's node tree from + */ public BuiltConfiguration( - final LoggerContext loggerContext, final ConfigurationSource source, final Component rootComponent) { + final @Nullable LoggerContext loggerContext, + final @Nullable ConfigurationSource source, + final Component rootComponent) { + super(loggerContext, source); + + Objects.requireNonNull(rootComponent, "The 'rootComponent' argument must not be null."); + statusConfig = new StatusConfiguration().withStatus(getDefaultStatus()); + for (final Component component : rootComponent.getComponents()) { switch (component.getPluginType()) { case "Scripts": { @@ -78,26 +104,44 @@ public BuiltConfiguration( customLevelsComponent = component; break; } + default: { + // NO-OP + break; + } } } + this.rootComponent = rootComponent; } + /** {@inheritDoc} */ @Override public void setup() { + final List children = rootNode.getChildren(); - if (propertiesComponent.getComponents().size() > 0) { + + if (propertiesComponent != null && !propertiesComponent.getComponents().isEmpty()) { children.add(convertToNode(rootNode, propertiesComponent)); } - if (scriptsComponent.getComponents().size() > 0) { + + if (scriptsComponent != null && !scriptsComponent.getComponents().isEmpty()) { children.add(convertToNode(rootNode, scriptsComponent)); } - if (customLevelsComponent.getComponents().size() > 0) { + + if (customLevelsComponent != null + && !customLevelsComponent.getComponents().isEmpty()) { children.add(convertToNode(rootNode, customLevelsComponent)); } - children.add(convertToNode(rootNode, loggersComponent)); - children.add(convertToNode(rootNode, appendersComponent)); - if (filtersComponent.getComponents().size() > 0) { + + if (loggersComponent != null && !loggersComponent.getComponents().isEmpty()) { + children.add(convertToNode(rootNode, loggersComponent)); + } + + if (appendersComponent != null && !appendersComponent.getComponents().isEmpty()) { + children.add(convertToNode(rootNode, appendersComponent)); + } + + if (filtersComponent != null && !filtersComponent.getComponents().isEmpty()) { if (filtersComponent.getComponents().size() == 1) { children.add( convertToNode(rootNode, filtersComponent.getComponents().get(0))); @@ -105,18 +149,18 @@ public void setup() { children.add(convertToNode(rootNode, filtersComponent)); } } - rootComponent = null; - } - public String getContentType() { - return this.contentType; - } - - public void setContentType(final String contentType) { - this.contentType = contentType; + rootComponent = null; } - public void createAdvertiser(final String advertiserString, final ConfigurationSource configSource) { + /** + * Creates an advertiser node. + * + * @param advertiserString the advertiser string + * @param configSource the configuration source + */ + public void createAdvertiser( + final @Nullable String advertiserString, final @Nullable ConfigurationSource configSource) { byte[] buffer = null; try { if (configSource != null) { @@ -126,38 +170,111 @@ public void createAdvertiser(final String advertiserString, final ConfigurationS } } } catch (final IOException ioe) { - LOGGER.warn("Unable to read configuration source " + configSource.toString()); + LOGGER.warn("Unable to read configuration source {}", configSource); } super.createAdvertiser(advertiserString, configSource, buffer, contentType); } + /** + * Gets the content type. + * + * @return returns the content-type + */ + public String getContentType() { + return this.contentType; + } + + /** + * Returns the status configuration of the {@code StatusLogger} fallback listener. + * @return the status configurations + */ public StatusConfiguration getStatusConfiguration() { return statusConfig; } - public void setPluginPackages(final String packages) { - pluginPackages.addAll(Arrays.asList(packages.split(Patterns.COMMA_SEPARATOR))); + /** + * Sets the content-type. + *

+ * If the given {@code contentType} is {@code null}, the default content-type "{@code text}" will be assigned. + *

+ * + * @param contentType the content-type + */ + public void setContentType(final @Nullable String contentType) { + this.contentType = (contentType != null) ? contentType : DEFAULT_CONTENT_TYPE; } + /** + * Sets the plugin-packages to scan for Log4j plugins. + * @param packages a comma-separated list of package names + */ + public void setPluginPackages(final @Nullable String packages) { + if (packages != null) { + pluginPackages.addAll(Arrays.asList(packages.split(Patterns.COMMA_SEPARATOR))); + } + } + + /** + * Sets the shutdown-hook flag. + * @param flag "{@code disable}" to disable the shutdown-hook; otherwise, enabled + */ public void setShutdownHook(final String flag) { isShutdownHookEnabled = !"disable".equalsIgnoreCase(flag); } + /** + * Sets the shutdown time in milliseconds. + *

+ * The shutdown-timeout specifies how long appenders and background tasks will get to shut down when the + * JVM is shutting down. + *

+ *

+ * The default is zero which means that each appender uses its default timeout, and doesn't wait for background + * tasks. Not all appenders will honor this, it is a hint and not an absolute guarantee that the shutdown + * procedure will not take longer. + *

+ *

+ * Setting the shutdown-timeout too low increase the risk of losing outstanding log events that have not yet + * been written to the final destination. + *

+ *

+ * This setting is ignored if {@link #setShutdownHook(String)} has been set to "disable". + *

+ * @param shutdownTimeoutMillis the shutdown timeout (in milliseconds) + */ public void setShutdownTimeoutMillis(final long shutdownTimeoutMillis) { this.shutdownTimeoutMillis = shutdownTimeoutMillis; } + /** + * Sets the configuration's "monitorInterval" attribute. + *

+ * The monitor interval specifies the number of seconds between checks for changes to the configuration source. + *

+ * + * @param intervalSeconds the monitor interval (in seconds) + */ public void setMonitorInterval(final int intervalSeconds) { if (this instanceof Reconfigurable && intervalSeconds > 0) { initializeWatchers((Reconfigurable) this, getConfigurationSource(), intervalSeconds); } } - @Override - public PluginManager getPluginManager() { - return pluginManager; - } - + /** + * Converts the given {@link Component} into a {@link Node} with the given {@code parent}. + *

+ * This method will recurse through the children of the given component building a node tree. + *

+ *

+ * Note: This method assigns the specified parent within the new child node; however, it does not + * add the new node to the parent. That is performed by the caller (which in most cases this + * method due to recursion). + *

+ * + * @param parent the parent node + * @param component the component to convert + * @return the converted node + */ protected Node convertToNode(final Node parent, final Component component) { final String name = component.getPluginType(); final PluginType pluginType = pluginManager.getPluginType(name); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderComponentBuilder.java index 6a936730e11..42e96706537 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderComponentBuilder.java @@ -16,29 +16,58 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import org.apache.logging.log4j.core.Appender; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** - * Holds the Appender Component attributes and subcomponents. + * A default implementation of the {@link AppenderComponentBuilder} interface for building + * an {@link Appender} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

* * @since 2.4 */ +@ProviderType class DefaultAppenderComponentBuilder extends DefaultComponentAndConfigurationBuilder implements AppenderComponentBuilder { + /** + * Constructs a new component builder instance. + * + * @param builder the configuration builder + * @param pluginType the plugin-type of the appender component to build + * @param name the appender name + * @throws NullPointerException if tthe {@code builder} argument is {@code null} + */ public DefaultAppenderComponentBuilder( - final DefaultConfigurationBuilder builder, final String name, final String type) { - super(builder, name, type); + final DefaultConfigurationBuilder builder, + final String pluginType, + final @Nullable String name) { + super(builder, pluginType, name); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public AppenderComponentBuilder add(final LayoutComponentBuilder builder) { return addComponent(builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public AppenderComponentBuilder add(final FilterComponentBuilder builder) { return addComponent(builder); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderRefComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderRefComponentBuilder.java index d63cfc4bec5..bfe567b3b06 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderRefComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultAppenderRefComponentBuilder.java @@ -16,24 +16,40 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import org.apache.logging.log4j.core.config.AppenderRef; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; +import org.osgi.annotation.versioning.ProviderType; /** - * Holds the Appender Component attributes and subcomponents. + * A default implementation of the {@link AppenderRefComponentBuilder} interface for building + * an {@link AppenderRef} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

* * @since 2.4 */ +@ProviderType class DefaultAppenderRefComponentBuilder extends DefaultComponentAndConfigurationBuilder implements AppenderRefComponentBuilder { - public DefaultAppenderRefComponentBuilder( - final DefaultConfigurationBuilder builder, final String ref) { + /** + * Constructs a new component builder instance. + * @param builder the configuration builder + * @throws NullPointerException if tthe {@code builder} argument is {@code null} + */ + public DefaultAppenderRefComponentBuilder(final DefaultConfigurationBuilder builder) { super(builder, "AppenderRef"); - addAttribute("ref", ref); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public AppenderRefComponentBuilder add(final FilterComponentBuilder builder) { return addComponent(builder); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java index 8b2b46aafd1..613a21dde66 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentAndConfigurationBuilder.java @@ -18,32 +18,64 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** - * Extends {@code DefaultComponentBuilder} to specify - * {@code DefaultConfigurationBuilder} as the - * {@code ConfigurationBuilder} type. + * Extends {@code DefaultComponentBuilder} to specify {@code DefaultConfigurationBuilder} + * as the {@code ConfigurationBuilder} type. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

* * @since 2.4 */ -class DefaultComponentAndConfigurationBuilder> +@ProviderType +abstract class DefaultComponentAndConfigurationBuilder> extends DefaultComponentBuilder> { - DefaultComponentAndConfigurationBuilder( - final DefaultConfigurationBuilder builder, - final String name, - final String type, - final String value) { - super(builder, name, type, value); + /** + * Constructs a new instance with the given plugin-type and {@code null} name and value. + * @param builder the configuration-builder + * @param pluginType the plugin-type of the component being built + * @throws NullPointerException if either the {@code builder} or {@code pluginType} arguments are {@code null} + */ + protected DefaultComponentAndConfigurationBuilder( + final DefaultConfigurationBuilder builder, final String pluginType) { + + this(builder, pluginType, null, null); } - DefaultComponentAndConfigurationBuilder( - final DefaultConfigurationBuilder builder, final String name, final String type) { - super(builder, name, type); + /** + * Constructs a new instance with the given plugin-type, name and {@code null} value. + * @param builder the configuration-builder + * @param pluginType the plugin-type of the component being built + * @param name the component name + * @throws NullPointerException if either the {@code builder} or {@code pluginType} arguments are {@code null} + */ + protected DefaultComponentAndConfigurationBuilder( + final DefaultConfigurationBuilder builder, + final String pluginType, + final @Nullable String name) { + + this(builder, pluginType, name, null); } - public DefaultComponentAndConfigurationBuilder( - final DefaultConfigurationBuilder builder, final String type) { - super(builder, type); + /** + * Constructs a new instance with the given plugin-type, name and value. + * @param builder the configuration-builder + * @param pluginType the plugin-type of the component being built + * @param name the component name + * @param value the component value + * @throws NullPointerException if either the {@code builder} or {@code pluginType} arguments are {@code null} + */ + protected DefaultComponentAndConfigurationBuilder( + final DefaultConfigurationBuilder builder, + final String pluginType, + final @Nullable String name, + final @Nullable String value) { + + super(builder, pluginType, name, value); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilder.java index 2983a243acc..8d4a33b12f2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultComponentBuilder.java @@ -17,104 +17,217 @@ package org.apache.logging.log4j.core.config.builder.impl; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.Component; import org.apache.logging.log4j.core.config.builder.api.ComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** - * Generic component that captures attributes and Components in preparation for assembling the Appender's - * Component. + * Generic base default {@link ComponentBuilder} implementation that captures attributes and children + * which are used to build a new {@link Component} instance. * + * @param the type of the component builder + * @param the type of the configuration builder * @since 2.4 */ +@ProviderType class DefaultComponentBuilder, CB extends ConfigurationBuilder> implements ComponentBuilder { private final CB builder; - private final String type; + private final String pluginType; private final Map attributes = new LinkedHashMap<>(); private final List components = new ArrayList<>(); - private final String name; - private final String value; - - public DefaultComponentBuilder(final CB builder, final String type) { - this(builder, null, type, null); + private final @Nullable String name; + private final @Nullable String value; + + /** + * Constructs a new instance with the given configuration builder and plugin-type with {@code null} name and value. + * @param builder the configuration builder + * @param pluginType the plugin-type of the component being built + * @throws NullPointerException if either {@code builder} or {@code type} argument is null + */ + public DefaultComponentBuilder(final CB builder, final String pluginType) { + this(builder, pluginType, null, null); } - public DefaultComponentBuilder(final CB builder, final String name, final String type) { - this(builder, name, type, null); + /** + * Constructs a new instancer with the given configuration builder, plugin-type, name, and {@code null} value. + * @param builder the configuration builder + * @param pluginType the plugin-type of the component being built + * @param name the component name + * @throws NullPointerException if either {@code builder} or {@code type} argument is null + */ + public DefaultComponentBuilder(final CB builder, final String pluginType, final @Nullable String name) { + this(builder, pluginType, name, null); } - public DefaultComponentBuilder(final CB builder, final String name, final String type, final String value) { - this.type = type; - this.builder = builder; + /** + * Constructs a new instance with the given configuration builder, plugin-type, name and value. + * @param builder the configuration builder + * @param pluginType the type (plugin-type) of the component being built + * @param name the component name + * @param value the component value + * @throws NullPointerException if either {@code builder} or {@code type} argument is null + */ + public DefaultComponentBuilder( + final CB builder, final String pluginType, final @Nullable String name, final @Nullable String value) { + super(); + this.builder = Objects.requireNonNull(builder, "The 'builder' argument must not be null."); + this.pluginType = Objects.requireNonNull(pluginType, "The 'type' argument must not be null."); this.name = name; this.value = value; } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final boolean value) { - return put(key, Boolean.toString(value)); + public Component build() { + final Component component = new Component(pluginType, name, value); + component.getAttributes().putAll(attributes); + component.getComponents().addAll(components); + return component; } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final Enum value) { - return put(key, value.name()); + public T addComponent(final ComponentBuilder builder) { + Objects.requireNonNull(builder, "The 'builder' argument must not be null."); + components.add(builder.build()); + return self(); } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final int value) { - return put(key, Integer.toString(value)); + public @NonNull CB getBuilder() { + return builder; } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final Level level) { - return put(key, level.toString()); + public @Nullable String getName() { + return name; } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final Object value) { - return put(key, value.toString()); + public T setAttribute(final String key, final boolean value) { + return putAttribute(key, Boolean.toString(value)); } + /** {@inheritDoc} */ @Override - public T addAttribute(final String key, final String value) { - return put(key, value); + public T setAttribute(final String key, final int value) { + return putAttribute(key, Integer.toString(value)); } + /** {@inheritDoc} */ @Override - @SuppressWarnings("unchecked") - public T addComponent(final ComponentBuilder builder) { - components.add(builder.build()); - return (T) this; + public T setAttribute(final String key, final @Nullable Enum value) { + return putAttribute(key, (value != null) ? value.name() : null); } + /** {@inheritDoc} */ @Override - public Component build() { - final Component component = new Component(type, name, value); - component.getAttributes().putAll(attributes); - component.getComponents().addAll(components); - return component; + public T setAttribute(final String key, final @Nullable Level level) { + return putAttribute(key, (level != null) ? level.toString() : null); } + /** {@inheritDoc} */ @Override - public CB getBuilder() { - return builder; + public T setAttribute(final String key, final @Nullable Object value) { + return putAttribute(key, Objects.toString(value, null)); } + /** {@inheritDoc} */ @Override - public String getName() { - return name; + public T setAttribute(final String key, final @Nullable String value) { + return putAttribute(key, value); + } + + /** + * Clears the internal state removing all configured attributes and components. + *

+ * This method is primarily intended to be used in testing. + *

+ */ + protected void clear() { + synchronized (this) { + attributes.clear(); + components.clear(); + } + } + + /** + * Gets the value of the component attribute with the given key. + * + * @param key the key + * @return the attribute value or {@code null} if not found + */ + protected @Nullable String getAttribute(final @NonNull String key) { + + Objects.requireNonNull(key, "The 'key' argument must not be null."); + + return this.attributes.get(key); + } + + /** + * Gets the map of key/value component attributes. + *

+ * The result map is guaranteed to have both non-{@code null} keys and values. + *

+ * @return an immutable map of the key/value attributes + */ + protected Map getAttributes() { + return Collections.unmodifiableMap(attributes); + } + + /** + * Gets the list of child components. + * @return an immutable list of the child components + */ + protected List getComponents() { + return Collections.unmodifiableList(this.components); + } + + /** + * Puts the given key/value pair to the attribute map. + *

+ * If the value is {@code null} the component attribute with the given {@code key} will + * instead be removed from the map. + *

+ * @param key the key + * @param value the value + * @return this builder (for chaining) + * @throws NullPointerException if the given {@code key} argument is {@code null} + */ + private T putAttribute(final @NonNull String key, final @Nullable String value) { + + Objects.requireNonNull(key, "The 'key' argument must not be null."); + + if (value != null) { + attributes.put(key, value); + } else { + attributes.remove(key); + } + + return self(); } + /** + * Returns an instance of this builder cast to its generic type. + * @return this builder + */ @SuppressWarnings("unchecked") - protected T put(final String key, final String value) { - attributes.put(key, value); + private T self() { return (T) this; } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCompositeFilterComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCompositeFilterComponentBuilder.java index 359644eb8de..e6e5bc670ae 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCompositeFilterComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCompositeFilterComponentBuilder.java @@ -19,24 +19,38 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.CompositeFilterComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; -import org.apache.logging.log4j.core.filter.AbstractFilter.AbstractFilterBuilder; +import org.apache.logging.log4j.core.filter.CompositeFilter; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link CompositeFilterComponentBuilder} interface for building + * a {@link CompositeFilter} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultCompositeFilterComponentBuilder extends DefaultComponentAndConfigurationBuilder implements CompositeFilterComponentBuilder { - public DefaultCompositeFilterComponentBuilder( - final DefaultConfigurationBuilder builder, - final String onMatch, - final String onMismatch) { + /** + * Constructs a new instance with the given configurration builder and default plugin-type "{@code Filters}". + * @param builder the configuration builder + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ + public DefaultCompositeFilterComponentBuilder(final DefaultConfigurationBuilder builder) { super(builder, "Filters"); - addAttribute(AbstractFilterBuilder.ATTR_ON_MATCH, onMatch); - addAttribute(AbstractFilterBuilder.ATTR_ON_MISMATCH, onMismatch); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public CompositeFilterComponentBuilder add(final FilterComponentBuilder builder) { return addComponent(builder); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java index 633e619b281..6a469a0ecc4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultConfigurationBuilder.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import aQute.bnd.annotation.baseline.BaselineIgnore; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.io.IOException; import java.io.OutputStream; @@ -24,24 +25,22 @@ import java.lang.reflect.Constructor; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.TimeUnit; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; -import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.core.Filter; +import org.apache.logging.log4j.core.Filter.Result; import org.apache.logging.log4j.core.LoggerContext; -import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.ConfigurationException; import org.apache.logging.log4j.core.config.ConfigurationSource; import org.apache.logging.log4j.core.config.LoggerConfig; @@ -59,57 +58,81 @@ import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; -import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.Throwables; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** + * A {@link ConfigurationBuilder} implementation for building a {@link BuiltConfiguration} or a custom + * implementation thereof. + * * @param The BuiltConfiguration type. * @since 2.4 */ +@ProviderType public class DefaultConfigurationBuilder implements ConfigurationBuilder { private static final String INDENT = " "; - private final Component root = new Component(); - private Component loggers; - private Component appenders; - private Component filters; - private Component properties; - private Component customLevels; - private Component scripts; + private final Component root = new Component("root"); + private final Component loggers; + private final Component appenders; + private final Component filters; + private final Component properties; + private final Component customLevels; + private final Component scripts; private final Class clazz; - private ConfigurationSource source; - private int monitorInterval; - private Level level; - private String destination; - private String packages; - private String shutdownFlag; - private long shutdownTimeoutMillis; - private String advertiser; - private LoggerContext loggerContext; - private String name; - + private @Nullable ConfigurationSource configurationSource = null; + private int monitorInterval = 0; + private @Nullable Level statusLevel; + private @Nullable String destination; + private @Nullable String packages; + private @Nullable String shutdownFlag; + private long shutdownTimeoutMillis = 0L; + private @Nullable String advertiser; + private @Nullable LoggerContext loggerContext; + private @Nullable String configurationName; + + /* static initialization */ @SuppressFBWarnings( value = {"XXE_DTD_TRANSFORM_FACTORY", "XXE_XSLT_TRANSFORM_FACTORY"}, justification = "This method only uses internally generated data.") - public static void formatXml(final Source source, final Result result) - throws TransformerConfigurationException, TransformerFactoryConfigurationError, TransformerException { + public static void formatXml(final Source source, final javax.xml.transform.Result result) + throws TransformerFactoryConfigurationError, TransformerException { final Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", Integer.toString(INDENT.length())); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.transform(source, result); } + /** + * Constructs a new configuration builder using the default {@link BuiltConfiguration} as + * a target configuration type. + */ @SuppressWarnings("unchecked") public DefaultConfigurationBuilder() { this((Class) BuiltConfiguration.class); root.addAttribute("name", "Built"); } + /** + * Constructs a new configuration builder with the given target {@link BuiltConfiguration} implementation class. + *

+ * Note the implementation will be instantiated per reflection and must have a constructor with the following + * parameters: + *

+ *
{@code
+     *   public BuiltConfigurationClazz(LogggerContext, ConfigurationSource, Component) {
+     *     ...
+     *   }
+     * }
+ * + * @param clazz the {@code BuiltConfiguration} implementation class + */ public DefaultConfigurationBuilder(final Class clazz) { - if (clazz == null) { - throw new IllegalArgumentException("A Configuration class must be provided"); - } + + Objects.requireNonNull(clazz, "The 'clazz' argument must not be null."); + this.clazz = clazz; final List components = root.getComponents(); properties = new Component("Properties"); @@ -126,41 +149,51 @@ public DefaultConfigurationBuilder(final Class clazz) { components.add(loggers); } - protected ConfigurationBuilder add(final Component parent, final ComponentBuilder builder) { - parent.getComponents().add(builder.build()); - return this; - } - + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public ConfigurationBuilder add(final AppenderComponentBuilder builder) { return add(appenders, builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public ConfigurationBuilder add(final CustomLevelComponentBuilder builder) { return add(customLevels, builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public ConfigurationBuilder add(final FilterComponentBuilder builder) { return add(filters, builder); } - @Override - public ConfigurationBuilder add(final ScriptComponentBuilder builder) { - return add(scripts, builder); - } - - @Override - public ConfigurationBuilder add(final ScriptFileComponentBuilder builder) { - return add(scripts, builder); - } - + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public ConfigurationBuilder add(final LoggerComponentBuilder builder) { return add(loggers, builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override public ConfigurationBuilder add(final RootLoggerComponentBuilder builder) { for (final Component c : loggers.getComponents()) { @@ -171,33 +204,94 @@ public ConfigurationBuilder add(final RootLoggerComponentBuilder builder) { return add(loggers, builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ + @Override + public ConfigurationBuilder add(final PropertyComponentBuilder builder) { + return add(properties, builder); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ + @Override + public ConfigurationBuilder add(final ScriptComponentBuilder builder) { + return add(scripts, builder); + } + + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ @Override - public ConfigurationBuilder addProperty(final String key, final String value) { - properties.addComponent(newComponent(key, "Property", value).build()); + public ConfigurationBuilder add(final ScriptFileComponentBuilder builder) { + return add(scripts, builder); + } + + /** + * Adds the {@code Component} built by the given {@code builder} as a child of the given {@code parent} component. + * @param parent the parent component + * @param builder the builder used to build the child componentt + * @return this builder (for chaining) + */ + protected ConfigurationBuilder add(final Component parent, final ComponentBuilder builder) { + Objects.requireNonNull(parent, "The 'parent' argument must not be null."); + Objects.requireNonNull(builder, "The 'builder' argument must not be null."); + parent.getComponents().add(builder.build()); return this; } + /** + * {@inheritDoc} + * + * @deprecated use {@link #add(PropertyComponentBuilder)} + */ + @Override + @Deprecated + public ConfigurationBuilder addProperty(@Nullable String name, @Nullable String value) { + return add(newProperty(name, value)); + } + + /** + * {@inheritDoc} + * + * @deprecated use {@link #setRootProperty(String, String)} + */ + @Override + @Deprecated + public ConfigurationBuilder addRootProperty(String key, String value) { + return setRootProperty(key, value); + } + + /** {@inheritDoc} */ @Override public T build() { return build(true); } + /** {@inheritDoc} */ @Override public T build(final boolean initialize) { T configuration; try { - if (source == null) { - source = ConfigurationSource.NULL_SOURCE; + if (configurationSource == null) { + configurationSource = ConfigurationSource.NULL_SOURCE; } final Constructor constructor = clazz.getConstructor(LoggerContext.class, ConfigurationSource.class, Component.class); - configuration = constructor.newInstance(loggerContext, source, root); + configuration = constructor.newInstance(loggerContext, configurationSource, root); configuration.getRootNode().getAttributes().putAll(root.getAttributes()); - if (name != null) { - configuration.setName(name); + if (configurationName != null) { + configuration.setName(configurationName); } - if (level != null) { - configuration.getStatusConfiguration().withStatus(level); + if (statusLevel != null) { + configuration.getStatusConfiguration().withStatus(statusLevel); } if (destination != null) { configuration.getStatusConfiguration().withDestination(destination); @@ -212,7 +306,7 @@ public T build(final boolean initialize) { configuration.setShutdownTimeoutMillis(shutdownTimeoutMillis); } if (advertiser != null) { - configuration.createAdvertiser(advertiser, source); + configuration.createAdvertiser(advertiser, configurationSource); } configuration.setMonitorInterval(monitorInterval); } catch (final Exception ex) { @@ -225,393 +319,620 @@ public T build(final boolean initialize) { return configuration; } - private String formatXml(final String xml) - throws TransformerConfigurationException, TransformerException, TransformerFactoryConfigurationError { - final StringWriter writer = new StringWriter(); - formatXml(new StreamSource(new StringReader(xml)), new StreamResult(writer)); - return writer.toString(); + /** {@inheritDoc} */ + @Override + public ScriptComponentBuilder newScript( + final @Nullable String name, final @Nullable String language, final @Nullable String text) { + return new DefaultScriptComponentBuilder(this, name) + .setLanguageAttribute(language) + .setTextAttribute(text); } + /** {@inheritDoc} */ @Override - public void writeXmlConfiguration(final OutputStream output) throws IOException { - try { - final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output); - writeXmlConfiguration(xmlWriter); - xmlWriter.close(); - } catch (final XMLStreamException e) { - if (e.getNestedException() instanceof IOException) { - throw (IOException) e.getNestedException(); - } - Throwables.rethrow(e); - } + public ScriptFileComponentBuilder newScriptFile(final @Nullable String path) { + return new DefaultScriptFileComponentBuilder(this, path).setPathAttribute(path); } + /** {@inheritDoc} */ @Override - public String toXmlConfiguration() { - final StringWriter writer = new StringWriter(); - try { - final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer); - writeXmlConfiguration(xmlWriter); - xmlWriter.close(); - return formatXml(writer.toString()); - } catch (final XMLStreamException | TransformerException e) { - Throwables.rethrow(e); - } - return writer.toString(); + public ScriptFileComponentBuilder newScriptFile(final @Nullable String name, final @Nullable String path) { + return new DefaultScriptFileComponentBuilder(this, (name != null) ? name : path).setPathAttribute(path); } - private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLStreamException { - xmlWriter.writeStartDocument(); - xmlWriter.writeStartElement("Configuration"); - if (name != null) { - xmlWriter.writeAttribute("name", name); - } - if (level != null) { - xmlWriter.writeAttribute("status", level.name()); - } - if (destination != null) { - xmlWriter.writeAttribute("dest", destination); - } - if (packages != null) { - xmlWriter.writeAttribute("packages", packages); - } - if (shutdownFlag != null) { - xmlWriter.writeAttribute("shutdownHook", shutdownFlag); - } - if (shutdownTimeoutMillis > 0) { - xmlWriter.writeAttribute("shutdownTimeout", String.valueOf(shutdownTimeoutMillis)); - } - if (advertiser != null) { - xmlWriter.writeAttribute("advertiser", advertiser); - } - if (monitorInterval > 0) { - xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval)); - } - - writeXmlSection(xmlWriter, properties); - writeXmlSection(xmlWriter, scripts); - writeXmlSection(xmlWriter, customLevels); - if (filters.getComponents().size() == 1) { - writeXmlComponent(xmlWriter, filters.getComponents().get(0)); - } else if (filters.getComponents().size() > 1) { - writeXmlSection(xmlWriter, filters); - } - writeXmlSection(xmlWriter, appenders); - writeXmlSection(xmlWriter, loggers); - - xmlWriter.writeEndElement(); // "Configuration" - xmlWriter.writeEndDocument(); + /** {@inheritDoc} */ + @Override + public AppenderComponentBuilder newAppender(final @Nullable String name, final String type) { + return new DefaultAppenderComponentBuilder(this, type, name); } - private void writeXmlSection(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException { - if (!component.getAttributes().isEmpty() - || !component.getComponents().isEmpty() - || component.getValue() != null) { - writeXmlComponent(xmlWriter, component); - } + /** {@inheritDoc} */ + @Override + public AppenderRefComponentBuilder newAppenderRef(final @Nullable String ref) { + return new DefaultAppenderRefComponentBuilder(this).setRefAttribute(ref); } - private void writeXmlComponent(final XMLStreamWriter xmlWriter, final Component component) - throws XMLStreamException { - if (!component.getComponents().isEmpty() || component.getValue() != null) { - xmlWriter.writeStartElement(component.getPluginType()); - writeXmlAttributes(xmlWriter, component); - for (final Component subComponent : component.getComponents()) { - writeXmlComponent(xmlWriter, subComponent); - } - if (component.getValue() != null) { - xmlWriter.writeCharacters(component.getValue()); - } - xmlWriter.writeEndElement(); - } else { - xmlWriter.writeEmptyElement(component.getPluginType()); - writeXmlAttributes(xmlWriter, component); - } + /** {@inheritDoc} */ + @Override + public LoggerComponentBuilder newAsyncLogger(final @Nullable String name) { + return new DefaultLoggerComponentBuilder(this, "AsyncLogger", name); } - private void writeXmlAttributes(final XMLStreamWriter xmlWriter, final Component component) - throws XMLStreamException { - for (final Map.Entry attribute : - component.getAttributes().entrySet()) { - xmlWriter.writeAttribute(attribute.getKey(), attribute.getValue()); - } + /** {@inheritDoc} */ + @Override + public LoggerComponentBuilder newAsyncLogger(final @Nullable String name, final boolean includeLocation) { + return this.newAsyncLogger(name).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public ScriptComponentBuilder newScript(final String name, final String language, final String text) { - return new DefaultScriptComponentBuilder(this, name, language, text); + public LoggerComponentBuilder newAsyncLogger(final @Nullable String name, final @Nullable Level level) { + return this.newAsyncLogger(name).setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public ScriptFileComponentBuilder newScriptFile(final String path) { - return new DefaultScriptFileComponentBuilder(this, path, path); + public LoggerComponentBuilder newAsyncLogger( + final @Nullable String name, final @Nullable Level level, final boolean includeLocation) { + return this.newAsyncLogger(name) + .setIncludeLocationAttribute(includeLocation) + .setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public ScriptFileComponentBuilder newScriptFile(final String name, final String path) { - return new DefaultScriptFileComponentBuilder(this, name, path); + public LoggerComponentBuilder newAsyncLogger(final @Nullable String name, final @Nullable String level) { + return this.newAsyncLogger(name).setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public AppenderComponentBuilder newAppender(final String name, final String type) { - return new DefaultAppenderComponentBuilder(this, name, type); + public LoggerComponentBuilder newAsyncLogger( + final @Nullable String name, final @Nullable String level, final boolean includeLocation) { + return this.newAsyncLogger(name).setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public AppenderRefComponentBuilder newAppenderRef(final String ref) { - return new DefaultAppenderRefComponentBuilder(this, ref); + public RootLoggerComponentBuilder newAsyncRootLogger() { + return new DefaultRootLoggerComponentBuilder(this, "AsyncRoot"); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name) { - return new DefaultLoggerComponentBuilder(this, name, null, "AsyncLogger"); + public RootLoggerComponentBuilder newAsyncRootLogger(final boolean includeLocation) { + return newAsyncRootLogger().setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, null, "AsyncLogger", includeLocation); + public RootLoggerComponentBuilder newAsyncRootLogger(final @Nullable Level level) { + return newAsyncRootLogger().setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name, final Level level) { - return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger"); + public RootLoggerComponentBuilder newAsyncRootLogger(final @Nullable Level level, final boolean includeLocation) { + return newAsyncRootLogger().setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name, final Level level, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, level.toString(), "AsyncLogger", includeLocation); + public RootLoggerComponentBuilder newAsyncRootLogger(final @Nullable String level) { + return newAsyncRootLogger().setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name, final String level) { - return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger"); + public RootLoggerComponentBuilder newAsyncRootLogger(final @Nullable String level, final boolean includeLocation) { + return newAsyncRootLogger().setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newAsyncLogger(final String name, final String level, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, level, "AsyncLogger", includeLocation); + public > ComponentBuilder newComponent(final String pluginType) { + return newComponent(null, pluginType, null); } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger() { - return new DefaultRootLoggerComponentBuilder(this, "AsyncRoot"); + public > ComponentBuilder newComponent( + final @Nullable String name, final String pluginType) { + return this.newComponent(name, pluginType, null); } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger(final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, null, "AsyncRoot", includeLocation); + public > ComponentBuilder newComponent( + final @Nullable String name, final String pluginType, final @Nullable String value) { + return new DefaultComponentBuilder<>(this, pluginType, name, value); } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger(final Level level) { - return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot"); + public PropertyComponentBuilder newProperty(final @Nullable String name, final @Nullable String value) { + return new DefaultPropertyComponentBuilder(this, name, value); } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger(final Level level, final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, level.toString(), "AsyncRoot", includeLocation); + public KeyValuePairComponentBuilder newKeyValuePair(final @Nullable String key, final @Nullable String value) { + return new DefaultKeyValuePairComponentBuilder(this) + .setKeyAttribute(key) + .setValueAttribute(value); } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger(final String level) { - return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot"); + public CustomLevelComponentBuilder newCustomLevel(final @Nullable String name, final int intLevel) { + return new DefaultCustomLevelComponentBuilder(this, name).setIntLevelAttribute(intLevel); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code pluginType} argument is {@code null} + */ @Override - public RootLoggerComponentBuilder newAsyncRootLogger(final String level, final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, level, "AsyncRoot", includeLocation); + public FilterComponentBuilder newFilter(final String pluginType) { + return new DefaultFilterComponentBuilder(this, pluginType); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code pluginType} argument is {@code null} + */ @Override - public > ComponentBuilder newComponent(final String type) { - return new DefaultComponentBuilder<>(this, type); + public FilterComponentBuilder newFilter( + final String pluginType, final @Nullable Result onMatch, final @Nullable Result onMismatch) { + return newFilter(pluginType).setOnMatchAttribute(onMatch).setOnMismatchAttribute(onMismatch); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code pluginType} argument is {@code null} + */ @Override - public > ComponentBuilder newComponent(final String name, final String type) { - return new DefaultComponentBuilder<>(this, name, type); + public FilterComponentBuilder newFilter( + final String pluginType, final @Nullable String onMatch, final @Nullable String onMismatch) { + return newFilter(pluginType).setOnMatchAttribute(onMatch).setOnMismatchAttribute(onMismatch); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code pluginType} argument is {@code null} + */ @Override - public > ComponentBuilder newComponent( - final String name, final String type, final String value) { - return new DefaultComponentBuilder<>(this, name, type, value); + public LayoutComponentBuilder newLayout(final String pluginType) { + return new DefaultLayoutComponentBuilder(this, pluginType); } + /** {@inheritDoc} */ @Override - public PropertyComponentBuilder newProperty(final String name, final String value) { - return new DefaultPropertyComponentBuilder(this, name, value); + public LoggerComponentBuilder newLogger(final @Nullable String name) { + return new DefaultLoggerComponentBuilder(this, name); } + /** {@inheritDoc} */ @Override - public KeyValuePairComponentBuilder newKeyValuePair(final String key, final String value) { - return new DefaultKeyValuePairComponentBuilder(this, key, value); + public LoggerComponentBuilder newLogger(final @Nullable String name, final boolean includeLocation) { + return newLogger(name).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public CustomLevelComponentBuilder newCustomLevel(final String name, final int level) { - return new DefaultCustomLevelComponentBuilder(this, name, level); + public LoggerComponentBuilder newLogger(final @Nullable String name, final @Nullable Level level) { + return newLogger(name).setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public FilterComponentBuilder newFilter( - final String type, final Filter.Result onMatch, final Filter.Result onMismatch) { - return new DefaultFilterComponentBuilder(this, type, onMatch.name(), onMismatch.name()); + public LoggerComponentBuilder newLogger( + final @Nullable String name, final @Nullable Level level, final boolean includeLocation) { + return newLogger(name).setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public FilterComponentBuilder newFilter(final String type, final String onMatch, final String onMismatch) { - return new DefaultFilterComponentBuilder(this, type, onMatch, onMismatch); + public LoggerComponentBuilder newLogger(final @Nullable String name, final @Nullable String level) { + return newLogger(name).setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public LayoutComponentBuilder newLayout(final String type) { - return new DefaultLayoutComponentBuilder(this, type); + public LoggerComponentBuilder newLogger( + final @Nullable String name, final @Nullable String level, final boolean includeLocation) { + return newLogger(name).setIncludeLocationAttribute(includeLocation).setLevelAttribute(level); } @Override - public LoggerComponentBuilder newLogger(final String name) { - return new DefaultLoggerComponentBuilder(this, name, null); + public RootLoggerComponentBuilder newRootLogger() { + return new DefaultRootLoggerComponentBuilder(this); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newLogger(final String name, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, null, includeLocation); + public RootLoggerComponentBuilder newRootLogger(final boolean includeLocation) { + return newRootLogger().setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newLogger(final String name, final Level level) { - return new DefaultLoggerComponentBuilder(this, name, level.toString()); + public RootLoggerComponentBuilder newRootLogger(final @Nullable Level level) { + return newRootLogger().setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newLogger(final String name, final Level level, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, level.toString(), includeLocation); + public RootLoggerComponentBuilder newRootLogger(final @Nullable Level level, final boolean includeLocation) { + return newRootLogger().setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newLogger(final String name, final String level) { - return new DefaultLoggerComponentBuilder(this, name, level); + public RootLoggerComponentBuilder newRootLogger(final @Nullable String level) { + return newRootLogger().setLevelAttribute(level); } + /** {@inheritDoc} */ @Override - public LoggerComponentBuilder newLogger(final String name, final String level, final boolean includeLocation) { - return new DefaultLoggerComponentBuilder(this, name, level, includeLocation); + public RootLoggerComponentBuilder newRootLogger(final @Nullable String level, final boolean includeLocation) { + return newRootLogger().setLevelAttribute(level).setIncludeLocationAttribute(includeLocation); } - @Override - public RootLoggerComponentBuilder newRootLogger() { - return new DefaultRootLoggerComponentBuilder(this, null); + /** + * Returns the advertiser. + * @return the advertiser or {@code null} if undefined + */ + protected @Nullable String getAdvertiser() { + return this.advertiser; } - @Override - public RootLoggerComponentBuilder newRootLogger(final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, null, includeLocation); + /** + * Returns the configuration name. + * @return the configuration name or {@code null} if undefined + */ + protected @Nullable String getConfigurationName() { + return this.configurationName; + } + + /** + * Returns the configuration source. + * + * @return the configuration source or {@code null} if undefined + */ + protected @Nullable ConfigurationSource getConfigurationSource() { + return this.configurationSource; + } + + /** + * Returns the configuration's destination. + * @return the destination or {@code null} if undefined + */ + protected @Nullable String getDestination() { + return this.destination; + } + + /** + * Returns the configuration's logger-context. + * @return the logger-context or {@code null} if undefined + */ + protected @Nullable LoggerContext getLoggerContext() { + return this.loggerContext; + } + + /** + * Returns the configuration's monitor interval in seconds. + * @return the monitor interval (in seconds) + */ + protected int getMonitorInterval() { + return this.monitorInterval; + } + + /** + * Returns the comma-separated of package-names to search for plugins. + * @return the packages or {@code null} if undefined + */ + protected @Nullable String getPackages() { + return this.packages; + } + + /** + * Returns the root property with the given key. + * @param key the key + * @return the property value or {@code null} if undefined + * @throws NullPointerException if the given {@code key} argument is {@code null} + */ + protected @Nullable String getRootProperty(String key) { + + Objects.requireNonNull(key, "The 'key' argument must not be null."); + + return this.root.getAttributes().get(key); + } + + /** + * Returns the configuration's shutdown timeout in milliseconds. + * @return the shutdown timeout (in millis) + */ + protected long getShutdownTimeout() { + return this.shutdownTimeoutMillis; + } + + /** + * Returns the configuration's status logger level. + * @return the status logger level or {@code null} if undefined + */ + protected @Nullable Level getStatusLevel() { + return this.statusLevel; } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newRootLogger(final Level level) { - return new DefaultRootLoggerComponentBuilder(this, level.toString()); + public ConfigurationBuilder setAdvertiser(final @Nullable String advertiser) { + this.advertiser = advertiser; + return this; } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newRootLogger(final Level level, final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, level.toString(), includeLocation); + public ConfigurationBuilder setConfigurationName(final @Nullable String name) { + this.configurationName = name; + return this; } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newRootLogger(final String level) { - return new DefaultRootLoggerComponentBuilder(this, level); + public ConfigurationBuilder setConfigurationSource(final @Nullable ConfigurationSource configurationSource) { + this.configurationSource = configurationSource; + return this; } + /** {@inheritDoc} */ @Override - public RootLoggerComponentBuilder newRootLogger(final String level, final boolean includeLocation) { - return new DefaultRootLoggerComponentBuilder(this, level, includeLocation); + public ConfigurationBuilder setDestination(final @Nullable String destination) { + this.destination = destination; + return this; } + /** {@inheritDoc} */ @Override - public ConfigurationBuilder setAdvertiser(final String advertiser) { - this.advertiser = advertiser; + @BaselineIgnore("2.25.0") + public ConfigurationBuilder setLoggerContext(final @Nullable LoggerContext loggerContext) { + this.loggerContext = loggerContext; return this; } /** - * Set the name of the configuration. + * {@inheritDoc} * - * @param name the name of the {@link Configuration}. By default is {@code "Assembled"}. - * @return this builder instance + * @throws NumberFormatException if the {@code intervalSeconds} argument is not a valid integer representation */ @Override - public ConfigurationBuilder setConfigurationName(final String name) { - this.name = name; + public ConfigurationBuilder setMonitorInterval(final int intervalSeconds) { + if (intervalSeconds >= 0) { + monitorInterval = intervalSeconds; + } else { + throw new IllegalArgumentException("The monitor interval must be greater than or equal to 0."); + } return this; } /** - * Set the ConfigurationSource. + * {@inheritDoc} * - * @param configurationSource the {@link ConfigurationSource} - * @return this builder instance + * @throws NumberFormatException if the {@code intervalSeconds} argument is not a valid integer representation */ @Override - public ConfigurationBuilder setConfigurationSource(final ConfigurationSource configurationSource) { - source = configurationSource; - return this; + public ConfigurationBuilder setMonitorInterval(final @Nullable String intervalSeconds) { + return setMonitorInterval(intervalSeconds != null ? Integer.parseInt(intervalSeconds) : 0); } + /** {@inheritDoc} */ @Override - public ConfigurationBuilder setMonitorInterval(final String intervalSeconds) { - monitorInterval = Integers.parseInt(intervalSeconds); + public ConfigurationBuilder setPackages(final @Nullable String packages) { + this.packages = packages; return this; } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code key} argument is {@code null} + */ @Override - public ConfigurationBuilder setPackages(final String packages) { - this.packages = packages; + public ConfigurationBuilder setRootProperty(final String key, final @Nullable String value) { + + Objects.requireNonNull(key, "The 'key' argument must not be null."); + + if (value != null) { + root.getAttributes().put(key, value); + } else { + root.getAttributes().remove(key); + } + return this; } + /** {@inheritDoc} */ @Override - public ConfigurationBuilder setShutdownHook(final String flag) { + public ConfigurationBuilder setShutdownHook(final @Nullable String flag) { this.shutdownFlag = flag; return this; } + /** + * {@inheritDoc} + * + * @throws IllegalArgumentException if the {@code shutdownTimeoutMillis} argument is invalid + */ @Override - public ConfigurationBuilder setShutdownTimeout(final long timeout, final TimeUnit timeUnit) { - this.shutdownTimeoutMillis = timeUnit.toMillis(timeout); + public ConfigurationBuilder setShutdownTimeout(final long shutdownTimeoutMillis) { + if (shutdownTimeoutMillis >= 0) { + this.shutdownTimeoutMillis = shutdownTimeoutMillis; + } else { + throw new IllegalArgumentException( + "The 'shutdownTimeoutMillis' argument must be greater than " + "or equal to 0."); + } return this; } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the given {@code timeUnit} argument is {@code null} + */ @Override - public ConfigurationBuilder setStatusLevel(final Level level) { - this.level = level; + public ConfigurationBuilder setShutdownTimeout(final long shutdownTimeout, final TimeUnit timeUnit) { + Objects.requireNonNull(timeUnit, "The 'timeUnit' argument must not be null."); + this.shutdownTimeoutMillis = timeUnit.toMillis(shutdownTimeout); return this; } /** - * @deprecated This method is ineffective and only kept for binary backward compatibility. + * Sets the status level. + * @param level the configuration status logging level + * @return this builder (for chaining) */ @Override - @Deprecated - public ConfigurationBuilder setVerbosity(final String verbosity) { + public ConfigurationBuilder setStatusLevel(final @Nullable Level level) { + this.statusLevel = level; return this; } + /** + * Sets the verbosity. + * @param verbosity the configuration verbosity + * @return this component builder (for chaining) + * @deprecated This method is ineffective and only kept for binary backward compatibility. + */ @Override - public ConfigurationBuilder setDestination(final String destination) { - this.destination = destination; + @Deprecated + public ConfigurationBuilder setVerbosity(final @Nullable String verbosity) { return this; } + // XML generation + + private String formatXml(final String xml) throws TransformerException, TransformerFactoryConfigurationError { + Objects.requireNonNull(xml, "The 'xml' argument must not be null."); + final StringWriter writer = new StringWriter(); + formatXml(new StreamSource(new StringReader(xml)), new StreamResult(writer)); + return writer.toString(); + } + @Override - public void setLoggerContext(final LoggerContext loggerContext) { - this.loggerContext = loggerContext; + public void writeXmlConfiguration(final OutputStream output) throws IOException { + Objects.requireNonNull(output, "The 'output' argument must not be null."); + try { + final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(output); + writeXmlConfiguration(xmlWriter); + xmlWriter.close(); + } catch (final XMLStreamException e) { + if (e.getNestedException() instanceof IOException) { + throw (IOException) e.getNestedException(); + } + Throwables.rethrow(e); + } } @Override - public ConfigurationBuilder addRootProperty(final String key, final String value) { - root.getAttributes().put(key, value); - return this; + public String toXmlConfiguration() { + final StringWriter writer = new StringWriter(); + try { + final XMLStreamWriter xmlWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(writer); + writeXmlConfiguration(xmlWriter); + xmlWriter.close(); + return formatXml(writer.toString()); + } catch (final XMLStreamException | TransformerException e) { + Throwables.rethrow(e); + } + return writer.toString(); + } + + private void writeXmlConfiguration(final XMLStreamWriter xmlWriter) throws XMLStreamException { + Objects.requireNonNull(xmlWriter, "The 'xmlWriter' argument must not be null."); + xmlWriter.writeStartDocument(); + xmlWriter.writeStartElement("Configuration"); + if (configurationName != null) { + xmlWriter.writeAttribute("name", configurationName); + } + if (statusLevel != null) { + xmlWriter.writeAttribute("status", statusLevel.name()); + } + if (destination != null) { + xmlWriter.writeAttribute("dest", destination); + } + if (packages != null) { + xmlWriter.writeAttribute("packages", packages); + } + if (shutdownFlag != null) { + xmlWriter.writeAttribute("shutdownHook", shutdownFlag); + } + if (shutdownTimeoutMillis > 0) { + xmlWriter.writeAttribute("shutdownTimeout", String.valueOf(shutdownTimeoutMillis)); + } + if (advertiser != null) { + xmlWriter.writeAttribute("advertiser", advertiser); + } + if (monitorInterval > 0) { + xmlWriter.writeAttribute("monitorInterval", String.valueOf(monitorInterval)); + } + + writeXmlSection(xmlWriter, properties); + writeXmlSection(xmlWriter, scripts); + writeXmlSection(xmlWriter, customLevels); + if (filters.getComponents().size() == 1) { + writeXmlComponent(xmlWriter, filters.getComponents().get(0)); + } else if (filters.getComponents().size() > 1) { + writeXmlSection(xmlWriter, filters); + } + writeXmlSection(xmlWriter, appenders); + writeXmlSection(xmlWriter, loggers); + + xmlWriter.writeEndElement(); // "Configuration" + xmlWriter.writeEndDocument(); + } + + private void writeXmlSection(final XMLStreamWriter xmlWriter, final Component component) throws XMLStreamException { + Objects.requireNonNull(xmlWriter, "The 'xmlWriter' argument must not be null."); + Objects.requireNonNull(component, "The 'component' argument must not be null."); + if (!component.getAttributes().isEmpty() + || !component.getComponents().isEmpty() + || component.getValue() != null) { + writeXmlComponent(xmlWriter, component); + } + } + + private void writeXmlComponent(final XMLStreamWriter xmlWriter, final Component component) + throws XMLStreamException { + Objects.requireNonNull(xmlWriter, "The 'xmlWriter' argument must not be null."); + Objects.requireNonNull(component, "The 'component' argument must not be null."); + if (!component.getComponents().isEmpty() || component.getValue() != null) { + xmlWriter.writeStartElement(component.getPluginType()); + writeXmlAttributes(xmlWriter, component); + for (final Component subComponent : component.getComponents()) { + writeXmlComponent(xmlWriter, subComponent); + } + if (component.getValue() != null) { + xmlWriter.writeCharacters(component.getValue()); + } + xmlWriter.writeEndElement(); + } else { + xmlWriter.writeEmptyElement(component.getPluginType()); + writeXmlAttributes(xmlWriter, component); + } + } + + private void writeXmlAttributes(final XMLStreamWriter xmlWriter, final Component component) + throws XMLStreamException { + Objects.requireNonNull(xmlWriter, "The 'xmlWriter' argument must not be null."); + Objects.requireNonNull(component, "The 'component' argument must not be null."); + for (final Map.Entry attribute : + component.getAttributes().entrySet()) { + xmlWriter.writeAttribute(attribute.getKey(), attribute.getValue()); + } } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCustomLevelComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCustomLevelComponentBuilder.java index 12f0b2b022a..324fe1f6986 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCustomLevelComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultCustomLevelComponentBuilder.java @@ -17,17 +17,34 @@ package org.apache.logging.log4j.core.config.builder.impl; import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.CustomLevelConfig; import org.apache.logging.log4j.core.config.builder.api.CustomLevelComponentBuilder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link CustomLevelComponentBuilder} interface for building + * a {@link CustomLevelConfig} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultCustomLevelComponentBuilder extends DefaultComponentAndConfigurationBuilder implements CustomLevelComponentBuilder { + /** + * Constructs a new component builder instance. + * @param builder the configuration builder + * @param name the component name + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ public DefaultCustomLevelComponentBuilder( - final DefaultConfigurationBuilder builder, final String name, final int level) { - super(builder, name, "CustomLevel"); - addAttribute("intLevel", level); + final DefaultConfigurationBuilder builder, final @Nullable String name) { + + super(builder, "CustomLevel", name); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultFilterComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultFilterComponentBuilder.java index 8a7acab7379..f743a07705c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultFilterComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultFilterComponentBuilder.java @@ -16,23 +16,34 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import org.apache.logging.log4j.core.Filter; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; -import org.apache.logging.log4j.core.filter.AbstractFilter.AbstractFilterBuilder; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link FilterComponentBuilder} interface for building + * a {@link Filter} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultFilterComponentBuilder extends DefaultComponentAndConfigurationBuilder implements FilterComponentBuilder { + /** + * Create a new filter component builder instance with the given plugin-type. + * @param builder the configuration builder. + * @param pluginType the plugin-type of the filter component to build + * @throws NullPointerException if either the {@code builder} or {@code pluginType} argument is {@code null} + */ public DefaultFilterComponentBuilder( - final DefaultConfigurationBuilder builder, - final String type, - final String onMatch, - final String onMismatch) { - super(builder, type); - addAttribute(AbstractFilterBuilder.ATTR_ON_MATCH, onMatch); - addAttribute(AbstractFilterBuilder.ATTR_ON_MISMATCH, onMismatch); + final DefaultConfigurationBuilder builder, final String pluginType) { + + super(builder, pluginType); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultKeyValuePairComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultKeyValuePairComponentBuilder.java index 9394232a717..9df60b012fa 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultKeyValuePairComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultKeyValuePairComponentBuilder.java @@ -18,17 +18,29 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.KeyValuePairComponentBuilder; +import org.apache.logging.log4j.core.util.KeyValuePair; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link KeyValuePairComponentBuilder} interface for building + * a {@link KeyValuePair} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.9 */ +@ProviderType class DefaultKeyValuePairComponentBuilder extends DefaultComponentAndConfigurationBuilder implements KeyValuePairComponentBuilder { - public DefaultKeyValuePairComponentBuilder( - final DefaultConfigurationBuilder builder, final String key, final String value) { + /** + * Create a new key-value pair component builder instance with the default plugin-type "{@code KeyValuePair}". + * @param builder the configuration builder + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ + public DefaultKeyValuePairComponentBuilder(final DefaultConfigurationBuilder builder) { super(builder, "KeyValuePair"); - addAttribute("key", key); - addAttribute("value", value); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLayoutComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLayoutComponentBuilder.java index fcfe497ba36..6f348bba3a8 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLayoutComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLayoutComponentBuilder.java @@ -16,17 +16,34 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import org.apache.logging.log4j.core.Layout; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.LayoutComponentBuilder; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link LayoutComponentBuilder} interface for building + * a {@link Layout} component in a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultLayoutComponentBuilder extends DefaultComponentAndConfigurationBuilder implements LayoutComponentBuilder { + /** + * Create a new layout component builder instance with the given plugin-type. + * + * @param builder the configuration builder + * @param pluginType the target plugin-type of the logger component + * @throws NullPointerException if either the {@code builder} or {@code pluginType} argument is {@code null} + */ public DefaultLayoutComponentBuilder( - final DefaultConfigurationBuilder builder, final String type) { - super(builder, type); + final DefaultConfigurationBuilder builder, final String pluginType) { + super(builder, pluginType); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLoggerComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLoggerComponentBuilder.java index a7cab3d22c5..44ade085f0c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLoggerComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultLoggerComponentBuilder.java @@ -16,94 +16,67 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.LoggerComponentBuilder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link LoggerComponentBuilder} interface for building + * a {@link Logger} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultLoggerComponentBuilder extends DefaultComponentAndConfigurationBuilder implements LoggerComponentBuilder { /** - * Configure a logger. - * @param builder - * @param name - * @param level - */ - public DefaultLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, final String name, final String level) { - super(builder, name, "Logger"); - if (level != null) { - addAttribute("level", level); - } - } - - /** - * Configure a logger. - * @param builder - * @param name - * @param level - * @param includeLocation + * Create a new logger component builder instance with the default plugin-type "{@code Logger}". + * @param builder the configuration builder + * @throws NullPointerException if the {@code builder} argument is {@code null} */ public DefaultLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, - final String name, - final String level, - final boolean includeLocation) { - super(builder, name, "Logger"); - if (level != null) { - addAttribute("level", level); - } - addAttribute("includeLocation", includeLocation); + final DefaultConfigurationBuilder builder, final @Nullable String name) { + this(builder, "Logger", name); } /** - * Configure a logger. - * @param builder - * @param name - * @param level - * @param type + * Create a new logger component builder instance with the given plugin-type. + * + * @param builder the configuration builder + * @param pluginType the target plugin-type of the logger component + * @throws NullPointerException if the {@code builder} argument is {@code null} */ public DefaultLoggerComponentBuilder( final DefaultConfigurationBuilder builder, - final String name, - final String level, - final String type) { - super(builder, name, type); - if (level != null) { - addAttribute("level", level); - } + final String pluginType, + final @Nullable String name) { + super(builder, pluginType, name); } /** - * Configure a logger. - * @param builder - * @param name - * @param level - * @param type - * @param includeLocation + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} is {@code null} */ - public DefaultLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, - final String name, - final String level, - final String type, - final boolean includeLocation) { - super(builder, name, type); - if (level != null) { - addAttribute("level", level); - } - addAttribute("includeLocation", includeLocation); - } - @Override public LoggerComponentBuilder add(final AppenderRefComponentBuilder builder) { return addComponent(builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the {@code builder} is {@code null} + */ @Override public LoggerComponentBuilder add(final FilterComponentBuilder builder) { return addComponent(builder); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultPropertyComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultPropertyComponentBuilder.java index aeba78fb187..cb18d87b991 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultPropertyComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultPropertyComponentBuilder.java @@ -17,16 +17,37 @@ package org.apache.logging.log4j.core.config.builder.impl; import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Property; import org.apache.logging.log4j.core.config.builder.api.PropertyComponentBuilder; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link PropertyComponentBuilder} interface for constructing + * a {@link Property} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.9 */ +@ProviderType class DefaultPropertyComponentBuilder extends DefaultComponentAndConfigurationBuilder implements PropertyComponentBuilder { + /** + * Constructs a new component builder instance. + * @param builder the configuration builder + * @param name the property name + * @param value the property value + * @throws NullPointerException if the given {@code builder} is {@code} + */ public DefaultPropertyComponentBuilder( - final DefaultConfigurationBuilder builder, final String name, final String value) { - super(builder, name, "Property", value); + final DefaultConfigurationBuilder builder, + final @Nullable String name, + final @Nullable String value) { + + super(builder, "Property", name, value); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultRootLoggerComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultRootLoggerComponentBuilder.java index c33f2e3fdfe..df4f7cb23c4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultRootLoggerComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultRootLoggerComponentBuilder.java @@ -16,84 +16,61 @@ */ package org.apache.logging.log4j.core.config.builder.impl; +import static org.apache.logging.log4j.core.config.LoggerConfig.RootLogger; + import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.AppenderRefComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.FilterComponentBuilder; import org.apache.logging.log4j.core.config.builder.api.RootLoggerComponentBuilder; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link RootLoggerComponentBuilder} interface for building + * a {@link RootLogger} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.4 */ +@ProviderType class DefaultRootLoggerComponentBuilder extends DefaultComponentAndConfigurationBuilder implements RootLoggerComponentBuilder { /** - * Configure the root logger. - * @param builder - * @param level - */ - public DefaultRootLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, final String level) { - super(builder, "", "Root"); - if (level != null) { - addAttribute("level", level); - } - } - - /** - * Configure the root logger. - * @param builder - * @param level - * @param includeLocation + * Create a new root logger component builder instance with the default plugin-type "{@code Root}". + * @param builder the configuration builder. */ - public DefaultRootLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, - final String level, - final boolean includeLocation) { - super(builder, "", "Root"); - if (level != null) { - addAttribute("level", level); - } - addAttribute("includeLocation", includeLocation); + public DefaultRootLoggerComponentBuilder(final DefaultConfigurationBuilder builder) { + this(builder, "Root"); } /** - * Configure the root logger. - * @param builder - * @param level - * @param type + * Create a new root logger component builder instance with the given plugin-type. + * @param builder the configuration builder + * @param pluginType the target plugin-type of the logger component */ public DefaultRootLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, final String level, final String type) { - super(builder, "", type); - if (level != null) { - addAttribute("level", level); - } + final DefaultConfigurationBuilder builder, final String pluginType) { + super(builder, pluginType, ""); } /** - * Configure the root logger. - * @param builder - * @param level - * @param type + * {@inheritDoc} + * + * @throws NullPointerException if the given {@code builder} is {@code null} */ - public DefaultRootLoggerComponentBuilder( - final DefaultConfigurationBuilder builder, - final String level, - final String type, - final boolean includeLocation) { - super(builder, "", type); - if (level != null) { - addAttribute("level", level); - } - addAttribute("includeLocation", includeLocation); - } - @Override public RootLoggerComponentBuilder add(final AppenderRefComponentBuilder builder) { return addComponent(builder); } + /** + * {@inheritDoc} + * + * @throws NullPointerException if the given {@code builder} is {@code null} + */ @Override public RootLoggerComponentBuilder add(final FilterComponentBuilder builder) { return addComponent(builder); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptComponentBuilder.java index ebcaa7920ba..38774ea3ee5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptComponentBuilder.java @@ -18,24 +18,32 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.ScriptComponentBuilder; +import org.apache.logging.log4j.core.script.Script; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** + * A default implementation of the {@link ScriptComponentBuilder} interface for building a {@link Script} component + * for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

+ * * @since 2.5 */ +@ProviderType class DefaultScriptComponentBuilder extends DefaultComponentAndConfigurationBuilder implements ScriptComponentBuilder { + /** + * Constructs a new component builder instance. + * @param builder the configuration builder + * @param name the script name + * @throws NullPointerException if the {@code builder} argument is {@code null} + */ public DefaultScriptComponentBuilder( - final DefaultConfigurationBuilder builder, - final String name, - final String language, - final String text) { - super(builder, name, "Script"); - if (language != null) { - addAttribute("language", language); - } - if (text != null) { - addAttribute("text", text); - } + final DefaultConfigurationBuilder builder, final @Nullable String name) { + super(builder, "Script", name); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptFileComponentBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptFileComponentBuilder.java index ced65e9cd99..68dc8848151 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptFileComponentBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/DefaultScriptFileComponentBuilder.java @@ -18,42 +18,32 @@ import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.builder.api.ScriptFileComponentBuilder; +import org.apache.logging.log4j.core.script.ScriptFile; +import org.jspecify.annotations.Nullable; +import org.osgi.annotation.versioning.ProviderType; /** - * Creates a ScriptFile ComponentBuilder. + * A default implementation of the {@link ScriptFileComponentBuilder} interface for building + * an {@link ScriptFile} component for a Log4j configuration. + * + *

+ * Note: This builder is not thread-safe. Instances should not be shared between threads. + *

* * @since 2.5 */ +@ProviderType class DefaultScriptFileComponentBuilder extends DefaultComponentAndConfigurationBuilder implements ScriptFileComponentBuilder { + /** + * Create a new filter component builder instance with the given plugin-type. + * @param builder the configuration builder. + * @param name the script component name + * @throws NullPointerException if either the {@code builder} or {@code pluginType} argument is {@code null} + */ public DefaultScriptFileComponentBuilder( - final DefaultConfigurationBuilder builder, final String name, final String path) { - super(builder, name != null ? name : path, "ScriptFile"); - addAttribute("path", path); - } - - @Override - public DefaultScriptFileComponentBuilder addLanguage(final String language) { - addAttribute("language", language); - return this; - } - - @Override - public DefaultScriptFileComponentBuilder addIsWatched(final boolean isWatched) { - addAttribute("isWatched", Boolean.toString(isWatched)); - return this; - } - - @Override - public DefaultScriptFileComponentBuilder addIsWatched(final String isWatched) { - addAttribute("isWatched", isWatched); - return this; - } - - @Override - public DefaultScriptFileComponentBuilder addCharset(final String charset) { - addAttribute("charset", charset); - return this; + final DefaultConfigurationBuilder builder, @Nullable final String name) { + super(builder, "ScriptFile", name); } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java index c56f92230f3..f31b31db42b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/builder/impl/package-info.java @@ -20,8 +20,10 @@ * @since 2.4 */ @Export -@Version("2.20.2") +@NullMarked +@Version("2.25.0") package org.apache.logging.log4j.core.config.builder.impl; +import org.jspecify.annotations.NullMarked; import org.osgi.annotation.bundle.Export; import org.osgi.annotation.versioning.Version; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java index 6dc9fd6957a..5e1f7c27a5d 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/properties/PropertiesConfigurationBuilder.java @@ -84,7 +84,7 @@ public PropertiesConfigurationBuilder setConfigurationSource(final Configuration public PropertiesConfiguration build() { for (final String key : rootProperties.stringPropertyNames()) { if (!key.contains(".")) { - builder.addRootProperty(key, rootProperties.getProperty(key)); + builder.setRootProperty(key, rootProperties.getProperty(key)); } } builder.setStatusLevel(Level.toLevel(rootProperties.getProperty(STATUS_KEY), Level.ERROR)) @@ -99,7 +99,7 @@ public PropertiesConfiguration build() { final Properties propertyPlaceholders = PropertiesUtil.extractSubset(rootProperties, "property"); for (final String key : propertyPlaceholders.stringPropertyNames()) { - builder.addProperty(key, propertyPlaceholders.getProperty(key)); + builder.add(builder.newProperty(key, propertyPlaceholders.getProperty(key))); } final Map scripts = @@ -118,7 +118,7 @@ public PropertiesConfiguration build() { } final Properties levelProps = PropertiesUtil.extractSubset(rootProperties, "customLevel"); - if (levelProps.size() > 0) { + if (!levelProps.isEmpty()) { for (final String key : levelProps.stringPropertyNames()) { builder.add(builder.newCustomLevel(key, Integers.parseInt(levelProps.getProperty(key)))); } @@ -183,13 +183,11 @@ public PropertiesConfiguration build() { props.setProperty("", rootProp); rootProperties.remove("rootLogger"); } - if (props.size() > 0) { + if (!props.isEmpty()) { builder.add(createRootLogger(props)); } - builder.setLoggerContext(loggerContext); - - return builder.build(false); + return builder.setLoggerContext(loggerContext).build(false); } private ScriptComponentBuilder createScript(final Properties properties) { @@ -219,7 +217,7 @@ private AppenderComponentBuilder createAppender(final String key, final Properti final AppenderComponentBuilder appenderBuilder = builder.newAppender(name, type); addFiltersToComponent(appenderBuilder, properties); final Properties layoutProps = PropertiesUtil.extractSubset(properties, "layout"); - if (layoutProps.size() > 0) { + if (!layoutProps.isEmpty()) { appenderBuilder.add(createLayout(name, layoutProps)); } @@ -245,7 +243,7 @@ private AppenderRefComponentBuilder createAppenderRef(final String key, final Pr final AppenderRefComponentBuilder appenderRefBuilder = builder.newAppenderRef(ref); final String level = Strings.trimToNull((String) properties.remove("level")); if (!Strings.isEmpty(level)) { - appenderRefBuilder.addAttribute("level", level); + appenderRefBuilder.setLevelAttribute(level); } return addFiltersToComponent(appenderRefBuilder, properties); } @@ -282,10 +280,10 @@ private LoggerComponentBuilder createLogger(final String key, final Properties p addFiltersToComponent(loggerBuilder, properties); final String additivity = (String) properties.remove("additivity"); if (!Strings.isEmpty(additivity)) { - loggerBuilder.addAttribute("additivity", additivity); + loggerBuilder.setAdditivityAttribute(additivity); } if (levelAndRefs != null) { - loggerBuilder.addAttribute("levelAndRefs", levelAndRefs); + loggerBuilder.setAttribute("levelAndRefs", levelAndRefs); } return loggerBuilder; } @@ -316,7 +314,7 @@ private RootLoggerComponentBuilder createRootLogger(final Properties properties) } addLoggersToComponent(loggerBuilder, properties); if (levelAndRefs != null) { - loggerBuilder.addAttribute("levelAndRefs", levelAndRefs); + loggerBuilder.setAttribute("levelAndRefs", levelAndRefs); } return addFiltersToComponent(loggerBuilder, properties); } @@ -334,7 +332,7 @@ private static > ComponentBuilder createCompone final ComponentBuilder parent, final String key, final Properties properties) { final String name = (String) properties.remove(CONFIG_NAME); final String type = (String) properties.remove(CONFIG_TYPE); - if (Strings.isEmpty(type)) { + if (type == null || Strings.isEmpty(type)) { throw new ConfigurationException("No type attribute provided for component " + key); } final ComponentBuilder componentBuilder = parent.getBuilder().newComponent(name, type); @@ -343,7 +341,7 @@ private static > ComponentBuilder createCompone private static > B processRemainingProperties( final B builder, final Properties properties) { - while (properties.size() > 0) { + while (!properties.isEmpty()) { final String propertyName = properties.stringPropertyNames().iterator().next(); final int index = propertyName.indexOf('.'); @@ -352,7 +350,7 @@ private static > B processRemainingProperties( final Properties componentProperties = PropertiesUtil.extractSubset(properties, prefix); builder.addComponent(createComponent(builder, prefix, componentProperties)); } else { - builder.addAttribute(propertyName, properties.getProperty(propertyName)); + builder.setAttribute(propertyName, properties.getProperty(propertyName)); properties.remove(propertyName); } } diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutConcurrentEncodeTest.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutConcurrentEncodeTest.java index 6ef74dc9119..685a08dbc4f 100644 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutConcurrentEncodeTest.java +++ b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutConcurrentEncodeTest.java @@ -113,15 +113,15 @@ private static void withContextFromTemplate( final Configuration config = configBuilder .add(configBuilder .newAppender(appenderName, "File") - .addAttribute( + .setAttribute( "fileName", appenderFilepath.toAbsolutePath().toString()) - .addAttribute("append", false) - .addAttribute("immediateFlush", false) - .addAttribute("ignoreExceptions", false) + .setAttribute("append", false) + .setAttribute("immediateFlush", false) + .setAttribute("ignoreExceptions", false) .add(configBuilder .newLayout("JsonTemplateLayout") - .addAttribute("eventTemplate", eventTemplateJson) - .addAttribute("recyclerFactory", recyclerFactory))) + .setAttribute("eventTemplate", eventTemplateJson) + .setAttribute("recyclerFactory", recyclerFactory))) .add(configBuilder.newRootLogger(Level.ALL).add(configBuilder.newAppenderRef(appenderName))) .build(false); diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java index e920933ae2e..a2f03e4c411 100644 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java +++ b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java @@ -57,6 +57,7 @@ import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.SocketAppender; import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginFactory; @@ -291,9 +292,9 @@ void test_property_injection() { // Create the layout with property. final String propertyValue = "propertyValue"; - final Configuration config = ConfigurationBuilderFactory.newConfigurationBuilder() - .addProperty(propertyName, propertyValue) - .build(); + final ConfigurationBuilder configurationBuilder = ConfigurationBuilderFactory.newConfigurationBuilder(); + configurationBuilder.add(configurationBuilder.newProperty(propertyName, propertyValue)); + final Configuration config = configurationBuilder.build(); final JsonTemplateLayout layout = JsonTemplateLayout.newBuilder() .setConfiguration(config) .setEventTemplate(eventTemplate) diff --git a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java index f4bdd62ec84..eab1d3d1e5d 100644 --- a/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java +++ b/log4j-layout-template-json-test/src/test/java/org/apache/logging/log4j/layout/template/json/TestHelpers.java @@ -29,7 +29,6 @@ import org.apache.logging.log4j.core.config.DefaultConfiguration; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder; import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory; -import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration; import org.apache.logging.log4j.core.test.appender.ListAppender; import org.apache.logging.log4j.layout.template.json.util.JsonReader; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; @@ -114,10 +113,9 @@ public static void withContextFromTemplate( final BiConsumer consumer) { // Create the configuration builder. - final ConfigurationBuilder configBuilder = - ConfigurationBuilderFactory.newConfigurationBuilder() - .setStatusLevel(Level.ERROR) - .setConfigurationName(configName); + final ConfigurationBuilder configBuilder = ConfigurationBuilderFactory.newConfigurationBuilder() + .setStatusLevel(Level.ERROR) + .setConfigurationName(configName); // Create the configuration. final String eventTemplateJson = writeJson(eventTemplate); @@ -125,10 +123,10 @@ public static void withContextFromTemplate( final Configuration config = configBuilder .add(configBuilder .newAppender(appenderName, "List") - .addAttribute("raw", true) + .setAttribute("raw", true) .add(configBuilder .newLayout("JsonTemplateLayout") - .addAttribute("eventTemplate", eventTemplateJson))) + .setAttribute("eventTemplate", eventTemplateJson))) .add(configBuilder.newRootLogger(Level.ALL).add(configBuilder.newAppenderRef(appenderName))) .build(false); diff --git a/src/changelog/.2.x.x/2791_rework_ComponentBuilder_API.xml b/src/changelog/.2.x.x/2791_rework_ComponentBuilder_API.xml new file mode 100644 index 00000000000..592ef0e95c8 --- /dev/null +++ b/src/changelog/.2.x.x/2791_rework_ComponentBuilder_API.xml @@ -0,0 +1,12 @@ + + + + + Fix problem when null attribute values are set on DefaultComponentBuilder. GitHub issue #2791. + Added JVerify annotations to config.builder.* packages. + Updated Builder APIs and Default***Builder implementations. + +