diff --git a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/RequiredClass.java b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/RequiredClass.java index c2a329c8c66..5f6ca8bbccc 100644 --- a/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/RequiredClass.java +++ b/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/validation/constraints/RequiredClass.java @@ -25,7 +25,7 @@ import org.apache.logging.log4j.plugins.validation.validators.RequiredClassValidator; /** - * Marks a plugin builder field or plugin factory parameter as required. + * Requires that a given class be available. * * @since 3.0.0 */ diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json new file mode 100644 index 00000000000..5ad06b4cd60 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.json @@ -0,0 +1,18 @@ +{ + "Configuration": { + "Appenders": { + "Console": { + "name": "CONSOLE", + "JsonTemplateLayout": {} + } + }, + "Loggers": { + "Root": { + "level": "WARN", + "AppenderRef": { + "ref": "CONSOLE" + } + } + } + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties new file mode 100644 index 00000000000..bd45ad2d7b2 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.properties @@ -0,0 +1,22 @@ +# +# 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. +# +appender.0.type = Console +appender.0.name = CONSOLE +appender.0.layout.type = JsonTemplateLayout + +rootLogger.level = WARN +rootLogger.appenderRef.0.ref = CONSOLE diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml new file mode 100644 index 00000000000..3acc82eb214 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml new file mode 100644 index 00000000000..a46826acd48 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ConfigurationBuilder/log4j2.yaml @@ -0,0 +1,28 @@ +# +# 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. +# +Configuration: + + Appenders: + Console: + name: "CONSOLE" + JsonTemplateLayout: {} + + Loggers: + Root: + level: "WARN" + AppenderRef: + ref: "CONSOLE" diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java new file mode 100644 index 00000000000..5d8a00b4241 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfiguration.java @@ -0,0 +1,47 @@ +/* + * 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 com.example; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.AppenderRef; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.LoggerConfig; + +public class ExampleConfiguration extends XmlConfiguration { + + public ExampleConfiguration(LoggerContext loggerContext, ConfigurationSource configSource) { + super(loggerContext, configSource); + } + + @Override + protected void doConfigure() { + Appender appender = createAppender("ExampleAppender"); + appender.start(); + addAppender(appender); + LoggerConfig loggerConfig = LoggerConfig.newBuilder() + .withConfig(this) + .withAdditivity(false) + .withLevel(Level.INFO) + .withLoggerName("com.example") + .withRefs(new AppenderRef[] {AppenderRef.createAppenderRef("ExampleAppender", null, null)}) + .build(); + loggerConfig.addAppender(appender, null, null); + addLogger("com.example", loggerConfig); + } +} diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java new file mode 100644 index 00000000000..25a625c1b15 --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/ExampleConfigurationFactory.java @@ -0,0 +1,46 @@ +/* + * 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 com.example; + +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.ConfigurationSource; +import org.apache.logging.log4j.core.config.Order; +import org.apache.logging.log4j.core.config.plugins.Plugin; + +// tag::class[] +@Order(100) +@Plugin(name = "ExampleConfigurationFactory", category = ConfigurationFactory.CATEGORY) +public class ExampleConfigurationFactory extends ConfigurationFactory { + + @Override + public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) { // <1> + // Return a `Configuration`... + } + + @Override + public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) { + // Return a `Configuration`... + } + + @Override + public String[] getSupportedTypes() { + return new String[] {"*"}; + } +} +// end::class[] diff --git a/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java new file mode 100644 index 00000000000..bdd5dd6772c --- /dev/null +++ b/src/site/antora/modules/ROOT/examples/manual/customconfig/Usage.java @@ -0,0 +1,90 @@ +/* + * 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 com.example; + +import java.net.URI; +import java.util.Arrays; +import org.apache.logging.log4j.Level; +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.ConfigurationFactory; +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.config.composite.CompositeConfiguration; + +final class Usage { + + private static Configuration createConfiguration() { + // tag::createConfiguration[] + ConfigurationBuilder configBuilder = + ConfigurationBuilderFactory.newConfigurationBuilder(); // <1> + Configuration configuration = configBuilder + .add( + configBuilder // <2> + .newAppender("CONSOLE", "List") + .add(configBuilder.newLayout("JsonTemplateLayout"))) + .add( + configBuilder // <3> + .newRootLogger(Level.WARN) + .add(configBuilder.newAppenderRef("CONSOLE"))) + .build(false); // <4> + // end::createConfiguration[] + } + + private static void useConfiguration() { + // tag::useConfiguration[] + Configuration configuration = createConfiguration(); + try (LoggerContext loggerContext = Configurator.initialize(configuration)) { + // Use `LoggerContext`... + } + // end::useConfiguration[] + } + + private static void reconfigureActiveLoggerContext() { + // tag::reconfigureActiveLoggerContext[] + Configuration configuration = createConfiguration(); + Configurator.reconfigure(configuration); + // end::reconfigureActiveLoggerContext[] + } + + private static Configuration loadConfigurationFile() { + // tag::loadConfigurationFile[] + ConfigurationFactory.getInstance() + .getConfiguration( + null, // <1> + null, // <2> + URI.create("uri://to/my/log4j2.xml")); // <3> + // end::loadConfigurationFile[] + } + + private static Configuration combineConfigurations() { + // tag::combineConfigurations[] + ConfigurationFactory configFactory = ConfigurationFactory.getInstance(); + AbstractConfiguration commonConfig = (AbstractConfiguration) // <2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-common.xml")); // <1> + AbstractConfiguration appConfig = (AbstractConfiguration) // <2> + configFactory.getConfiguration(null, null, URI.create("classpath:log4j2-app.xml")); // <1> + AbstractConfiguration runtimeConfig = ConfigurationBuilderFactory.newConfigurationBuilder() + // ... + .build(false); // <3> + return new CompositeConfiguration(Arrays.asList(commonConfig, appConfig, runtimeConfig)); // <4> + // end::combineConfigurations[] + } +} diff --git a/src/site/antora/modules/ROOT/images/Log4jClasses.jpg b/src/site/antora/modules/ROOT/images/Log4jClasses.jpg deleted file mode 100755 index ea16084bd68..00000000000 Binary files a/src/site/antora/modules/ROOT/images/Log4jClasses.jpg and /dev/null differ diff --git a/src/site/antora/modules/ROOT/pages/articles.adoc b/src/site/antora/modules/ROOT/pages/articles.adoc deleted file mode 100644 index bf1d3255f23..00000000000 --- a/src/site/antora/modules/ROOT/pages/articles.adoc +++ /dev/null @@ -1,145 +0,0 @@ -//// -Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -//// -= Articles and Tutorials - -A collection of external articles and tutorials about Log4j 2. -The xref:manual/index.adoc[Log4j 2 manual] is the ultimate guide for up-to-date and detailed information on how to configure and use Log4j 2. - -== Chinese - -* https://www.infoq.cn/news/2016/06/log4j-garbage-free/[Log4j 2.6免垃圾收集] (June 12, 2016) -* https://blog.csdn.net/autfish/article/details/51203709[详解log4j2(上) - 从基础到实战] (April 20, 2016) -* https://www.jianshu.com/p/7aec512a003c[Log4j2使用笔记] (November 3, 2015) -* https://www.infoq.cn/news/2015/09/interview-log4j-pmc/[过渡到全新Log4j:Log4j项目管理委员会访谈] (September 14, 2015) -* https://www.infoq.cn/news/2015/09/log4j-version-1-reaches-eol/[Log4j版本1生命周期终结] (September 3, 2015) -* https://www.infoq.cn/news/2014/08/apache-log4j2/[Apache Log4j 2.0值得升级吗] (August 5, 2014) -* https://blog.csdn.net/lrenjun/article/details/8178875[log4j2 使用详解] (November 13, 2012) -* https://web.archive.org/web/20211016221310/https://my.oschina.net/xianggao/blog/523401[Log4j2配置文件详解] (October 29, 2015) - -== English - -* https://medium.com/codex/log4j2-create-custom-log-levels-and-how-to-use-them-48685e133fd1[Log4J2 | Create custom log levels and how to use them] (April 29, 2021) -* http://makeseleniumeasy.com/2021/04/26/log4j2-tutorial-7-working-mechanism-of-default-rollover-strategy/[Log4j2 Tutorial 7 -- Working Mechanism Of Default Rollover Strategy] (April 26, 2021) -* http://makeseleniumeasy.com/2021/04/20/log4j2-tutorial-6-introduction-to-rollingfileappender-and-its-triggers-and-strategies/[Log4j2 Tutorial 6 -- Introduction To RollingFileAppender And Its Triggers And Strategies] (April 20, 2021) -* http://makeseleniumeasy.com/2021/04/13/log4j2-tutorial-5-xml-configuration-file-to-log-into-file-and-console-using-log4j2-together/[Log4j2 Tutorial 5 -- XML Configuration File To Log Into File And Console Using Log4j2 Together] (April, 13, 2021) -* http://makeseleniumeasy.com/2021/04/09/log4j2-tutorial-4-print-logs-in-external-file-using-xml-configuration-file-of-log4j2/[Log4j2 Tutorial 4 -- Print Logs In External File Using XML Configuration File Of Log4J2] (April 9, 2021) -* http://makeseleniumeasy.com/2021/03/24/log4j2-tutorial-3-setup-console-appender-using-xml-configuration-file/[Log4j2 Tutorial 3 -- Setup Console Appender Using XML Configuration File] (March 24, 2021) -* http://makeseleniumeasy.com/2021/03/17/log4j2-tutorial-2-creating-log4j2-maven-project-with-default-configuration-file/[Log4j2 Tutorial 2 -- Creating Log4j2 Maven Project With Default Configuration File] (March 17, 2021) -* http://makeseleniumeasy.com/2021/03/11/log4j2-tutorial-1-introduction-to-apache-log4j2/[Log4j2 Tutorial 1 -- Introduction To Apache Log4j2] (March 11, 2021) -* https://petrepopescu.tech/2021/03/how-to-make-a-custom-message-converter-for-log4j2/[How to make a custom message converter for Log4J2] (March 4, 2021) -* http://www.mastertheboss.com/jbossas/jboss-log/how-to-use-log4j2-in-your-wildfly-applications/[How to configure Log4j2 in your WildFly applications] (August 16, 2020) -* http://www.masterspringboot.com/configuration/logging/configuring-log4j2-in-spring-boot-applications[Configuring Log4j2 in a Spring Boot 2 application] (August 16, 2020) -* https://www.ralphgoers.com/post/getting-the-most-out-of-the-log4j-2-api[Getting the most out of the Log4j 2 API] (January 1, 2020) -* https://www.baeldung.com/log4j2-programmatic-config[Programmatic Configuration with Log4j 2] (December 31, 2019) -* https://www.ralphgoers.com/post/log4j-1-compatibility-in-log4j-2[Log4j 1 Compatibility in Log4j 2] (December 22, 2019) -* https://www.ralphgoers.com/post/why-was-log4j-2-created[Why was Log4j 2 created] (December 14, 2019) -* https://www.marcobehler.com/guides/java-logging[A Guide to Logging in Java] (June 23, 2019) -* https://www.alibabacloud.com/blog/exploring-the-secrets-of-java-logs-log4j-2-log-system_594821[Exploring the Secrets of Java Logs: Log4j 2 Log System] (May 17, 2019) -* https://mkyong.com/logging/apache-log4j-2-tutorials/[Apache Log4j 2 Tutorials] (March 27, 2019) -* https://stackify.com/compare-java-logging-frameworks/[Java Logging Frameworks: log4j vs logback vs log4j2] (October 30, 2018) -* https://howtodoinjava.com/log4j2/[Log4j2 Tutorial] (June 3, 2018) -* https://crunchify.com/java-how-to-create-your-own-logging-level-in-log4j-configuring-log4j/[In Java How to Create your own Logging Level using Log4j (Configuring Log4j 2)] {May 30, 2018) -* https://www.baeldung.com/log4j2-programmatic-config[Programmatic Configuration with Log4j 2] (May 22, 2018) -* https://www.javacodegeeks.com/2018/03/tales-from-the-field-migrating-from-log4j-to-log4j2.html[Tales from the Field: Migrating from Log4J to Log4J2] (March 12, 2018) -* https://examples.javacodegeeks.com/enterprise-java/log4j/log4j-2-best-practices-example/[Log4j 2 Best Practices example] (November 14, 2017) -* https://web.archive.org/web/20180308003803/http://musigma.org/logging/2017/11/06/logging.html[Logging Fundamentals] (November 6, 2017) -* http://www.rationaljava.com/2017/10/allocation-free-logging-with-log4j2.html[Allocation free logging with Log4j2] (October 27, 2017) -* https://www.loggly.com/blog/benchmarking-java-logging-frameworks/[Benchmarking Java logging frameworks] (October 25, 2017) -* https://www.baeldung.com/log4j-2-lazy-logging[Log4j 2 and Lambda Expressions] (August 22, 2017) -* https://www.callicoder.com/spring-boot-log4j-2-example/[How to use Log4j 2 with Spring Boot] (August 11, 2017) -* https://www.boraji.com/log4j-2-rollingfileappender-example[Log4j 2 - RollingFileAppender example] (July 26, 2017) -* https://stackify.com/log4j2-java/[How Log4J2 Works: 10 Ways to Get the Most Out Of It] (June 14, 2017) -* https://www.baeldung.com/log4j2-appenders-layouts-filters[Intro to Log4j2 -- Appenders, Layouts and Filters] (February 28, 2017) -* https://dzone.com/articles/getting-own-log4j2-file-for-mule-via-spring[Getting Your Own Log4j2 File for Mule via Spring] (December 29, 2016) -* https://garygregory.wordpress.com/2016/11/27/loading-a-log4j-configuration-for-a-specific-ejb/[Loading a Log4j Configuration for a specific EJB] (November 27, 2016) -* https://anishekagarwal.medium.com/log4j2-logging-a-primer-f10ed18e9de6[Log4j2 Logging: A Primer] (October 15, 2016) -* https://www.digitalocean.com/community/tutorials/log4j2-example-tutorial-configuration-levels-appenders[Log4j2 Example Tutorial -- Configuration, Levels, Appenders] (July 6, 2016) -* https://howtodoinjava.com/log4j2/log4j2-htmllayout-configuration-example/[Log4j2 HTMLLayout Configuration Example] (June 27, 2016) -* https://javaevangelist.blogspot.com/2016/06/log4j2-javautillogging-jul-adapter.html[Log4j2 java.util.logging (JUL) Adapter Example] (June 24, 2016) -* https://dzone.com/articles/log4j-2-configuration-using-properties-file[Log4J 2 Configuration: Using the Properties File] (May 18, 2016) -* https://springframework.guru/using-log4j-2-spring-boot/[Using Log4j 2 with Sprint Boot] (April 7, 2016) -* https://www.infoq.com/news/2016/05/log4j-garbage-free/[Log4j 2.6 Goes Garbage-Free] (May 30, 2016) -* https://web.archive.org/web/20180306104946/http://musigma.org/java/log4j/2016/05/29/log4j-2.6.html[What's New in Log4j 2.6] (May 29, 2016) -* https://springframework.guru/asynchronous-logging-with-log4j-2/[Asynchronous Logging With Log4j 2] (March 31, 2016) -* https://springframework.guru/log4j-2-configuration-using-yaml/[Log4J 2 Configuration: Using YAML] (March 26, 2016) -* https://springframework.guru/log4j-2-configuration-using-json/[Log4J 2 Configuration: Using JSON] (March 23, 2016) -* https://web.archive.org/web/20220315041422/https://qbox.io/blog/getting-logstash-2x-ready-for-log4j2[Getting Logstash 2.x Ready for Log4j2] (March 10, 2016) -* https://springframework.guru/log4j-2-configuration-using-xml/[Log4J 2 Configuration: Using XML] (March 10, 2016) -* https://springframework.guru/log4j-2-configuration-using-properties-file/[Log4J 2 Configuration: Using Properties File] (March 8, 2016) -* https://springframework.guru/introducing-log4j-enterprise-class-logging/[Introducing Log4j 2 -- Enterprise Class Logging] (February 8, 2016) -* https://www.javacodegeeks.com/2015/10/better-performing-non-logging-logger-calls-in-log4j2.html[Better Performing Non-Logging Logger Calls in Log4j2] (October 20, 2015) -* https://marxsoftware.blogspot.com/2015/10/log4j2-non-logging-performance.html[Better Performing Non-Logging Logger Calls in Log4j2] (October 15, 2015) -* https://www.javacodegeeks.com/2015/10/easy-and-consistent-log4j2-logger-naming.html[Easy and Consistent Log4j2 Logger Naming] (October 10, 2015) -* https://garygregory.wordpress.com/2015/09/16/a-gentle-introduction-to-the-log4j-api-and-lambda-basics/[Writing clean logging code using Java 8 lambdas] (September 16, 2015) -* https://garygregory.wordpress.com/2015/09/10/the-art-of-test-driven-development-understanding-logging/[The Art of Test Driven Development: Understanding Logging] (September 10, 2015) -* https://garygregory.wordpress.com/2015/09/08/the-art-of-test-driven-development-per-test-logging/[The Art of Test Driven Development: Per-Test Logging] (September 8, 2015) -* https://www.infoq.com/news/2015/09/interview-log4j-pmc/[The Transition to a New Log4j: a Q&A with Log4j's Project Management Committee] (September 8, 2015) -* https://www.infoq.com/news/2015/08/log4j-version-1-reaches-eol/[Log4j Version 1 Reaches End of Life] (August 26, 2015) -* https://news.apache.org/foundation/entry/apache_logging_services_project_announces[Apache Logging Services Project Announces Log4j 1 End-Of-Life; -Recommends Upgrade to Log4j 2] (August 6, 2015) -* https://www.innoq.com/en/blog/per-request-debugging-with-log4j2/[Per request debugging with Log4j 2 filters] (May 8, 2015) -* https://blog.oio.de/2015/04/27/log4j-2-configuration-depending-environment/[Log4j 2 configuration depending on environment] (April 27, 2015) -* http://www.journaldev.com/7128/apache-log4j-2-tutorial-configuration-levels-appenders-lookup-layouts-and-filters-example[Apache Log4j 2 Tutorial -- Configuration, Levels, Appenders, Lookup, Layouts and Filters Example] (March 16, 2015) -* https://blogs.mulesoft.com/dev-guides/how-to-tutorials/mule-3-6-asynchronous-logging/[Disrupting your Asynchronous Loggers] (March 5, 2015) -* https://andrew-flower.com/blog/Create_Custom_Log4j_Plugins[Extending Log4j2 - Creating Custom Log4j2 Plugins] (February 20, 2015) -* https://andrew-flower.com/blog/Basic_Log4j2_Configuration[Log4j2 - a crash course...] (February 10, 2015) -* https://memorynotfound.com/log4j2-with-log4j2-xml-configuration-example/[Log4j2 with log4j2.xml Configuration Example] (February 10, 2015) -* https://www.rapid7.com/blog/post/2015/02/05/logging-from-your-java-application-using-log4j2/[Logging From Your Java Application Using Log4j2] (February 5, 2015) -* https://blogs.mulesoft.com/dev-guides/how-to-tutorials/mule-3-6-asynchronous-logging/[Asynchronous Logging in Mule 3.6] (January 20, 2015) -* https://www.infoq.com/news/2014/07/apache-log4j2/[Apache Log4j 2.0 - Worth the Upgrade?] (July 31, 2014) -* https://mycuteblog.com/log4j2-xml-configuration-example/[log4j2 xml configuration example] (July 26, 2014) -* https://tech.finn.no/2014/07/01/log4j2-in-production-making-it-fly/[Log4j 2 in Production -- Making it Fly] (July 2, 2014) -* https://grobmeier.solutions/log4j-2-performance-close-to-insane-20072013.html[Log4j 2: Performance Close to Insane] (July 20, 2013) -* https://news.ycombinator.com/item?id=5612035[Hacker News: Asynchronous Loggers for Low-Latency Logging] (April 26, 2013) -* https://grobmeier.solutions/the-new-log4j-2-0-05122012.html[The New Log4j 2.0] (December 5, 2012) - -== English (Videos) - -* https://www.youtube.com/watch?v=sdOiA1Xql0o[Log4J2 and Java configuration with properties file] (February 18, 2018) -* https://www.youtube.com/watch?v=BbcSNOtEGWs[Apache Log4j 2 Configuration| Log4j2 with JDK 9.0] (January 8, 2018) -* https://www.youtube.com/watch?v=KKO5wGi_vEc[Spring Boot - log4j 2 configuration example] (December 30,2017) -* https://www.youtube.com/watch?v=-XNvCNHjIKw[Understanding and working with Log4j2 for logging in Selenium framework (Part B)] (December 20, 2016) -* https://www.youtube.com/watch?v=RWZ0gsfkkc4[Understanding and working with Log4j2 for logging in Selenium framework (Part A)] (December 18, 2016) -* https://vimeo.com/169542136[Matt Sicker - Asynchronous Logging in Log4j 2.6 (CJUG Lightning Talk)] (June 2, 2016) - ** Errata: "ThresholdFilter" should be "BurstFilter" -* https://www.youtube.com/watch?v=Yv0n-4AsOiI[Nancy M Schorr - Log4j2 with Java and Maven for Logging] (October 14, 2015) -* https://www.youtube.com/watch?v=EWftNoRhS_M[Ramesh Rajaram - Log4j Key Features] (April 10, 2015) -* https://www.youtube.com/watch?v=ZzVSs_JEhgs[Matt Sicker - Introducing Log4j 2.0] (May 6, 2014) -* https://www.youtube.com/watch?v=HB0r5DuxGPI[Nicholas Williams - Log4j 2 in Web Applications: A Deeper Look at Effective Java EE Logging] (May 6, 2014) - -== German - -* https://entwickler.de/java/apache-log4j-26-lauft-nun-auch-ohne-mull/[Apache Log4j 2.6 läuft nun auch ohne Müll] (May 31, 2016) -* https://www.innoq.com/en/articles/2015/01/logging-konsolidieren-log4j2/[Logging konsolidieren und Performance gewinnen] (January 23, 2015) - -== Japanese - -* https://tm-b.hatenablog.com/entry/2016/08/18/200715[中年プログラマーの息抜き] (August 18, 2016) -* https://minor.hatenablog.com/entry/2016/05/22/193556[【log4j2】ThreadContextを利用してすべてのログに追加情報を出力する] (May 22, 2016) -* https://qiita.com/kazurof/items/abbd42f11bfc125f3190[Log4j 2でログ出力をテストするサンプルソース] (February 22, 2016) -* https://www.infoq.com/jp/news/2015/09/interview-log4j-pmc/[新Log4jへの移行: Log4jプロジェクト管理グループとのQ&A] (September 27, 2015) -* https://www.infoq.com/jp/news/2015/09/log4j-version-1-reaches-eol/[Log4jバージョン1のサポートが終了] (September 23, 2015) -* https://qiita.com/pica/items/f801c74848f748f76b58[log4j2の設定ファイル(XML)] (July 27, 2015) -* https://yamashiro0110.hatenadiary.jp/entry/2014/08/24/093336[Log4j2の使い方めも] (August 24, 2014) -* https://www.infoq.com/jp/news/2014/08/apache-log4j2/[Apache Log4j 2.0 - アップグレードする価値はあるか?] (August 17, 2014) -* https://kazuhira-r.hatenablog.com/entry/20140628/1403959552[Log4j2を試してみる] (June 28, 2014) -* https://nabedge.blogspot.com/2013/10/log4j2.html[log4j2にログを集める] (October 26, 2013) - -== Korean - -* https://dveamer.github.io/java/Log4j2.html[Log4j 2 설정하기] (January 24, 2016) -* https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte3:fdl:%EC%84%A4%EC%A0%95_%ED%8C%8C%EC%9D%BC%EC%9D%84_%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94_%EB%B0%A9%EB%B2%95[Log4j 2 환경설정 [설정 파일 사용 시\]] (May 14, 2014) diff --git a/src/site/antora/modules/ROOT/pages/faq.adoc b/src/site/antora/modules/ROOT/pages/faq.adoc index c50d10b2344..ec4d98576d2 100644 --- a/src/site/antora/modules/ROOT/pages/faq.adoc +++ b/src/site/antora/modules/ROOT/pages/faq.adoc @@ -153,7 +153,7 @@ There are several advantages to using Log4j API: * SLF4J forces your application to log ``String``s. Log4j API supports logging any `CharSequence` if you want to log text, but also supports logging any `Object` as is. It is the responsibility of the logging _implementation_ to handle this object, and we consider it a design mistake to limit applications to logging ``String``s. -* Log4j API offers support for logging {log4j2-url}/manual/messages.adoc[`Message` objects]. +* Log4j API offers support for logging {log4j2-url}/manual/messages.html[`Message` objects]. Messages allow support for interesting and complex constructs to be passed through the logging system and be efficiently manipulated. Users are free to create their own message types and write custom layouts, filters and lookups to manipulate them. * Log4j API supports {log4j2-url}/manual/api.adoc#fluent-api[lambda expressions] both in its plain and fluent API. diff --git a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc index c98172be67d..2b7d9374040 100644 --- a/src/site/antora/modules/ROOT/pages/manual/architecture.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/architecture.adoc @@ -16,468 +16,848 @@ //// = Architecture -== Main Components - -Log4j uses the classes shown in the diagram below. - -image:Log4jClasses.jpg[Log4j 2 Class Relationships,title="Log4j 2 Class Relationships"] - -Applications using the Log4j 2 API will request a Logger with a specific -name from the LogManager. The LogManager will locate the appropriate -LoggerContext and then obtain the Logger from it. If the Logger must be -created it will be associated with the LoggerConfig that contains either -a) the same name as the Logger, b) the name of a parent package, or c) -the root LoggerConfig. LoggerConfig objects are created from Logger -declarations in the configuration. The LoggerConfig is associated with -the Appenders that deliver the LogEvents. - -[id=logger-hierarchy] -=== Logger Hierarchy - -The first and foremost advantage of any logging API over plain -`System.out.println()` resides in its ability to disable certain log -statements while allowing others to print unhindered. This capability -assumes that the logging space, that is, the space of all possible -logging statements, is categorized according to some developer-chosen -criteria. - -In Log4j 1.x the Logger Hierarchy was maintained through a relationship -between Loggers. In Log4j 2 this relationship no longer exists. Instead, -the hierarchy is maintained in the relationship between LoggerConfig -objects. - -Loggers and LoggerConfigs are named entities. Logger names are -case-sensitive and they follow the hierarchical naming rule: - -Named Hierarchy:: -A LoggerConfig is said to be an _ancestor_ of another LoggerConfig if -its name followed by a dot is a prefix of the _descendant_ logger -name. A LoggerConfig is said to be a _parent_ of a _child_ -LoggerConfig if there are no ancestors between itself and the -descendant LoggerConfig. - -For example, the LoggerConfig named `"com.foo"` is a parent of the -LoggerConfig named `"com.foo.Bar"`. Similarly, `"java"` is a parent of -`"java.util"` and an ancestor of `"java.util.Vector"`. This naming -scheme should be familiar to most developers. - -The root LoggerConfig resides at the top of the LoggerConfig hierarchy. -It is exceptional in that it always exists and it is part of every -hierarchy. A Logger that is directly linked to the root LoggerConfig can -be obtained as follows: - -[source,java] ----- -Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME); ----- - -Alternatively, and more simply: - -[source,java] ----- -Logger logger = LogManager.getRootLogger(); ----- - -All other Loggers can be retrieved using the -{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.String)[`LogManager.getLogger`] -static method by passing the name of the desired Logger. Further -information on the Logging API can be found in the -xref:manual/api.adoc[Log4j API]. - -=== LoggerContext - -The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/LoggerContext.html[`LoggerContext`] -acts as the anchor point for the Logging system. However, it is possible -to have multiple active LoggerContexts in an application depending on -the circumstances. More details on the LoggerContext are in the -xref:manual/logsep.adoc[Log Separation] section. - -=== Configuration - -Every LoggerContext has an active -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]. -The Configuration contains all the Appenders, context-wide Filters, -LoggerConfigs and contains the reference to the StrSubstitutor. -During reconfiguration, two Configuration objects will exist. Once all Loggers -have been redirected to the new Configuration, the old Configuration -will be stopped and discarded. - -=== Logger - -As stated previously, Loggers are created by calling -{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/LogManager.html#getLogger(java.lang.String)[`LogManager.getLogger`]. -The Logger itself performs no direct actions. It simply has a name and -is associated with a LoggerConfig. It extends -{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/spi/AbstractLogger.html[`AbstractLogger`] -and implements the required methods. As the configuration is modified -Loggers may become associated with a different LoggerConfig, thus -causing their behavior to be modified. - -Retrieving Loggers - -Calling the `LogManager.getLogger` method with the same name will always -return a reference to the same Logger object. - -For example, in - -[source,java] ----- -Logger x = LogManager.getLogger("wombat"); -Logger y = LogManager.getLogger("wombat"); ----- - -`x` and `y` refer to _exactly_ the same Logger object. - -Configuration of the log4j environment is typically done at application -initialization. The preferred way is by reading a configuration file. -This is discussed in xref:manual/configuration.adoc[Configuration]. - -Log4j makes it easy to name Loggers by _software component_. This can be -accomplished by instantiating a Logger in each class, with the logger -name equal to the fully qualified name of the class. This is a useful -and straightforward method of defining loggers. As the log output bears -the name of the generating Logger, this naming strategy makes it easy to -identify the origin of a log message. However, this is only one -possible, albeit common, strategy for naming loggers. Log4j does not -restrict the possible set of loggers. The developer is free to name the -loggers as desired. - -Since naming Loggers after their owning class is such a common idiom, -the convenience method `LogManager.getLogger()` is provided to -automatically use the calling class's fully qualified class name as the -Logger name. - -Nevertheless, naming loggers after the class where they are located -seems to be the best strategy known so far. - -[#loggerconfig] -=== LoggerConfig - -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] -objects are created when Loggers are declared in the logging -configuration. The LoggerConfig contains a set of Filters that must -allow the LogEvent to pass before it will be passed to any Appenders. It -contains references to the set of Appenders that should be used to -process the event. - -==== Log Levels - -LoggerConfigs will be assigned a Log -{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/Level.html[`Level`]. -The set of built-in levels includes ALL, TRACE, DEBUG, INFO, WARN, ERROR, -FATAL, and OFF. Log4j 2 also supports {log4j2-url}/manual/customloglevels.adoc[custom log -levels]. Another mechanism for getting more granularity is to use -{log4j2-url}/manual/markers.adoc[markers] instead. The OFF and ALL -levels are not intended to be used on calls to the logging API. -Specifying OFF in the configuration implies no logging events should -match while specifying ALL would mean all events match, including custom -events. However, OFF can be used on logging API calls in special cases -where the event should always be logged regardless of the configuration. -However, it is generally recommended that a Marker with a corresponding -global Marker Filter be used instead. - -{logging-services-url}/log4j/1.x/manual.html[Log4j 1] and -{logback-url}/manual/architecture.html#effectiveLevel[Logback] -both have the concept of "Level Inheritance". In Log4j 2, Loggers and -LoggerConfigs are two different objects so this concept is implemented -differently. Each Logger references the appropriate LoggerConfig which -in turn can reference its parent, thus achieving the same effect. - -Below are five tables with various assigned level values and the -resulting levels that will be associated with each Logger. Note that in -all these cases if the root LoggerConfig is not configured a default -Level will be assigned to it. - -.Example 1 -[cols=",,,",options="header",] -|==================================================================== -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Logger Level +Log4j Core is the reference implementation of xref:manual/api.adoc[] and composed of several components. +In this section we will try to explain major pillars its architecture stands on. +An overview these major classes can be depicted as follows: + +[#architecture-diagram] +.An overview of major classes and their relation +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +note left of LoggerContext { + Anchor for the logging system +} + +LoggerContext --> "0..*" Logger + +package "Configuration" as c { + + class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor + } + + note left of Configuration + Encapsulates components compiled + from a user-provided configuration + file (e.g., `log4j2.xml`) + end note + + Configuration --> Filter + + Configuration --> "0..*" Appender + + Configuration --> "0..*" LoggerConfig + + Configuration --> StrSubstitutor + + class Appender { + AbstractManager manager + Layout layout + Filter filter + void append(LogEvent) + } + + Appender --> Layout + + Appender --> Filter + + class Layout { + byte[] encode(LogEvent) + } + + class Filter { + Result filter(LogEvent) + } + + note right of Filter + Note that a `Filter` can + be provided at 4 levels: + 1. `Configuration` + 2. `LoggerConfig` + 3. `AppenderControl` + 4. `Appender` + end note + + class LoggerConfig { + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) + } + + LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + + LoggerConfig --> Filter + + class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) + } + + note right of AppenderControl + Decorates an `Appender` + with a `Filter` + end note + + AppenderControl -[#green,thickness=6]-> Appender + + AppenderControl --> Filter + + class StrSubstitutor { + Interpolator interpolator + String replace(String input) + } + + note right of StrSubstitutor + Responsible for + property substitution + (e.g., `${env:USER}`) + end note + + StrSubstitutor --> Interpolator + + class Interpolator { + StrLookup[] lookups + String lookup(String input) + } + + Interpolator --> "0..*" StrLookup + + class StrLookup { + String lookup(String input) + } +} + +LoggerContext --> Configuration + +class Logger { + void log(Level level, Message message) +} + +note right of Logger + The main API entry point + users interact with +end note + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +class AbstractManager { +} + +Appender -[#green,thickness=6]-> AbstractManager + +@enduml +.... + +At a really high level, + +* A <>, the composition anchor, gets created in combination with a <>. +Both can be created either directly (i.e., programmatically) or indirectly at first interaction with Log4j. +* `LoggerContext` creates <>s that users interact with for logging purposes. +* <> delivers a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a target (file, socket, database, etc.) and typically uses a <> to encode log events and an <> to handle the lifecycle of the target resource. +* <> encapsulates configuration for a `Logger`, as `AppenderControl` and `AppenderRef` for ``Appender``s. +* <> is equipped with <> to allow property substitution in `String`-typed values. +* A typical `log()` call triggers a chain of invocations through classes `Logger`, `LoggerConfig`, `AppenderControl`, `Appender`, and `AbstractManager` in order – this is depicted using green arrows in xref:architecture-diagram[xrefstyle=short]. + +Following sections examine this interplay in detail. + +[#LoggerContext] +== `LoggerContext` + +The {log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContext.html[`LoggerContext`] acts as the anchor point for the logging system. +It is associated with an active <> and is primarily responsible for instantiating <>s. + +[#LoggerContext-diagram] +.`LoggerContext` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext #line.bold { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> Configuration + +LoggerContext --> "0..*" Logger + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +class Logger { + void log(Level level, Message message) +} + +@enduml +.... + +In most cases, applications have a single global `LoggerContext`. +Though in certain cases (e.g., Java EE applications), Log4j can be configured to accommodate multiple ``LoggerContext``s. +Refer to xref:manual/logsep.adoc[] for details. + +[#Configuration] +== `Configuration` + +Every <> is associated with an active link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]. +It models the configuration of all appenders, layouts, filters, loggers, and contains the reference to <>. + +[#Configuration-diagram] +.`Configuration` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> Configuration + +class Configuration #line.bold { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> "0..*" Filter + +Configuration --> "0..*" Appender + +Configuration --> "0..*" LoggerConfig + +Configuration --> StrSubstitutor + +class Appender { + Layout layout + void append(LogEvent) +} + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +class StrSubstitutor { + Interpolator interpolator + String replace(String input) +} +@enduml +.... + +Configuration of Log4j Core is typically done at application initialization. +The preferred way is by reading a xref:manual/configuration.adoc[configuration file], but it can also be done xref:manual/customconfig.adoc[programmatically]. +This is further discussed in xref:manual/config-intro.adoc[]. + +[#reconfiguration] +=== Reconfiguration reliability + +The main motivation for the existing architecture is the reliability to configuration changes. +When a reconfiguration event occurs, two `Configuration` instances are active at the same time. +Threads that already started processing a log event will either: + +* continue logging to the old configuration, if execution already reached the `LoggerConfig` class, +* or switch to the new configuration. + +The service that manages the reconfiguration process is called link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ReliabilityStrategy.html[`ReliabilityStrategy`] and it decides: + +* when should ``Logger``s switch to the new configuration, +* when should the old configuration be stopped. + +.Overview of the reconfiguration process +[plantuml] +.... +@startuml +left to right direction + +package LoggerContext { + object Logger + + package "New Configuration" as c2 { + object "LoggerConfig" as lc2 + object "AppenderControl" as ac2 + object "Appender" as app2 + } + + package "Old Configuration" as c1 { + object "LoggerConfig" as lc1 + object "AppenderControl" as ac1 + object "Appender" as app1 + } +} + +object AbstractManager + +Logger ..> lc1 +lc1 --> ac1 +ac1 --> app1 +app1 --> AbstractManager + +Logger --> lc2 +lc2 --> ac2 +ac2 --> app2 +app2 --> AbstractManager +@enduml +.... + +[#Logger] +== `Logger` + +{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/Logger.html[`Logger`]s are the primary user entry point for logging. +They are created by calling one of the `getLogger()` methods of {log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] – this is further documented in xref:manual/api.adoc[]. +The `Logger` itself performs no direct actions. +It simply has a name and is associated with a <>. + +[#Logger-diagram] +.`Logger` and other directly related classes +[plantuml] +.... +@startuml + +class LoggerContext { + Configuration config + Logger[] loggers + Logger getLogger(String name) +} + +LoggerContext --> "0..*" Logger + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +class Logger #line.bold { + void log(Level level, Message message) +} + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +@enduml +.... + +The hierarchy between <>s, implies the very same hierarchy between ``Logger``s too. +You can use `LogManager.getRootLogger()` to get the root logger. +Note that Log4j API has no assumptions on a `Logger` hierarchy – this is a feature implemented by Log4j Core. + +When the <> is modified, ``Logger``s may become associated with a different `LoggerConfig`, thus causing their behavior to be modified. +Refer to xref:manual/configuration.adoc#configuring-loggers[configuring ``Logger``s] for further information. + +[#LoggerConfig] +== `LoggerConfig` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] binds <> definitions to their associated components (appenders, filters, etc.) as declared in the active <>. +The details of mapping a `Configuration` to ``LoggerConfig``s is explained xref:manual/configuration.adoc#configuring-loggers[here]. +``Logger``s effectively interact with appenders, filters, etc. through corresponding ``LoggerConfig``s. +A `LoggerConfig` essentially contains + +* A reference to its parent (except if it is the root logger) +* A {log4j2-url}/manual/customloglevels.html[level] denoting the severity of messages that are accepted (defaults to `ERROR`) +* <>s that must allow the `LogEvent` to pass before it will be passed to any <>s +* References to <>s that should be used to process the event + +[#LoggerConfig-diagram] +.`LoggerConfig` and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> "0..*" LoggerConfig + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig #line.bold { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +LoggerConfig --> "0..*" AppenderRef + +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) +} + +class Logger { + void log(Level level, Message message) +} + +Logger -[#green,thickness=6]-> LoggerConfig : delegates `log()` + +@enduml +.... + +[#logger-hiearchy] +=== Logger hierarchy + +Log4j Core has a *hierarchical* model of ``LoggerConfig``s, and hence ``Logger``s. +A `LoggerConfig` called `child` is said to be parented by `parent`, if `parent` has the _longest prefix match_ on name. +This match is case-sensitive and performed after tokenizing the name by splitting it from `.` (dot) characters. +For a positive name match, tokens must match exhaustively. +See xref:#logger-hiearchy-diagram[xrefstyle=short] for an example. + +[#logger-hiearchy-diagram] +.Example hierarchy of loggers named `X`, `X.Y`, `X.Y.Z`, and `X.YZ` +[plantuml] +.... +@startmindmap +* root +** X +*** X.Y +**** X.Y.Z +*** X.YZ +@endmindmap +.... + +If a `LoggerConfig` is not provided an explicit level, it will be inherited from its parent. +Similarly, if a user programmatically requests a `Logger` with a name that doesn't have a directly corresponding `LoggerConfig` configuration entry with its name, the `LoggerConfig` of the parent will be used. + +.Click for examples on `LoggerConfig` hierarchy +[%collapsible] +==== +Below we demonstrate the `LoggerConfig` hierarchy by means of _level inheritance_. +That is, we will examine the effective level of a `Logger` in various `LoggerConfig` settings. + +.Only the root logger is configured with a level, and it is `DEBUG` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` name |Configured level |Effective level |root |root |DEBUG |DEBUG -|X |root |DEBUG |DEBUG -|X.Y |root |DEBUG |DEBUG -|X.Y.Z |root |DEBUG |DEBUG -|==================================================================== - -In example 1 above, only the root logger is configured and has a Log -Level. All the other Loggers reference the root LoggerConfig and use its -Level. - -.Example 2 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +|X |root | |DEBUG +|X.Y |root | |DEBUG +|X.Y.Z |root | |DEBUG +|=== + +.All loggers are configured with a level +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR |X.Y |X.Y |INFO |INFO |X.Y.Z |X.Y.Z |WARN |WARN -|============================================================= - -In example 2, all loggers have a configured LoggerConfig and obtain -their Level from it. +|=== -.Example 3 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +.All loggers are configured with a level, except the logger `X.Y` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X |ERROR |ERROR +|X.Y |X | |ERROR |X.Y.Z |X.Y.Z |WARN |WARN -|============================================================= - -In example 3, the loggers`root`, `X` and `X.Y.Z` each have a configured -LoggerConfig with the same name. The Logger `X.Y` does not have a -configured LoggerConfig with a matching name so uses the configuration -of LoggerConfig `X` since that is the LoggerConfig whose name has the -the longest match to the start of the Logger's name. - -.Example 4 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |level -|root |root |DEBUG |DEBUG -|X |X |ERROR |ERROR -|X.Y |X |ERROR |ERROR -|X.Y.Z |X |ERROR |ERROR -|============================================================= - -In example 4, the loggers `root` and `X` each have a Configured -LoggerConfig with the same name. The loggers `X.Y` and `X.Y.Z` do not -have configured LoggerConfigs and so get their Level from the -LoggerConfig assigned to them, `X`, since it is the LoggerConfig whose -name has the longest match to the start of the Logger's name. - -.Example 5 -[cols=",,,",options="header",] -|============================================================= -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |level +|=== + +.All loggers are configured with a level, except loggers `X.Y` and `X.Y.Z` +[%header,cols="1m,1m,1m,1m"] +|=== +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X.Y |INFO |INFO -|X.YZ |X |ERROR |ERROR -|============================================================= - -In example 5, the loggers `root`.`X`, and `X.Y` each has a configured -LoggerConfig with the same name. The logger `X.YZ` does not have -configured LoggerConfig and so gets its Level from the LoggerConfig -assigned to it, `X`, since it is the LoggerConfig whose name has the -longest match to the start of the Logger's name. It is not associated -with LoggerConfig `X.Y` since tokens after periods must match exactly. - -.Example 6 -[cols=4*,options="header"] +|X.Y |X | |ERROR +|X.Y.Z |X | |ERROR +|=== + +.All loggers are configured with a level, except the logger `X.YZ` +[%header,cols="1m,1m,1m,1m"] |=== -|Logger Name |Assigned LoggerConfig |LoggerConfig Level |Level +|Logger name |Assigned `LoggerConfig` |Configured level |Effective level |root |root |DEBUG |DEBUG |X |X |ERROR |ERROR -|X.Y |X.Y | |ERROR -|X.Y.Z |X.Y | |ERROR +|X.Y |X.Y |INFO |INFO +|X.YZ |X | |ERROR |=== +==== -In example 6, LoggerConfig X.Y has no configured level so it inherits -its level from LoggerConfig X. Logger X.Y.Z uses LoggerConfig X.Y since -it doesn't have a LoggerConfig with a name that exactly matches. It too -inherits its logging level from LoggerConfig X. +For further information on log levels and using them for filtering purposes in a configuration, see {log4j2-url}/manual/customloglevels.html[Levels]. -The table below illustrates how Level filtering works. In the table, -the vertical header shows the Level of the LogEvent, while the horizontal -header shows the Level associated with the appropriate LoggerConfig. The -intersection identifies whether the LogEvent would be allowed to pass -for further processing (Yes) or discarded (No). +[#Filter] +== `Filter` -[cols=8*,options="header"] -|=== -|Event Level -7+|LoggerConfig Level +In addition to <>, Log4j provides link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[`Filter`]s to evaluate the parameters of a logging call (i.e., context-wide filter) or a log event, and decide if it should be processed further in the pipeline. -|  |`TRACE` |`DEBUG` |`INFO` |`WARN` |`ERROR` |`FATAL` |`OFF` +[#Filter-diagram] +.`Filter` and other directly related classes +[plantuml] +.... +@startuml -|`ALL` |❌ |❌ |❌ |❌ |❌ |❌ |❌ +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} -|`TRACE` |✅ |❌ |❌ |❌ |❌ |❌ |❌ +Configuration --> "0..*" Filter -|`DEBUG` |✅ |✅ |❌ |❌ |❌ |❌ |❌ +Configuration --> "0..*" LoggerConfig -|`INFO` |✅ |✅ |✅ |❌ |❌ |❌ |❌ +class Filter #line.bold { + Result filter(LogEvent) +} -|`WARN` |✅ |✅ |✅ |✅ |❌ |❌ |❌ +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} -|`ERROR` |✅ |✅ |✅ |✅ |✅ |❌ |❌ +LoggerConfig --> "0..*" AppenderRef -|`FATAL` |✅ |✅ |✅ |✅ |✅ |✅ |❌ +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl -|`OFF` |✅ |✅ |✅ |✅ |✅ |✅ |✅ -|=== +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +AppenderRef --> Filter + +AppenderControl --> Filter + +@enduml +.... + +Refer to xref:manual/filters.adoc[] for further information. + +[#Appender] +== `Appender` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[`Appender`]s are responsible for delivering a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] to a certain target; console, file, database, etc. +While doing so, they typically use <>s to encode the log event. +See xref:manual/appenders.adoc[] for the complete guide. + +[#Appender-diagram] +.`Appender` and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> "0..*" Filter -=== Filter - -In addition to the automatic log Level filtering that takes place as -described in the previous section, Log4j provides -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Filter.html[`Filter`]s -that can be applied before control is passed to any LoggerConfig, after -control is passed to a LoggerConfig but before calling any Appenders, -after control is passed to a LoggerConfig but before calling a specific -Appender, and on each Appender. In a manner very similar to firewall -filters, each Filter can return one of three results, `Accept`, `Deny` -or `Neutral`. A response of `Accept` means that no other Filters should -be called and the event should progress. A response of `Deny` means the -event should be immediately ignored and control should be returned to -the caller. A response of `Neutral` indicates the event should be passed -to other Filters. If there are no other Filters the event will be -processed. - -Although an event may be accepted by a Filter the event still might not -be logged. This can happen when the event is accepted by the -pre-LoggerConfig Filter but is then denied by a LoggerConfig filter or -is denied by all Appenders. - -=== Appender - -The ability to selectively enable or disable logging requests based on -their logger is only part of the picture. Log4j allows logging requests -to print to multiple destinations. In log4j speak, an output destination -is called an -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Appender.html[`Appender`]. -Currently, appenders exist for the console, files, remote socket -servers, Apache Flume, remote UNIX Syslog daemons, and various -database APIs. See the section on xref:manual/appenders.adoc[Appenders] for -more details on the various types available. More than one Appender can -be attached to a Logger. - -An Appender can be added to a Logger by calling the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html#addLoggerAppender(org.apache.logging.log4j.core.Logger,%20org.apache.logging.log4j.core.Appender)[`addLoggerAppender`] -method of the current Configuration. If a LoggerConfig matching the name -of the Logger does not exist, one will be created, and the Appender will be -attached to it and then all Loggers will be notified to update their -LoggerConfig references. - -*Each enabled logging request for a given logger will be forwarded to -all the appenders in that Logger's LoggerConfig as well as the Appenders -of the LoggerConfig's parents.* In other words, Appenders are inherited -additively from the LoggerConfig hierarchy. For example, if a console -appender is added to the root logger, then all enabled logging requests -will at least print on the console. If in addition a file appender is -added to a LoggerConfig, say _C_, then enabled logging requests for _C_ -and _C_'s children will print in a file _and_ on the console. It is -possible to override this default behavior so that Appender accumulation -is no longer additive by setting `additivity="false"` on the Logger -declaration in the configuration file. - -The rules governing appender additivity are summarized below. - -Appender Additivity:: -The output of a log statement of Logger _L_ will go to all the -Appenders in the LoggerConfig associated with _L_ and the ancestors of -that LoggerConfig. This is the meaning of the term "appender -additivity". -+ -However, if an ancestor of the LoggerConfig associated with Logger -_L_, say _P_, has the additivity flag set to `false`, then _L_'s -output will be directed to all the appenders in _L_'s LoggerConfig and -it's ancestors up to and including _P_ but not the Appenders in any of -the ancestors of _P_. -+ -Loggers have their additivity flag set to `true` by default. - -The table below shows an example: +Configuration --> "0..*" Appender +Configuration --> "0..*" LoggerConfig + +class Appender #line.bold { + Layout layout + void append(LogEvent) +} + +Appender -[#green,thickness=6]-> Layout + +class Layout { + byte[] encode(LogEvent) +} + +class Filter { + Result filter(LogEvent) +} + +class LoggerConfig { + AppenderRef[] appenderRefs + AppenderControl[] appenderControls + Level level + Filter filter + void log(LogEvent) +} + +LoggerConfig --> "0..*" AppenderRef + +LoggerConfig -[#green,thickness=6]-> "0..*" AppenderControl + +LoggerConfig --> Filter + +class AppenderRef { + String appenderName + Level level + Filter filter +} + +AppenderRef --> Filter + +class AppenderControl { + Appender appender + Filter filter + void append(LogEvent) +} + +AppenderControl -[#green,thickness=6]-> Appender + +AppenderControl --> Filter + +@enduml +.... + +An `Appender` can be added to a <> by calling the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html#addLoggerAppender(org.apache.logging.log4j.core.Logger,%20org.apache.logging.log4j.core.Appender)[`addLoggerAppender()`] method of the current <>. +If a <> matching the name of the `Logger` does not exist, one will be created, and the `Appender` will be attached to it, and then all ``Logger``s will be notified to update their `LoggerConfig` references. + +[#appender-additivity] +=== Appender additivity + +Each enabled logging request for a given logger will be forwarded to all the appenders in the corresponding ``Logger``'s `LoggerConfig`, as well as to the ``Appender``s of the ``LoggerConfig``'s parents. +In other words, ``Appender``s are inherited *additively* from the `LoggerConfig` hierarchy. +For example, if a console appender is added to the root logger, then all enabled logging requests will at least print on the console. +If in addition a file appender is added to a `LoggerConfig`, say `LC`, then enabled logging requests for `LC` and ``LC``'s children will print in a file _and_ on the console. +It is possible to override this default behavior so that appender accumulation is no longer additive by setting `additivity` attribute to `false` on xref:manual/configuration.adoc#configuring-loggers[the `Logger` declaration in the configuration file]. + +The output of a log statement of `Logger` `L` will go to all the appenders in the `LoggerConfig` associated with `L` and the ancestors of that `LoggerConfig`. +However, if an ancestor of the `LoggerConfig` associated with `Logger` +`L`, say `P`, has the additivity flag set to `false`, then ``L``'s output will be directed to all the appenders in ``L``'s `LoggerConfig` and it's ancestors up to and including `P` but not the appenders in any of the ancestors of `P`. + +.Click for an example on appender additivity +[%collapsible] +==== +[#appender-additivity-diagram] +.Example hierarchy of logger configurations to demonstrate appender additivity +[plantuml] +.... +@startmindmap +* root +** A +*** A.B1 (additivity=false) +**** A.B1.C +***** A.B1.C.D +*** A.B2.C +**** A.B2.C.D (additivity=false) +@endmindmap +.... + +In xref:#appender-additivity-diagram[xrefstyle=short], the effective appenders for each logger configuration are as follows: + +.Effective appenders of logger configurations in xref:#appender-additivity-diagram[xrefstyle=short] +[cols="1c,1c,1c,1c,1c,1c,1c"] |=== -|Logger Name |Added Appenders |Additivity Flag |Output Targets |Comment - -|root -|A1 -|not applicable -|A1 -|The root logger has no parent so additivity does not apply to it. - -|x -|A-x1, A-x2 -|true -|A1, A-x1, A-x2 -|Appenders of "x" and root. - -|x.y -|none -|true -|A1, A-x1, A-x2 -|Appenders of "x" and root. It would not be typical to configure a Logger with no Appenders. - -|x.y.z -|A-xyz1 -|true -|A1, A-x1, A-x2, A-xyz1 -|Appenders in "x.y.z", "x" and root. - -|security -|A-sec -|false -|A-sec -|No appender accumulation since the additivity flag is set to `false`. - -|security.access -|none -|true -|A-sec -|Only appenders of "security" because the additivity flag in "security" is set to `false`. +.2+^.^h| Appender +6+^.h|Logger configuration + +| `A` +| `A.B1` +| `A.B1.C` +| `A.B1.C.D` +| `A.B2.C` +| `A.B2.C.D` + +| `root` +| ✅ +| ✅ +| ✅ +| ✅ +| ✅ +| ❌ + +| `A` +| ✅ +| ❌ +| ❌ +| ❌ +| ✅ +| ❌ + +| `A.B1` +| - +| ✅ +| ✅ +| ✅ +| - +| - + +| `A.B1.C` +| - +| - +| ✅ +| ✅ +| - +| - + +| `A.B1.C.D` +| - +| - +| - +| ✅ +| - +| - + +| `A.B2.C` +| - +| - +| - +| - +| ✅ +| ❌ + +| `A.B2.C.D` +| - +| - +| - +| - +| - +| ✅ |=== +==== -=== Layout +[#AbstractManager] +=== `AbstractManager` -More often than not, users wish to customize not only the output -destination but also the output format. This is accomplished by -associating a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[`Layout`] -with an Appender. The Layout is responsible for formatting the LogEvent -according to the user's wishes, whereas an appender takes care of -sending the formatted output to its destination. The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/PatternLayout.html[`PatternLayout`], -part of the standard log4j distribution, lets the user specify the -output format according to conversion patterns similar to the C language -`printf()` function. +To multiplex the access to external resources (files, network connections, etc.), most appenders are split into an +link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/AbstractManager.html[`AbstractManager`] +that handles the low-level access to the external resource and an `Appender` that transforms log events into a format that the manager can handle. -For example, the PatternLayout with the conversion pattern "%r [%t] %-5p -%c - %m%n" will output something akin to: +Managers that share the same resource are shared between appenders regardless of the `Configuration` or `LoggerContext` of the appenders. +For example +xref:manual/appenders.adoc#FileAppender[`FileAppender`]s +with the same `fileName` attribute all share the same +link:../javadoc/log4j-core/org/apache/logging/log4j/core/appender/FileManager.html[`FileManager`]. +[IMPORTANT] +==== +Due to the manager-sharing feature of many Log4j appenders, it is not possible to configure multiple appenders for the same resource that only differ in the way the underlying resource is configured. + +For example, it is not possible to have two file appenders (even in different logger contexts) that use the same file, but a different value of the `append` option. +Since during a <> multiple instances of the same appender exists, it is also not possible to toggle the value of the `append` option through reconfiguration. +==== + +[#Layout] +== `Layout` + +An <> uses a *layout* to encode a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] into a form that meets the needs of whatever will be consuming the log event. + +[#Layout-diagram] +.`Layout` and other directly related classes +[plantuml] .... -176 [main] INFO org.foo.Bar - Located nearest gas station. -.... +@startuml -The first field is the number of milliseconds elapsed since the start of -the program. The second field is the thread making the log request. The -third field is the level of the log statement. The fourth field is the -name of the logger associated with the log request. The text after the -'-' is the message of the statement. +class Appender { + Layout layout + void append(LogEvent) +} -Log4j comes with many different xref:manual/layouts.adoc[Layouts] for various -use cases such as JSON, XML, HTML, and Syslog (including the new RFC -5424 version). Other appenders such as the database connectors fill in -specified fields instead of a particular textual layout. +Appender -[#green,thickness=6]-> Layout -Just as importantly, log4j will render the content of the log message -according to user-specified criteria. For example, if you frequently -need to log `Oranges`, an object type used in your current project, then -you can create an OrangeMessage that accepts an Orange instance and pass -that to Log4j so that the Orange object can be formatted into an -appropriate byte array when required. +class Layout #line.bold { + byte[] encode(LogEvent) +} + +@enduml +.... + +Refer to xref:manual/layouts.adoc[] for details. [#StrSubstitutor] -=== StrSubstitutor and StrLookup - -The -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrSubstitutor.html[`StrSubstitutor`] -class and -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] -interface was borrowed from -https://commons.apache.org/proper/commons-lang/[Apache Commons Lang] and -then modified to support evaluating LogEvents. In addition the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`] -class was borrowed from Apache Commons Configuration to allow the -StrSubstitutor to evaluate variables from multiple StrLookups. It -too was modified to support evaluating LogEvents. Together these provide -a mechanism to allow the configuration to reference variables coming -from System Properties, the configuration file, the ThreadContext Map, -StructuredData in the LogEvent. The variables can either be resolved -when the configuration is processed or as each event is processed if -the component is capable of handling it. See xref:manual/lookups.adoc[Lookups] -for more information. +== `StrSubstitutor` et al. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrSubstitutor.html[`StrSubstitutor`] is a `String` interpolation tool that can be used in both configurations and components (e.g., appenders, layouts). +It accepts an link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/Interpolator.html[`Interpolator`] to determine if a key maps to a certain value. +`Interpolator` is essentially a facade delegating to multiple link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] (aka. _lookup_) implementations. + +[#StrSubstitutor-diagram] +.`StrSubstitutor` et al. and other directly related classes +[plantuml] +.... +@startuml + +class Configuration { + Appender[] appenders + Filter[] filters + LoggerConfig[] loggerConfigs + LoggerConfig getLoggerConfig(String name) + StrSubstitutor substitutor +} + +Configuration --> StrSubstitutor + +class StrSubstitutor #line.bold { + Interpolator interpolator + String replace(String input) +} + +StrSubstitutor --> Interpolator + +class Interpolator { + StrLookup[] lookups + String lookup(String input) +} + +Interpolator --> "0..*" StrLookup + +class StrLookup { + String lookup(String input) +} + +@enduml +.... + +See xref:manual/configuration.adoc#property-substitution[how property substitution works] and xref:manual/lookups.adoc[the predefined lookups] for further information. diff --git a/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc b/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc index 90bf892d869..10a7f64378f 100644 --- a/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/config-intro.adoc @@ -17,17 +17,10 @@ [id=configuration] = Configuration -Logging is a standard method for monitoring the health of an application and diagnosing problems that may arise within it. Even moderately sized applications can contain thousands of logging statements. +To decide which of these statements will be logged and where, users need to configure Log4j Core in one of following ways: -To decide which of these statements will be logged and where, users need to configure Log4j Core in one of two ways: +* Through a xref:manual/configuration.adoc[configuration file] +* Through xref:manual/customconfig.adoc[programmatic configuration] -* through a xref:manual/configuration.adoc[]. -Since version 2.0, the configuration file format has been considered part of the public API and has remained stable across significant version upgrades. - -* through xref:manual/customconfig.adoc[Programmatic Configuration], which provides a larger spectrum of possible customizations but might require code changes during version upgrades. - -[NOTE] -==== -To prevent a chicken-and-egg problem, users can only supply some configuration options (e.g., the configuration file location) through xref:manual/systemproperties.adoc[configuration properties]. -==== \ No newline at end of file +Some meta-configuration options (e.g., the configuration file location) are only available through xref:manual/systemproperties.adoc[system properties]. diff --git a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc index 0662519d88f..fc7f00881dd 100644 --- a/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/customconfig.adoc @@ -14,372 +14,236 @@ See the License for the specific language governing permissions and limitations under the License. //// -= Programmatic Configuration += Programmatic configuration -Log4j 2 provides a few ways for applications to create their own -programmatic configuration: +Next to xref:manual/configuration.adoc[configuration files], Log4j Core can be configured programmatically too. +In this page, we will explore utilities helping with programmatic configuration and demonstrate how they can be leveraged for certain use cases. -* Specify a custom `ConfigurationFactory` to start Log4j with a -programmatic configuration -* Use the `Configurator` to replace the configuration after Log4j started -* Initialize Log4j with a combination of a configuration file and -programmatic configuration -* Modify the current `Configuration` after initialization +[#prelim] +== Preliminaries + +To begin with, we strongly encourage you to check out the xref:manual/architecture.adoc[] page first. +Let's repeat some basic definitions of particular interest: + +xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]:: +It is the anchor of the logging system. +Generally there is one, statically-accessible, global `LoggerContext` for most applications. +But there can be multiple ``LoggerContext``s, for instance, to use in tests, in Java EE web applications, etc. + +xref:manual/architecture.adoc#Configuration[`Configuration`]:: +It encapsulates a Log4j Core configuration (properties, appenders, loggers, etc.) and is associated with a `LoggerContext`. + +[#tooling] +== Tooling + +For programmatic configuration, Log4j Core essentially provides the following tooling: + +<>:: for declaratively creating a `Configuration` + +<>:: for associating a `Configuration` with a `LoggerContext` + +<>:: for registering a `Configuration` factory to xref:manual/configuration.adoc[the configuration file mechanism] + +In short, we will create ``Configuration``s using `ConfigurationBuilder`, and activate them using `Configurator`. [#ConfigurationBuilder] -== The ConfigurationBuilder API - -Starting with release 2.4, Log4j provides a `ConfigurationBuilder` and a -set of component builders that allow a `Configuration` to be created -fairly easily. Actual configuration objects like `LoggerConfig` or -`Appender` can be unwieldy; they require a lot of knowledge about Log4j -internals which makes them difficult to work with if all you want is to -create a `Configuration`. - -The new `ConfigurationBuilder` API (in the -`org.apache.logging.log4j.core.config.builder.api` package) allows users -to create Configurations in code by constructing component -_definitions_. There is no need to work directly with actual -configuration objects. Component definitions are added to the -`ConfigurationBuilder`, and once all the definitions have been collected -all the actual configuration objects (like Loggers and Appenders) are -constructed. - -`ConfigurationBuilder` has convenience methods for the base components -that can be configured such as Loggers, Appenders, Filter, Properties, -etc. However, Log4j 2's plugin mechanism means that users can create any -number of custom components. As a trade-off, the `ConfigurationBuilder` -API provides only a limited number of "strongly typed" convenience -methods like `newLogger()`, `newLayout()` etc. The generic -`builder.newComponent()` method can be used if no convenience method -exists for the component you want to configure. - -For example, the builder does not know what sub-components can be -configured on specific components such as the RollingFileAppender vs. -the RoutingAppender. To specify a triggering policy on a -RollingFileAppender you would use builder.newComponent(). - -Examples of using the `ConfigurationBuilder` API are in the sections that -follow. +=== `ConfigurationBuilder` -[#ConfigurationFactory] -== Understanding ConfigurationFactory - -During initialization, Log4j 2 will search for available -xref:manual/extending.adoc#ConfigurationFactory[ConfigurationFactories] and -then select the one to use. The selected `ConfigurationFactory` creates -the `Configuration` that Log4j will use. Here is how Log4j finds the -available ConfigurationFactories: - -1. A system property named `log4j2.configurationFactory` can be set -with the name of the ConfigurationFactory to be used. -2. `ConfigurationFactory.setConfigurationFactory(ConfigurationFactory)` -can be called with the instance of the `ConfigurationFactory` to be used. -This must be called before any other calls to Log4j. -3. A `ConfigurationFactory` implementation can be added to the classpath -and configured as a plugin in the "ConfigurationFactory" category. The -`@Order` annotation can be used to specify the relative priority when -multiple applicable ConfigurationFactories are found. - -ConfigurationFactories have the concept of "supported types", which -basically maps to the file extension of the configuration file that the -ConfigurationFactory can handle. If a configuration file location is -specified, ConfigurationFactories whose supported type does not include -"*" or the matching file extension will not be used. - -[#Example] -== Initialize Log4j Using ConfigurationBuilder with a Custom ConfigurationFactory - -One way to programmatically configure Log4j 2 is to create a custom -`ConfigurationFactory` that uses the -<> to create a -Configuration. The below example overrides the `getConfiguration()` -method to return a `Configuration` created by the `ConfigurationBuilder`. -This will cause the `Configuration` to automatically be hooked into Log4j -when the `LoggerContext` is created. In the example below, because it -specifies a supported type of "*" it will override any configuration -files provided. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html[`ConfigurationBuilder`] interface models a fluent API to programmatically create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`]s. +If you have ever created a xref:manual/configuration.adoc[Log4j Core configuration file], consider `ConfigurationBuilder` as a convenience utility to model the very same declarative configuration structure programmatically. + +Let's show `ConfigurationBuilder` usage with an example. +Consider the following Log4j Core configuration file: + +[tabs] +==== +XML:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.xml[`log4j2.xml`] +[source,xml] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.xml[lines=24..34,indent=0] +---- + +JSON:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.json[`log4j2.json`] +[source,json] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.json[lines=3..16,indent=0] +---- + +YAML:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.yaml[`log4j2.yaml`] +[source,yaml] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.yaml[lines=19..-1,indent=0] +---- +Properties:: ++ +.Snippet from an example {antora-examples-url}/manual/customconfig/ConfigurationBuilder/log4j2.properties[`log4j2.properties`] +[source,properties] +---- +include::example$manual/customconfig/ConfigurationBuilder/log4j2.properties[lines=17..-1] +---- +==== + +Above Log4j Core configuration can be programmatically built using `ConfigurationBuilder` as follows: + +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -@Namespace(ConfigurationFactory.NAMESPACE) -@Plugin -@Order(50) -public class CustomConfigurationFactory extends ConfigurationFactory { - - static Configuration createConfiguration(final String name, ConfigurationBuilder builder) { - builder.setConfigurationName(name); - builder.setStatusLevel(Level.ERROR); - builder.add(builder.newFilter("ThresholdFilter", Filter.Result.ACCEPT, Filter.Result.NEUTRAL). - addAttribute("level", Level.DEBUG)); - AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE"). - addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT); - appenderBuilder.add(builder.newLayout("PatternLayout"). - addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); - appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, - Filter.Result.NEUTRAL).addAttribute("marker", "FLOW")); - builder.add(appenderBuilder); - builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG). - add(builder.newAppenderRef("Stdout")). - addAttribute("additivity", false)); - builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); - return builder.build(); - } - - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { - return getConfiguration(loggerContext, source.toString(), null); - } - - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final String name, final URI configLocation) { - ConfigurationBuilder builder = newConfigurationBuilder(); - return createConfiguration(name, builder); - } - - @Override - protected String[] getSupportedTypes() { - return new String[] {"*"}; - } -} +include::example$manual/customconfig/Usage.java[tag=createConfiguration,indent=0] ---- +<1> The default `ConfigurationBuilder` instance is obtained using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilderFactory.html#newConfigurationBuilder()[`ConfigurationBuilderFactory.newConfigurationBuilder()`] static method +<2> Add the appender along with the layout +<3> Add the root logger along with a level and appender reference +<4> Create the configuration, but *don't initialize* it ++ +[TIP] +==== +It is a good practice to not initialize ``Configuration``s when they are constructed. +This task should ideally be delegated to <>. +==== + +`ConfigurationBuilder` has convenience methods for the base components that can be configured such as loggers, appenders, filters, properties, etc. +Though there are cases where the provided convenience methods fall short of: -As of version 2.7, the `ConfigurationFactory.getConfiguration()` methods -take an additional `LoggerContext` parameter. +* Custom xref:manual/plugins.adoc#core[plugins that are declared to be represented in a configuration] +* Custom subcomponents (e.g., a xref:manual/appenders.adoc#TriggeringPolicies[triggering policy] for xref:manual/appenders.adoc#RollingFileAppender[`RollingFileAppender`]) + +For those, you can use the generic link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/builder/api/ConfigurationBuilder.html#newComponent()[`ConfigurationBuilder#newComponent()`] method. + +See {project-github-url}/log4j-core-test/src/test/java/org/apache/logging/log4j/core/config/Configurator1Test.java[`Configurator1Test.java`] for examples on `ConfigurationBuilder`, `newComponent()`, etc. usage. [#Configurator] -== Reconfigure Log4j Using ConfigurationBuilder with the Configurator +=== `Configurator` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configurator.html[`Configurator`] is a programmatic interface to associate a ``Configuration`` with either new, or an existing `LoggerContext`. -An alternative to a custom `ConfigurationFactory` is to configure with the -`Configurator`. Once a `Configuration` object has been constructed, it can -be passed to one of the `Configurator.initialize` methods to set up the -Log4j configuration. +[#Configurator-initialize] +==== Obtaining a `LoggerContext` -Using the `Configurator` in this manner allows the application control -over when Log4j is initialized. However, should any logging be attempted -before `Configurator.initialize()` is called then the default -configuration will be used for those log events. +You can use `Configurator` to obtain a `LoggerContext`: +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -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)); -AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", - ConsoleAppender.Target.SYSTEM_OUT); -appenderBuilder.add(builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); -appenderBuilder.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL) - .addAttribute("marker", "FLOW")); -builder.add(appenderBuilder); -builder.add(builder.newLogger("org.apache.logging.log4j", Level.DEBUG) - .add(builder.newAppenderRef("Stdout")).addAttribute("additivity", false)); -builder.add(builder.newRootLogger(Level.ERROR).add(builder.newAppenderRef("Stdout"))); -ctx = Configurator.initialize(builder.build()); +include::example$manual/customconfig/Usage.java[tag=useConfiguration,indent=0] ---- -This example shows how to create a configuration that includes a -RollingFileAppender. +`initialize()` will either return the `LoggerContext` currently associated with the caller, or create a new one. +This is a convenient way to create isolated ``LoggerContext``s for tests, etc. + +[#Configurator-reconfigure] +==== Reconfiguring the active `LoggerContext` + +You can use `Configurator` to reconfigure the active `LoggerContext` as follows: +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -ConfigurationBuilder builder = ConfigurationBuilderFactory.newConfigurationBuilder(); - -builder.setStatusLevel(Level.ERROR); -builder.setConfigurationName("RollingBuilder"); -// create a console appender -AppenderComponentBuilder appenderBuilder = builder.newAppender("Stdout", "CONSOLE").addAttribute("target", - ConsoleAppender.Target.SYSTEM_OUT); -appenderBuilder.add(builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n%throwable")); -builder.add(appenderBuilder); -// create a rolling file appender -LayoutComponentBuilder layoutBuilder = builder.newLayout("PatternLayout") - .addAttribute("pattern", "%d [%t] %-5level: %msg%n"); -ComponentBuilder triggeringPolicy = builder.newComponent("Policies") - .addComponent(builder.newComponent("CronTriggeringPolicy").addAttribute("schedule", "0 0 0 * * ?")) - .addComponent(builder.newComponent("SizeBasedTriggeringPolicy").addAttribute("size", "100M")); -appenderBuilder = builder.newAppender("rolling", "RollingFile") - .addAttribute("fileName", "target/rolling.log") - .addAttribute("filePattern", "target/archive/rolling-%d{MM-dd-yy}.log.gz") - .add(layoutBuilder) - .addComponent(triggeringPolicy); -builder.add(appenderBuilder); - -// create the new logger -builder.add(builder.newLogger("TestLogger", Level.DEBUG) - .add(builder.newAppenderRef("rolling")) - .addAttribute("additivity", false)); - -builder.add(builder.newRootLogger(Level.DEBUG) - .add(builder.newAppenderRef("rolling"))); -LoggerContext ctx = Configurator.initialize(builder.build()); +include::example$manual/customconfig/Usage.java[tag=reconfigureActiveLoggerContext,indent=0] ---- -[#Hybrid] -== Initialize Log4j by Combining Configuration File with Programmatic Configuration +Using the `Configurator` in this manner allows the application control over when Log4j is initialized. +However, should any logging be attempted before `Configurator.initialize()` is called then the default configuration will be used for those log events. + +[#ConfigurationFactory] +=== [[Example]] `ConfigurationFactory` -Sometimes you want to configure with a configuration file but do some -additional programmatic configuration. A possible use case might be that -you want to allow for a flexible configuration using XML but at the same -time make sure there are a few configuration elements that are always -present that can't be removed. +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] interface, which is mainly used by xref:manual/configuration.adoc#automatic-configuration[the configuration file mechanism] to load a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`], can be leveraged to inject a custom `Configuration`. +You need to -The easiest way to achieve this is to extend one of the standard -`Configuration` classes (`XMLConfiguration`, `JSONConfiguration`) and then -create a new `ConfigurationFactory` for the extended class. After the -standard configuration completes the custom configuration can be added -to it. +* xref:manual/configuration.adoc#ConfigurationFactory[Create a custom `ConfigurationFactory` plugin] +* Assign it a higher priority (i.e., higher `@Order` value) +* Support all configuration file types (i.e. return `*` from `getSupportedTypes()`) -The example below shows how to extend `XMLConfiguration` to manually add -an `Appender` and a `LoggerConfig` to the configuration. +Consider the example below: +.Snippet from an example {antora-examples-url}/manual/customconfig/ExampleConfigurationFactory.java[`ExampleConfigurationFactory.java`] [source,java] ---- -@Namespace("ConfigurationFactory") -@Plugin("MyXMLConfigurationFactory") -@Order(10) -public class MyXMLConfigurationFactory extends ConfigurationFactory { - - /** - * Valid file extensions for XML files. - */ - public static final String[] SUFFIXES = new String[] {".xml", "*"}; - - /** - * Return the Configuration. - * @param source The InputSource. - * @return The Configuration. - */ - public Configuration getConfiguration(InputSource source) { - return new MyXMLConfiguration(source, configFile); - } - - /** - * Returns the file suffixes for XML files. - * @return An array of File extensions. - */ - public String[] getSupportedTypes() { - return SUFFIXES; - } -} - -public class MyXMLConfiguration extends XMLConfiguration { - public MyXMLConfiguration(final ConfigurationFactory.ConfigurationSource configSource) { - super(configSource); - } - - @Override - protected void doConfigure() { - super.doConfigure(); - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Layout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", - "false", "false", "4000", layout, null, "false", null, config); - appender.start(); - addAppender(appender); - LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", - "true", refs, null, config, null ); - loggerConfig.addAppender(appender, null, null); - addLogger("org.apache.logging.log4j", loggerConfig); - } -} +include::example$manual/customconfig/ExampleConfigurationFactory.java[tag=class] ---- +<1> `getConfiguration(LoggerContext, ConfigurationSource)` is only called if `ConfigurationSource` is not null. +This is possible if the `Configuration` is provided programmatically. +Hence, you are encouraged to implement `getConfiguration(LoggerContext, String, URI)` overload too. -[#AddingToCurrent] -== Programmatically Modifying the Current Configuration after Initialization +[#guides] +== How-to guides -Applications sometimes have the need to customize logging separate from -the actual configuration. Log4j allows this although it suffers from a -few limitations: +In this section we will share guides on programmatically configuring Log4j Core for certain use cases. -1. If the configuration file is changed the configuration will be -reloaded and the manual changes will be lost. -2. Modification to the running configuration requires that all the -methods being called (addAppender and addLogger) be synchronized. +[#load-config-file] +=== Loading a configuration file -As such, the recommended approach for customizing a configuration is to -extend one of the standard Configuration classes, override the setup -method to first do super.setup() and then add the custom Appenders, -Filters and LoggerConfigs to the configuration before it is registered -for use. +<> provides the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html#getInstance()[`getInstance()`] method returning a meta-`ConfigurationFactory` that combines the behaviour of all available ``ConfigurationFactory`` implementations, including xref:manual/configuration.adoc#configuration-factories[the predefined ones]; `XmlConfigurationFactory`, `JsonConfigurationFactory`, etc. +You can use this `getInstance()` method to load a configuration file programmatically, granted that the input file format is supported by at least one of the available `ConfigurationFactory` plugins: -The following example adds an Appender and a new LoggerConfig using that -Appender to the current configuration. - -//TODO: update code example below with new plugin API +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- - final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); - final Configuration config = ctx.getConfiguration(); - Layout layout = PatternLayout.createDefaultLayout(config); - Appender appender = FileAppender.createAppender("target/test.log", "false", "false", "File", "true", - "false", "false", "4000", layout, null, "false", null, config); - appender.start(); - config.addAppender(appender); - AppenderRef ref = AppenderRef.createAppenderRef("File", null, null); - AppenderRef[] refs = new AppenderRef[] {ref}; - LoggerConfig loggerConfig = LoggerConfig.createLogger("false", "info", "org.apache.logging.log4j", - "true", refs, null, config, null ); - loggerConfig.addAppender(appender, null, null); - config.addLogger("org.apache.logging.log4j", loggerConfig); - ctx.updateLoggers(); -} +include::example$manual/customconfig/Usage.java[tag=loadConfigurationFile,indent=0] ---- +<1> Passing the `LoggerContext` argument as null, since this is the first time we are instantiating this `Configuration`, and it is not associated with a `LoggerContext` yet +<2> Passing the configuration name argument as null, since it is not used when the configuration source location is provided +<3> URI pointing to the configuration file; `file://path/to/log4j2.xml`, `classpath:log4j2.xml`, etc. -[#AppendingToWritersAndOutputStreams] -== Appending Log Events to Writers and OutputStreams Programmatically +[#CompositeConfiguration] +=== Combining multiple configurations -Log4j 2.5 provides facilities to append log events to Writers and -OutputStreams. For example, this provides simple integration for JDBC -Driver implementors that use Log4j internally and still want to support -the JDBC APIs `CommonDataSource.setLogWriter(PrintWriter)`, -`java.sql.DriverManager.setLogWriter(PrintWriter)`, and -`java.sql.DriverManager.setLogStream(PrintStream)`. +include::partial$manual/composite-configuration.adoc[tag=intro] -Given any `Writer`, like a `PrintWriter`, you tell Log4j to append -events to that writer by creating a `WriterAppender` and updating the -Log4j configuration: +You can programmatically combine multiple configurations into a single one using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/CompositeConfiguration.html[`CompositeConfiguration`]: +.Snippet from an example {antora-examples-url}/manual/customconfig/Usage.java[`Usage.java`] [source,java] ---- -void addAppender(final Writer writer, final String writerName) { - final LoggerContext context = LoggerContext.getContext(false); - final Configuration config = context.getConfiguration(); - final PatternLayout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = WriterAppender.createAppender(layout, null, writer, writerName, false, true); - appender.start(); - config.addAppender(appender); - updateLoggers(appender, config); -} - -private void updateLoggers(final Appender appender, final Configuration config) { - final Level level = null; - final Filter filter = null; - for (final LoggerConfig loggerConfig : config.getLoggers().values()) { - loggerConfig.addAppender(appender, level, filter); - } - config.getRootLogger().addAppender(appender, level, filter); -} +include::example$manual/customconfig/Usage.java[tag=combineConfigurations,indent=0] ---- +<1> Loading a common, and an application-specific configuration from file +<2> Casting them to `AbstractConfiguration`, the type required by `CompositeConfiguration` +<3> Programmatically creating an uninitialized configuration. +Note that no casting is needed. +<4> Creating a `CompositeConfiguration` using all three configurations created. +Note that passed configuration order matters! + +.How does `CompositeConfiguration` work? +[%collapsible] +==== +include::partial$manual/composite-configuration.adoc[tag=how] +==== -You can achieve the same effect with an `OutputStream`, like a -`PrintStream`: +[#Hybrid] +=== [[AddingToCurrent]] [[AppendingToWritersAndOutputStreams]] Modifying configuration components + +[WARNING] +==== +*We strongly advise against programmatically modifying components of a configuration!* +This section will explain what it is, and why you should avoid it. +==== + +It is unfortunately common that users modify components (appenders, filters, etc.) of a configuration programmatically as follows: [source,java] ---- -void addAppender(final OutputStream outputStream, final String outputStreamName) { - final LoggerContext context = LoggerContext.getContext(false); - final Configuration config = context.getConfiguration(); - final PatternLayout layout = PatternLayout.createDefaultLayout(config); - final Appender appender = OutputStreamAppender.createAppender(layout, null, outputStream, outputStreamName, false, true); - appender.start(); - config.addAppender(appender); - updateLoggers(appender, config); -} +LoggerContext context = LoggerContext.getContext(false); +Configuration config = context.getConfiguration(); +PatternLayout layout = PatternLayout.createDefaultLayout(config); +Appender appender = createCustomAppender(); +appender.start(); +config.addAppender(appender); +updateLoggers(appender, config); ---- -The difference is the use of `OutputStreamAppender` instead of -`WriterAppender`. +This approach is prone several problems: + +* Your code relies on Log4j Core internals which don't have any backward compatibility guarantees. +You not only risk breaking your build at a minor Log4j Core version upgrade, but also make the life of Log4j maintainers trying to evolve the project extremely difficult. +* You move out from the safety zone, where Log4j Core takes care of components' life cycle (initialization, reconfiguration, etc.), and step into a minefield seriously undermining the reliability of your logging setup. + +If you happen to have code programmatically modifying components of a configuration, we advise you to migrate to other declarative approaches shared in this page. +In case of need, feel free to ask for help in {logging-services-url}/support.html#discussions-user[user support channels]. diff --git a/src/site/antora/modules/ROOT/pages/manual/dependencyinjection.adoc b/src/site/antora/modules/ROOT/pages/manual/dependencyinjection.adoc index e6686c541a6..ee1f5b782e0 100644 --- a/src/site/antora/modules/ROOT/pages/manual/dependencyinjection.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/dependencyinjection.adoc @@ -24,19 +24,19 @@ Instance factories may be customized through post-processor service classes whic == Overview -The `org.apache.logging.log4j.plugins.di.ConfigurableInstanceFactory` runtime keeps a registry of bindings between `Key` and `Supplier`, where `org.apache.logging.log4j.plugins.di.Key` describes an instance type `T`, optional qualifier annotation type, and optional name plus namespace, and `java.util.function.Supplier` defines a factory for obtaining instances of `T` matching the corresponding `Key`. -To obtain a factory for a given key, if a binding is not already registered for said key, then the raw class of the key (i.e., the `Class` instance from `Key`) is checked for a single constructor annotated with `@org.apache.logging.log4j.plugins.Inject` or a no-arg constructor if no `@Inject` constructor is present. +The link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/ConfigurableInstanceFactory.html[`ConfigurableInstanceFactory`] runtime keeps a registry of bindings between link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/Key.html[`Key`] and `Supplier`, where `Key` describes an instance type `T`, optional qualifier annotation type, and optional name plus namespace, and `java.util.function.Supplier` defines a factory for obtaining instances of `T` matching the corresponding `Key`. +To obtain a factory for a given key, if a binding is not already registered for said key, then the raw class of the key (i.e., the `Class` instance from `Key`) is checked for a single constructor annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Inject.html[`@Inject`] or a no-arg constructor if no `@Inject` constructor is present. The injectable constructor is invoked with arguments which are obtained by looking up factories matching the `Key` of each constructor parameter. -Then, fields that are annotated with `@Inject` or annotated with an annotation that is itself annotated with `@QualifierType` (i.e., a _qualifier annotation_) are injected from factories matching the `Key` of the field. +Then, fields that are annotated with `@Inject` or annotated with an annotation that is itself annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/QualifierType.html[`@QualifierType`] (i.e., a _qualifier annotation_) are injected from factories matching the `Key` of the field. Next, methods that are annotated with `@Inject` or have a parameter annotated with a qualifier annotation are invoked using factories matching the `Key` of each parameter. Lastly, no-arg methods annotated with `@Inject` are invoked which can be useful for post-injection initialization logic. Additional strategies for resolving factories for unbound keys may be registered which are consulted first before falling back to this `@Inject` reflection logic. -Bindings may be registered using `ConfigurableInstanceFactory::registerBinding` or `ConfigurableInstanceFactory::registerBindingIfAbsent` using explicit `Binding` instances, or they may be registered using `ConfigurableInstanceFactory::registerBundle` as _bundles_ which are instances or injectable `Class` instances that contain one or more annotated factory methods. -Annotated factory methods are non-abstract methods annotated with an annotation that is itself annotated with `@org.apache.logging.log4j.plugins.FactoryType`. -Built-in factory annotations include `@org.apache.logging.log4j.plugins.Factory` and `@org.apache.logging.log4j.plugins.PluginFactory`. +Bindings may be registered using `ConfigurableInstanceFactory::registerBinding` or `ConfigurableInstanceFactory::registerBindingIfAbsent` using explicit `Key` and `Supplier` pairs, or they may be registered using `ConfigurableInstanceFactory::registerBundle` as _bundles_ which are instances or injectable `Class` instances that contain one or more annotated factory methods. +Annotated factory methods are non-abstract methods annotated with an annotation that is itself annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/FactoryType.html[`@FactoryType`]. +Built-in factory annotations include link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Factory.html[`@Factory`] and link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginFactory.html[`@PluginFactory`]. -`ConfigurableInstanceFactory` can create `@Configurable` namespace plugins from a tree of `org.apache.logging.log4j.plugins.Node` instances using `org.apache.logging.log4j.core.config.ConfigurationProcessor`. +`ConfigurableInstanceFactory` can create link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Configurable[`@Configurable`] namespace plugins from a tree of link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Node.html[`Node`] instances using link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationProcessor.html[`ConfigurationProcessor`]. This allows for node attributes and child nodes to be injected into a plugin class along with any other registered bindings. To configure a `Node`, its plugin class is checked for a static method annotated with a factory annotation (such as `@PluginFactory` or `@Factory`) first before falling back to checking for an `@Inject` constructor or a no-args constructor. This static factory method or constructor has its parameters injected. @@ -53,14 +53,16 @@ Injection points are injectable fields or parameters where a dependency should b _Injectable fields_ are fields annotated with `@Inject` or a qualifier annotation. _Injectable methods_ are methods annotated with `@Inject` or are not annotated with a factory annotation and have at least one parameter annotated with a qualifier annotation. _Injectable constructors_ are constructors annotated with `@Inject`; only one such constructor should exist per class. -When a field or parameter is annotated with a name-providing annotation (i.e., an annotation annotated with `@org.apache.logging.log4j.plugins.name.NameProvider`), then the provided name or name of the field or parameter are included in the `Key` for the injection point. -When these elements are annotated with a `@Namespace` annotation or meta-annotation, then that namespace name is included in the `Key` for the injection point. +When a field or parameter is annotated with a name-providing annotation (i.e., an annotation annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/name/NameProvider.html[`@NameProvider`]), then the provided name or name of the field or parameter are included in the `Key` for the injection point. +When these elements are annotated with a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Namespace.html[`@Namespace`] annotation or meta-annotation, then that namespace name is included in the `Key` for the injection point. Similarly, when a field or parameter is annotated with a qualifier annotation, then that qualifier annotation type is included in the `Key` for the injection point. An example of each injection point with a no-arg `@Inject` method to show the post-injected state: [source,java] ---- +import org.apache.logging.log4j.plugins.Inject; + class ExampleBean { @Override public String toString() { @@ -109,40 +111,43 @@ class MethodInjection { === Names and Qualifiers -Qualifiers are annotations that are annotated with `@org.apache.logging.log4j.plugins.QualifierType`. +Qualifiers are annotations that are annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/QualifierType.html[`@QualifierType`]. Qualifiers provide a way to match dependencies and factories based on more than just their type. -For example, the `@org.apache.logging.log4j.plugins.Named` qualifier allows for creating different bindings of the same type with different names (along with support for aliases). +For example, the link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Named.html[`@Named`] qualifier allows for creating different bindings of the same type with different names (along with support for aliases). Qualifiers on an injection point request a binding with that qualifier type and name. Qualifiers on a factory method register a binding with that qualifier type and name. -The name for a qualifier is provided via an `org.apache.logging.log4j.plugins.name.AnnotatedElementNameProvider` strategy class given in the `@NameProvider` annotation declared on the qualifier annotation. -Aliases are likewise provided via an `org.apache.logging.log4j.plugins.name.AnnotatedElementAliasesProvider` strategy class given in the `@AliasesProvider` annotated declared on the qualifier annotation. +The name for a qualifier is provided via an link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/name/AnnotatedElementNameProvider.html[`AnnotatedElementNameProvider`] strategy class given in the `@NameProvider` annotation declared on the qualifier annotation. +Aliases are likewise provided via an link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/name/AnnotatedElementAliasesProvider.html[`AnnotatedElementAliasesProvider`] strategy class given in the `@AliasesProvider` annotated declared on the qualifier annotation. === Scopes Scopes control the lifecycle of instances returned from a factory. -Scopes correspond to an annotation that is annotated with `@org.apache.logging.log4j.plugins.ScopeType`. -By default, `@org.apache.logging.log4j.plugins.Singleton` and the default scope are supported. +Scopes correspond to an annotation that is annotated with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/ScopeType.html[`@ScopeType`]. +By default, link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Singleton.html[`@Singleton`] and the default scope are supported. The `@Singleton` scope ensures that only a single instance is returned from factories bound in that scope. The default scope returns a new instance every time. Additional scopes may be registered via `ConfigurableInstanceFactory::registerScope`. == Injector Callbacks -An `ConfigurableInstanceFactory` may be initialized with `org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor` service classes. +An `ConfigurableInstanceFactory` may be initialized with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/spi/ConfigurableInstanceFactoryPostProcessor.html[`ConfigurableInstanceFactoryPostProcessor`] service classes. These service classes must be declared in their respective `module-info.java` files containing `provides org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor with my.fully.qualified.ClassName;` and should also be declared in a file named `META-INF/services/org.apache.logging.log4j.plugins.di.spi.ConfigurableInstanceFactoryPostProcessor` containing the line `my.fully.qualified.ClassName` for traditional classpath usage. -Post-processor services are invoked in the order defined by the `@Order` annotation on the class. +Post-processor services are invoked in the order defined by the link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Ordered.html[`@Ordered`] annotation on the class. Each post-processor is given the `ConfigurableInstanceFactory` being initialized where it can be introspected and modified. === Configurable Bindings -The default callback sets up bindings for the following keys if none have been registered. -Some of these bindings were previously configured through various system properties which are supported via the default callback and its default bindings, though they can be directly registered via custom callbacks with a negative order value. +Log4j sets up {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/CoreDefaultBundle.java[`CoreDefaultBundle`] containing default bindings for the following types if none are already registered. +More details can be found in the xref:manual/extending.adoc[extending Log4j manual]. -* `org.apache.logging.log4j.core.ContextDataInjector` +* `org.apache.logging.log4j.core.async.AsyncQueueFullPolicy` * `org.apache.logging.log4j.core.config.ConfigurationFactory` +* `org.apache.logging.log4j.core.config.URIConfigurationFactory` * `org.apache.logging.log4j.core.config.composite.MergeStrategy` * `org.apache.logging.log4j.core.impl.LogEventFactory` +* `org.apache.logging.log4j.core.lookup.ConfigurationStrSubstitutor` * `org.apache.logging.log4j.core.lookup.InterpolatorFactory` +* `org.apache.logging.log4j.core.lookup.RuntimeStrSubstitutor` * `org.apache.logging.log4j.core.lookup.StrSubstitutor` * `org.apache.logging.log4j.core.selector.ContextSelector` * `org.apache.logging.log4j.core.time.Clock` @@ -150,3 +155,8 @@ Some of these bindings were previously configured through various system propert * `org.apache.logging.log4j.core.util.ShutdownCallbackRegistry` * `org.apache.logging.log4j.core.util.WatchManager` * `org.apache.logging.log4j.core.config.ConfigurationScheduler` +* `org.apache.logging.log4j.kit.recycler.RecyclerFactory` +* `org.apache.logging.log4j.message.FlowMessageFactory` +* `org.apache.logging.log4j.message.MessageFactory` +* `org.apache.logging.log4j.spi.LoggerContextFactory` +* `org.apache.logging.log4j.spi.Provider` diff --git a/src/site/antora/modules/ROOT/pages/manual/extending.adoc b/src/site/antora/modules/ROOT/pages/manual/extending.adoc index 6bdc61e738b..8ac60d0d690 100644 --- a/src/site/antora/modules/ROOT/pages/manual/extending.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/extending.adoc @@ -14,490 +14,151 @@ See the License for the specific language governing permissions and limitations under the License. //// -= Extending Log4j -Log4j provides numerous ways that it can be manipulated and extended. -This section includes an overview of the various ways that are directly -supported by the Log4j 3 implementation. += Extending + +Log4j provides numerous extension points to adapt it for custom needs. +Several of such extension points are covered in the page of the associated component: + +* Log4j API +** {log4j2-url}/manual/customloglevels.html[Extending levels] +** {log4j2-url}/manual/markers.html[Extending markers] +** {log4j2-url}/manual/messages.html#extending[Extending messages] +** {log4j2-url}/manual/thread-context.html#extending[Extending thread context] +* Log4j Core +** xref:manual/appenders.adoc#extending[Extending appenders] +** xref:manual/filters.adoc#extending[Extending filters] +** xref:manual/layouts.adoc#extending[Extending layouts] +*** xref:manual/json-template-layout.adoc#extending[Extending JSON Template Layout] +*** xref:manual/pattern-layout.adoc#extending[Extending Pattern Layout] +** xref:manual/lookups.adoc#extending[Extending lookups] + +This section guides you on the rest of the Log4j extension points. + +[#mechanisms] +== Extension mechanisms + +Log4j allows extensions primarily using following mechanisms: + +[#Custom_Plugins] +=== Plugins + +include::partial$manual/plugin-preliminaries.adoc[] + +[#bindings] +=== Bindings + +The Log4j plugin system was enhanced in 3.0 to support arbitrary bindings for xref:manual/dependencyinjection.adoc[dependency injection]. +Many shared components in Log4j could be configured via system properties or similar to include a fully qualified class name to use. +Using custom bindings, however, these custom classes can be configured as code. +Default bindings are configured in {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/impl/CoreDefaultBundle.java[`CoreDefaultBundle`]. +Custom bindings can be installed by defining a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/di/spi/ConfigurableInstanceFactoryPostProcessor.html[`ConfigurableInstanceFactoryPostProcessor`] service class which invokes `ConfigurableInstanceFactory::registerBundle` to register the bundle class containing the bindings. +This approach is more portable than the legacy approach of using system properties to define the classes as it removes ambiguity of `ClassLoader` ownership and other details of modules and similar runtimes. + +[#service-loader] +=== ``ServiceLoader``s + +https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/ServiceLoader.html[`ServiceLoader`] is a simple service-provider loading facility baked into the Java platform itself. +Log4j uses ``ServiceLoader``s for extending places where + +* The service needs to be implementation agnostic. +As a result, <> cannot be used, since it is provided by the logging implementation, i.e., Log4j Core. +For instance, this is why {log4j2-url}/manual/thread-context.html#extending[extending Thread Context], which is a Log4j API component, works using ``ServiceLoader``s. + +* The service needs to be loaded before <>. +For instance, this is why <> works using ``ServiceLoader``s. + +Refer to https://docs.oracle.com/javase/{java-target-version}/docs/api/java/util/ServiceLoader.html[the `ServiceLoader` documentation] for details. + +[#system-properties] +=== System properties + +Log4j uses system properties to determine the fully-qualified class name (FQCN) to load for extending a certain functionality. +For instance, <> works using system properties. + +[WARNING] +==== +Loading a class using _only_ its FQCN can result in unexpected behaviour when there are multiple class loaders. +==== + +[#points] +== Extension points + +In this section we will guide you on certain Log4j extension points that are not covered elsewhere. + +[#Provider] +=== `Provider` + +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/Provider.html[`Provider`] is the anchor contract binding Log4j API to an implementation. +For instance, it has been implemented by Log4j Core, Log4j-to-JUL bridge, and Log4j-to-SLF4J bridge modules. + +Under the hood, link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] locates a `Provider` implementation using <>, and delegates invocations to it. +Hence, you can extend it by providing a `org.apache.logging.log4j.spi.Provider` implementation in the form of a `ServiceLoader`. + +Having multiple ``Provider``s in the classpath is strongly discouraged. +A specific provider can be <> using a custom bundle class if Log4j Core is also available. +Alternatively, you can use xref:manual/systemproperties.adoc#log4j.provider[the `log4j.provider` property] to explicitly select one. [#LoggerContextFactory] -== LoggerContextFactory - -The `LoggerContextFactory` binds the Log4j API to its implementation. -The Log4j `LogManager` locates a `LoggerContextFactory` by using -`java.util.ServiceLoader` to locate all instances of -`org.apache.logging.log4j.spi.Provider`. Each implementation must -provide a class that extends `org.apache.logging.log4j.spi.Provider` and -should have a no-arg constructor that delegates to Provider's -constructor passing the Priority, the API versions it is compatible -with, and the class that implements -`org.apache.logging.log4j.spi.LoggerContextFactory`. Log4j will compare -the current API version and if it is compatible the implementation -will be added to the list of providers. The API version in -`org.apache.logging.log4j.LogManager` is only changed when a feature is -added to the API that implementations need to be aware of. If more than -one valid implementation is located the value for the Priority will be -used to identify the factory with the highest priority. Finally, the -class that implements -`org.apache.logging.log4j.spi.LoggerContextFactory` will be instantiated -and bound to the LogManager. In Log4j 2 this is provided by -`Log4jContextFactory`. - -Applications may change the LoggerContextFactory that will be used by - -1. Create a binding to the logging implementation. -.. Implement a new link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[`LoggerContextFactory`]. -.. Implement a class that extends -{log4j2-url}/javadoc/log4j-api/org/apache/logging/spi/Provider.html[`org.apache.logging.spi.Provider`] -with a no-arg constructor that calls super-class's constructor with the -Priority, the API version(s), `LoggerContextFactory` class, and -optionally, a -{log4j2-url}/javadoc/log4j-api/org/apache/logging/log4j/spi/ThreadContextMap.html[`ThreadContextMap`] -implementation class. -.. Create a `META-INF/services/org.apache.logging.spi.Provider` file -that contains the name of the class that implements -`org.apache.logging.spi.Provider`. -2. Setting the system property "log4j2.loggerContextFactory" to the name -of the `LoggerContextFactory` class to use. -3. Setting the property "log4j2.loggerContextFactory" in a properties -file named "log4j2.LogManager.properties" to the name of the -LoggerContextFactory class to use. The properties file must be on the -classpath. - -[#InjectorCallback] -== InjectorCallback -InjectorCallback services are called by an link:../log4j-plugins/apidocs/org/apache/logging/log4j/plugins/di/Injector.html[`Injector`] when its `init()` method is invoked (see link:./dependencyinjection.html[Dependency Injection] for further information about `Injector`). -These callback services are invoked in order using `InjectorCallback::getOrder` and the natural integer comparator. -Callbacks may examine the current state of the invoking `Injector` as well as make changes to bindings or provide default bindings. -Backwards compatibility with system property based methods for configuring various extensions are handled through a default callback with order 0 which allows for making customizations before or after the defaults are set up. -Callback classes must have a no-args constructor and need to be defined in two places: - -1. Add the fully qualified class name of the callback class to a file named `META-INF/services/org.apache.logging.log4j.plugins.di.InjectorCallback`. -2. For compatibility with Java modules, add a line to `module-info.java` containing `provides org.apache.logging.log4j.plugins.di.InjectorCallback with my.fully.qualified.ClassName;`. +=== `LoggerContextFactory` + +link:../javadoc/log4j-api/org/apache/logging/log4j/spi/LoggerContextFactory.html[`LoggerContextFactory`] is the factory class used by Log4j API implementations to create xref:manual/architecture.adoc#LoggerContext[`LoggerContext`]s. +If you are using Log4j Core, you can use <>s to influence the way its `LoggerContextFactory` implementation works. +If you are creating a new Log4j API implementation, you should <> to introduce your custom `LoggerContextFactory` implementation. [#ContextSelector] -== ContextSelector - -ContextSelectors are called by the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[Log4j `LoggerContext` factory]. They perform the actual work of locating or -creating a LoggerContext, which is the anchor for Loggers and their -configuration. ContextSelectors are free to implement any mechanism they -desire to manage LoggerContexts. The default Log4jContextFactory checks -for the presence of an `Injector` binding for `ContextSelector`. -If none are defined, the System Property named "Log4jContextSelector" is checked. -If found, the property is expected to contain the name of the Class that implements the ContextSelector to be used. -This class is then used for creating `ContextSelector` instances. - -Log4j provides five ContextSelectors: - -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/BasicContextSelector.html[`BasicContextSelector`]:: - Uses either a LoggerContext that has been stored in a ThreadLocal or a - common LoggerContext. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ClassLoaderContextSelector.html[`ClassLoaderContextSelector`]:: - Associates LoggerContexts with the ClassLoader that created the caller - of the getLogger(...) call. This is the default ContextSelector. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/JndiContextSelector.html[`JndiContextSelector`]:: - Locates the LoggerContext by querying JNDI. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/async/AsyncLoggerContextSelector.html[`AsyncLoggerContextSelector`]:: - Creates a LoggerContext that ensures that all loggers are - AsyncLoggers. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/osgi/BundleContextSelector.html[`BundleContextSelector`]:: - Associates LoggerContexts with the ClassLoader of the bundle that - created the caller of the getLogger call. This is enabled by default - in OSGi environments. +=== `ContextSelector` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/Log4jContextFactory.html[`Log4jContextFactory`], the Log4j Core implementation of <>, delegates the actual work to a link:../javadoc/log4j-core/org/apache/logging/log4j/core/selector/ContextSelector.html[`ContextSelector`]. +This should be configured as a `ContextSelector` binding in a <>. +Alternatively, it can be configured using xref:manual/systemproperties.adoc#log4j.loggerContext.selector[the `log4j.loggerContext.selector` property]. [#ConfigurationFactory] -== ConfigurationFactory - -Modifying the way in which logging can be configured is usually one of -the areas with the most interest. The primary method for doing that is -by implementing or extending a -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`]. -Log4j provides two ways of adding new ConfigurationFactories. The first -is by defining the system property named "log4j.configurationFactory" to -the name of the class that should be searched first for a configuration. -The second method is by defining the `ConfigurationFactory` as a `Plugin`. - -All the ConfigurationFactories are then processed in order. Each factory -is called on its `getSupportedTypes()` method to determine the file -extensions it supports. If a configuration file is located with one of -the specified file extensions then control is passed to that -`ConfigurationFactory` to load the configuration and create the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] object. - -Most `Configuration` extend the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/AbstractConfiguration.html[`AbstractConfiguration`] class. This class expects that the subclass will process the configuration file and create -a hierarchy of `Node` objects. Each `Node` is fairly simple in that it -consists of the name of the node, the name/value pairs associated with -the node, The `PluginType` of the node and a List of all of its child -Nodes. `Configuration` will then be passed the `Node` tree and -instantiate the configuration objects from that. - -[source,java] ----- -@Namespace("ConfigurationFactory") -@Plugin("XMLConfigurationFactory") -@Order(5) -public class XMLConfigurationFactory extends ConfigurationFactory { - - /** - * Valid file extensions for XML files. - */ - public static final String[] SUFFIXES = new String[] {".xml", "*"}; - - /** - * Returns the Configuration. - * @param loggerContext The logger context. - * @param source The InputSource. - * @return The Configuration. - */ - @Override - public Configuration getConfiguration(final LoggerContext loggerContext, final ConfigurationSource source) { - return new XmlConfiguration(loggerContext, source); - } - - /** - * Returns the file suffixes for XML files. - * @return An array of File extensions. - */ - public String[] getSupportedTypes() { - return SUFFIXES; - } -} ----- +=== `ConfigurationFactory` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationFactory.html[`ConfigurationFactory`] is the factory class used by Log4j Core to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] instances given a xref:manual/architecture.adoc#LoggerContext[`LoggerContext`] and a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/ConfigurationSource.html[`ConfigurationSource`]. + +You can provide a custom `ConfigurationFactory` in the form of a <>. +For example, see {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfigurationFactory.java[`XmlConfigurationFactory.java`] and {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/xml/XmlConfiguration.java[`XmlConfiguration.java`] of Log4j Core. + +A specific configuration factory can be bound in a <> which can take priority over the default configuration factories. +You can also use xref:manual/systemproperties.adoc#log4j.configuration.factory[the `log4j.configuration.factory` property] to explicitly set a `ConfigurationFactory` to be used before any other factory implementation. [#LoggerConfig] -== LoggerConfig - -`LoggerConfig` objects are where Loggers created by applications tie into -the configuration. The Log4j implementation requires that all -LoggerConfigs are based on the LoggerConfig class, so applications -wishing to make changes must do so by extending the `LoggerConfig` class. -To declare the new `LoggerConfig`, declare it as a Plugin of type "Core" -and providing the name that applications should specify as the element -name in the configuration. The `LoggerConfig` should also define a -PluginFactory that will create an instance of the `LoggerConfig`. - -The following example shows how the root `LoggerConfig` simply extends a -generic `LoggerConfig`. - -[source,java] ----- -@Configurable(printObject = true) -@Plugin("root") -public static class RootLogger extends LoggerConfig { - - @PluginFactory - public static LoggerConfig createLogger(@PluginAttribute(defaultBooleanValue = true) boolean additivity, - @PluginAttribute(defaultStringValue = "ERROR") Level level, - @PluginElement AppenderRef[] refs, - @PluginElement Filter filter) { - List appenderRefs = Arrays.asList(refs); - return new LoggerConfig(LogManager.ROOT_LOGGER_NAME, appenderRefs, filter, level, additivity); - } -} ----- +=== `LoggerConfig` + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/LoggerConfig.html[`LoggerConfig`] denotes the `Logger` configurations in a `Configuration`. +A custom `LoggerConfig` needs to satisfy the following conditions: + +* It needs to extend from `LoggerConfig` class +* It needs to be declared as a <> +* It needs to include a `@Configurable` annotation on the class + +For example, see `RootLogger` definition in {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java[`LoggerConfig.java`]. [#LogEventFactory] -== LogEventFactory +=== `LogEventFactory` -A LogEventFactory is used to generate LogEvents. Applications may replace the standard LogEventFactory by setting the value of the system property Log4jLogEventFactory to the name of the custom LogEventFactory class. +Log4j Core uses link:../javadoc/log4j-core/org/apache/logging/log4j/core/impl/LogEventFactory.html[`LogEventFactory`] to create link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`]s. +You can replace the default `LogEventFactory` implementation with a custom one of yours by defining a <> for `LogEventFactory` or by using xref:manual/systemproperties.adoc#log4j.logEvent.factory[the `log4j.logEvent.factory` property]. -Note: When log4j is configured to have xref:manual/async.adoc#AllAsync[all -loggers asynchronous], log events are pre-allocated in a ring buffer and -the `LogEventFactory` is not used. +[NOTE] +==== +xref:manual/async.adoc[] discard `LogEventFactory` and any configuration related with it. +==== [#MessageFactory] -== MessageFactory - -A `MessageFactory` is used to generate `Message` objects. Applications may -replace the standard `ReusableMessageFactory` by setting the value of the -system property `log4j2.messageFactory` to the name of the custom -`MessageFactory` class. - -Flow messages for the `Logger.entry()` and `Logger.exit()` methods have -a separate `FlowMessageFactory`. Applications may replace the -`DefaultFlowMessageFactory` by setting the value of the system property -`log4j2.flowMessageFactory` to the name of the custom `FlowMessageFactory` -class. - -[#Lookups] -== Lookups - -Lookups are the means in which parameter substitution is performed. -During Configuration initialization an "Interpolator" is created that -locates all the Lookups and registers them for use when a variable needs -to be resolved. The interpolator matches the "prefix" portion of the -variable name to a registered Lookup and passes control to it to resolve -the variable. - -A Lookup must be declared using a `@Plugin @Lookup` annotation. The `value` specified on the `@Plugin` annotation will be used to -match the prefix. The example below shows a Lookup that will return -the value of a System Property. - -The provided Lookups are documented here: xref:manual/lookups.adoc[Lookups] - -[source,java] ----- -@Lookup -@Plugin("sys") -public class SystemPropertiesLookup implements StrLookup { - - /** - * Lookup the value for the key. - * @param key the key to be looked up, may be null - * @return The value for the key. - */ - public String lookup(String key) { - return System.getProperty(key); - } - - /** - * Lookup the value for the key using the data in the LogEvent. - * @param event The current LogEvent. - * @param key the key to be looked up, may be null - * @return The value associated with the key. - */ - public String lookup(LogEvent event, String key) { - return System.getProperty(key); - } -} ----- - -[#Filters] -== Filters - -As might be expected, Filters are used to reject or accept log -events as they pass through the logging system. A Filter is declared -using a `@Configurable` annotation with an `elementType` of "filter". -The `value` attribute on the `@Plugin` annotation is used to specify the name -of the element users should use to enable the Filter. Specifying the -`printObject` attribute with a value of "true" indicates that a call to -`toString` will format the arguments to the filter as the configuration is -being processed. The Filter must also specify a `@PluginFactory` method -or `@PluginFactoryBuilder` builder class and method -that will be called to create the Filter. - -The example below shows a Filter used to reject LogEvents based upon -their logging level. Notice the typical pattern where all the filter -methods resolve to a single filter method. - -[source,java] ----- -@Configurable(elementType = Filter.ELEMENT_TYPE, printObject = true) -@Plugin -public final class ThresholdFilter extends AbstractFilter { - - private final Level level; - - private ThresholdFilter(Level level, Result onMatch, Result onMismatch) { - super(onMatch, onMismatch); - this.level = level; - } - - public Result filter(Logger logger, Level level, Marker marker, String msg, Object[] params) { - return filter(level); - } - - public Result filter(Logger logger, Level level, Marker marker, Object msg, Throwable t) { - return filter(level); - } - - public Result filter(Logger logger, Level level, Marker marker, Message msg, Throwable t) { - return filter(level); - } - - @Override - public Result filter(LogEvent event) { - return filter(event.getLevel()); - } - - private Result filter(Level level) { - return level.isAtLeastAsSpecificAs(this.level) ? onMatch : onMismatch; - } - - @Override - public String toString() { - return level.toString(); - } - - /** - * Create a ThresholdFilter. - * @param level The log Level. - * @param onMatch The action to take on a match. - * @param onMismatch The action to take on a mismatch. - * @return The created ThresholdFilter. - */ - @PluginFactory - public static ThresholdFilter createFilter(@PluginAttribute(defaultStringValue = "ERROR") Level level, - @PluginAttribute(defaultStringValue = "NEUTRAL") Result onMatch, - @PluginAttribute(defaultStringValue = "DENY") Result onMismatch) { - return new ThresholdFilter(level, onMatch, onMismatch); - } -} ----- - -[#Appenders] -== Appenders - -Appenders are passed an event, (usually) invoke a Layout to format the -event, and then "publish" the event in whatever manner is desired. -Appenders are declared as `@Configurable` with an -`elementType` of "appender". The `value` attribute on the `@Plugin` annotation -specifies the name of the element users must provide in their -configuration to use the Appender. Appenders should specify `printObject` -as "true" if the toString method renders the values of the attributes -passed to the Appender. - -Appenders must also declare a `@PluginFactory` method that returns an instance -of the appender or a builder class used to create the appender. The example below shows -an Appender named "Stub" that can be used as an initial template. - -Most Appenders use Managers. A manager actually "owns" the resources, -such as an `OutputStream` or socket. When a reconfiguration occurs a new -Appender will be created. However, if nothing significant in the -previous Manager has changed, the new Appender will simply reference it -instead of creating a new one. This insures that events are not lost -while a reconfiguration is taking place without requiring that logging -pause while the reconfiguration takes place. - -[source,java] ----- -@Configurable(elementType = Appender.ELEMENT_TYPE, printObject = true) -@Plugin("Stub") -public final class StubAppender extends AbstractOutputStreamAppender { - - private StubAppender(String name, - Layout layout, - Filter filter, - boolean ignoreExceptions, - StubManager manager) { - super(name, layout, filter, ignoreExceptions, true, manager); - } - - @PluginFactory - public static StubAppender createAppender(@PluginAttribute @Required(message = "No name provided for StubAppender") String name, - @PluginAttribute boolean ignoreExceptions, - @PluginElement Layout layout, - @PluginElement Filter filter) { - - StubManager manager = StubManager.getStubManager(name); - if (manager == null) { - return null; - } - if (layout == null) { - layout = PatternLayout.createDefaultLayout(); - } - return new StubAppender(name, layout, filter, ignoreExceptions, manager); - } -} ----- - -[#Plugin_Builders] -== Plugin Builders - -Some plugins take a lot of optional configuration options. When a plugin -takes many options, it is more maintainable to use a builder class -rather than a factory method (see _Item 2: Consider a builder when faced -with many constructor parameters_ in _Effective Java_ by Joshua Bloch). -There are some other advantages to using an annotated builder class over -an annotated factory method: - -* Attribute names don't need to be specified if they match the field name or the parameter name. -* Default values can be specified in code rather than through an -annotation (also allowing a runtime-calculated default value which isn't -allowed in annotations). -* Adding new optional parameters doesn't require existing programmatic -configuration to be refactored. -* Easier to write unit tests using builders rather than factory methods -with optional parameters. -* Default values are specified via code rather than relying on -reflection and injection, so they work programmatically as well as in a -configuration file. - -Here is an example of a plugin factory from `ListAppender`: - -[source,java] ----- -@PluginFactory -public static ListAppender createAppender( - @PluginAttribute @Required(message = "No name provided for ListAppender") final String name, - @PluginAttribute final boolean entryPerNewLine, - @PluginAttribute final boolean raw, - @PluginElement final Layout layout, - @PluginElement final Filter filter) { - return new ListAppender(name, filter, layout, newLine, raw); -} ----- - -Here is that same factory using a builder pattern instead: - -[source,java] ----- -@PluginFactory -public static Builder newBuilder() { - return new Builder(); -} - -public static class Builder implements org.apache.logging.log4j.plugins.util.Builder { - - private String name; - private boolean entryPerNewLine; - private boolean raw; - private Layout layout; - private Filter filter; - - - public Builder setName( - @PluginAttribute - @Required(message = "No name provided for ListAppender") - final String name) { - this.name = name; - return this; - } - - public Builder setEntryPerNewLine(@PluginAttribute final boolean entryPerNewLine) { - this.entryPerNewLine = entryPerNewLine; - return this; - } - - public Builder setRaw(@PluginAttribute final boolean raw) { - this.raw = raw; - return this; - } - - public Builder setLayout(@PluginElement final Layout layout) { - this.layout = layout; - return this; - } - - public Builder setFilter(@PluginElement final Filter filter) { - this.filter = filter; - return this; - } - - @Override - public ListAppender build() { - return new ListAppender(name, filter, layout, entryPerNewLine, raw); - } -} ----- - -When plugins are being constructed after a configuration has been -parsed, a plugin builder will be used if available, otherwise a plugin -factory method will be used as a fallback. If a plugin contains neither -factory, then it cannot be used from a configuration file (it can still -be used programmatically of course). - -Here is an example of using a plugin factory versus a plugin builder -programmatically: - -[source,java] ----- -ListAppender list1 = ListAppender.createAppender("List1", true, false, null, null); -ListAppender list2 = ListAppender.newBuilder().setName("List1").setEntryPerNewLine(true).build(); ----- +=== `MessageFactory2` -[#Custom_Plugins] -== Custom Plugins +Log4j Core uses link:../javadoc/log4j-api/org/apache/logging/log4j/message/MessageFactory2.html[`MessageFactory2`] to create link:../javadoc/log4j-api/org/apache/logging/log4j/message/Message.html[`Message`]s. +You can replace the default `MessageFactory2` implementation with a custom one of yours by using xref:manual/systemproperties.adoc#log4j.message.factory[the `log4j.message.factory` property]. + +[IMPORTANT] +==== +Message factory implementations are expected to interpret formatting patterns containing placeholders denoted with `{}`. +For instance, the default message factory chooses between a {log4j2-url}/manual/messages.html#SimpleMessage[`SimpleMessage`] and a {log4j2-url}/manual/messages.html#ParameterizedMessage[`ParameterizedMessage`] depending on the presence of placeholders in the formatting pattern. -// TODO -See the xref:manual/plugins.adoc[Plugins] section of the manual. +If you want to change the placeholder style (e.g., switching from `{}` to `%s`), you should *not* replace the default message factory. +Because this will break the existing Log4j API calls using the standard placeholder style. +Instead, you can use link:../javadoc/log4j-api/org/apache/logging/log4j/LogManager.html[`LogManager`] methods accepting a message factory to create ``Logger``s with your custom message factory implementations. +==== diff --git a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc index 8ec816c3d1d..ea91e81d1d4 100644 --- a/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/garbagefree.adoc @@ -57,7 +57,7 @@ If not for yours, keep on reading. In order to have a garbage-free Log4j Core, you need to * <>, -* and employ garbage-free <>, <>, and <>. +* and employ garbage-free <>, <>, and <>. [#core-properties] === Properties diff --git a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc index 81ca4e3fb7b..2c43b44dd27 100644 --- a/src/site/antora/modules/ROOT/pages/manual/layouts.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/layouts.adoc @@ -18,6 +18,8 @@ = Layouts An xref:manual/appenders.adoc[appender] uses a *layout* to encode a link:../javadoc/log4j-core/org/apache/logging/log4j/core/LogEvent.html[`LogEvent`] into a form that meets the needs of whatever will be consuming the log event. +Layouts can encode this data into a `String`, a `byte[]`, or to a `ByteBufferDestination`. +Layouts used to support encoding data into an arbitrary `java.io.Serializable` classes in Log4j 2.x, but due to security concerns, this is no longer directly supported by layouts in Log4j 3.0. This page will try to answer following questions: * <> @@ -554,7 +556,11 @@ include::partial$manual/plugin-preliminaries.adoc[] === Extending layouts Layouts are xref:manual/plugins.adoc[plugins] implementing link:../javadoc/log4j-core/org/apache/logging/log4j/core/Layout.html[the `Layout` interface]. -If your layout is a `String`-based one, we recommend you to extend your plugin class from link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/AbstractStringLayout.html[`AbstractStringLayout`], which contains convenience for some of the boilerplate code shared by `String`-based layouts. +It is recommended you extend from link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/AbstractStringLayout.html[`AbstractStringLayout`]. +While annotating your layout with `@Plugin` and `@Configurable`, you need to make sure that + +* The plugin name in `@Plugin::value` or the simple class name of the plugin if no custom name is given must be unique across all available `Layout` plugins. + You can check out following files for examples: * {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/SyslogLayout.java[`SyslogLayout.java`] – simple, single-file, extending from `AbstractStringLayout` diff --git a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc index 1b9b6768978..8ab7e630214 100644 --- a/src/site/antora/modules/ROOT/pages/manual/plugins.adoc +++ b/src/site/antora/modules/ROOT/pages/manual/plugins.adoc @@ -14,78 +14,210 @@ See the License for the specific language governing permissions and limitations under the License. //// + = Plugins -Log4j 1.x allowed for extension by requiring class attributes on most of -the configuration declarations. In the case of some elements, notably -the PatternLayout, the only way to add new pattern converters was to -extend the PatternLayout class and add them via code. One goal of Log4j -2 is to make extending it extremely easy through the use of plugins. - -In Log4j 3.x, a plugin is declared by adding a `@Plugin` and `@Namespace` annotation to the class declaration. -During initialization the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/Configuration.html[`Configuration`] -will invoke the `PluginRegistry` -to load the built-in Log4j plugins as well as any custom plugins. The -`Injector` locates plugins by looking in the following places: - -1. Plugin collection classes on the classpath that are loaded by java.util.ServiceLoader. -These classes are generated automatically during the build (more details below). -2. (OSGi only) Serialized plugin listing files in each active OSGi -bundle. A `BundleListener` is added on activation to continue checking -new bundles after `log4j-plugins` has started. Bundles must register their plugin collection -class as an OSGi service. -3. Serialized plugin listing files on the classpath. These files were generated by -the plugin annotation processor in Log4j 2 2.x. These are processed to allow -compatibility. - -When multiple plugins use the same case-insensitive `name` within the same plugin category, then which one is selected is determined first by the presence of `@PluginOrder` annotations and then by the previously described plugin loading order. -For example, to override the `File` plugin which is provided by the built-in `FileAppender` class, you would need to place your plugin in a JAR file in the CLASSPATH ahead of`log4j-core.jar`. -This is not recommended; plugin name collisions will cause a warning to be emitted. -Note that in an OSGi environment, the order that bundles are scanned for plugins generally follows the same order that bundles were installed into the framework. -See https://www.osgi.org/javadoc/r5/core/org/osgi/framework/BundleContext.html#getBundles()[`getBundles()`] and https://www.osgi.org/javadoc/r5/core/org/osgi/framework/SynchronousBundleListener.html[`SynchronousBundleListener`]. -In short, name collisions are even more unpredictable in an OSGi environment without additional `@PluginOrder` usage. - -Plugin collection classes are generated by an annotation processor contained -in the log4j-plugins artifact which will automatically scan your code for -Log4j 2 plugins and generate a Java source file that references all the -located plugins. It will also generate a -META-INF/services/org.apache.logging.log4j.plugins.model.PluginService -file in compliance with java.util.ServiceLoader. -There is nothing extra that needs to be done to enable this; -the Java compiler will automatically pick up the annotation processor on -the class path unless you explicitly disable it. - -If annotation processing is disabled plugins may still be registered by either -[loweralpha] -.. manually providing a class that extends `org.apache.logging.log4j.plugins.model.PluginService` -and identifies all the plugins and also declaring a -`META-INF/services/org.apache.logging.log4j.plugins.model.PluginService` file -that provides the fully qualified name of the implemented class or -.. adding another compiler pass to the build process that -only handles annotation processing using the Log4j 2 annotation -processor class, -`org.apache.logging.log4j.plugin.processor.PluginProcessor`. -To do this using Apache Maven, add the following execution to your -_maven-compiler-plugin_ (version 2.2 or higher) build plugin: - -[source,xml] +Log4j plugin system is the de facto extension mechanism embraced by various Log4j Core components. +Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for xref:manual/dependencyinjection.adoc[Log4j-specific needs]. + +[NOTE] +==== +Log4j plugin system is implemented by Log4j Core, the logging implementation. +It is deliberately not a part of the Log4j API to keep the logging API footprint small. +==== + +[TIP] +==== +Did you know about *xref:plugin-reference.adoc[], the documentation extracted from the source code* of all predefined Log4j plugins? +Like Javadoc, but specialized for plugins! +==== + +In this section we will give an overview of the Log4j plugin system by answering certain questions: + +. <<#declare-plugin,How can you declare a plugin?>> +. <<#core,How can you declare a plugin that needs to be represented in a Log4j configuration file?>> +. <<#plugin-registry,How can you register your plugin to Log4j?>> +. <<#plugin-discovery,How does Log4j discover plugins?>> +. <<#plugin-load,How can you load other plugins in a plugin?>> + +[#declare-plugin] +== Declaring plugins + +A class can be declared as a plugin by adding a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Plugin.html[`@Plugin`] annotation and a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Configurable.html[`@Configurable`] annotation. + +`@Plugin::value`:: +Name of the plugin. +If left unspecified, this defaults to the simple class name of the annotated class. +This name should be unique within all plugins sharing the same `@Namepsace` (in this case, the `@Configurable` annotation uses the `Core` namespace). +The plugin name is matched using case-insensitive equality. + +`@Namespace::value`:: +Plugins are grouped together in namespaces where they can be identified by name. +Namespaces are case-sensitive. +Typical plugins use the `Core` namespace which is handled by the `@Configurable` annotation. + +`@Configurable::elementType` (deprecated):: +We don't recommend the usage of `elementType` anymore. +Existing usages are kept for backward compatibility reasons with the legacy configuration syntax: `> will determine the effective plugin. +For example, to override the `File` plugin which is provided by the built-in xref:manual/appenders.adoc#FileAppender[File Appender], you would need to place your plugin in a JAR file in the classpath ahead of Log4j Core JAR. +In an OSGi environment, the order that bundles are scanned for plugins generally follows the same order that bundles were installed into the framework; see https://www.osgi.org/javadoc/r5/core/org/osgi/framework/BundleContext.html#getBundles()[`getBundles()`] and https://www.osgi.org/javadoc/r5/core/org/osgi/framework/SynchronousBundleListener.html[`SynchronousBundleListener`]. +In short, name collisions are even more unpredictable in an OSGi environment. +==== + +[#core] +== Declaring plugins represented in a configuration file + +If your plugin needs to be represented by an element in a configuration file (such as an xref:manual/appenders.adoc[appender], xref:manual/layouts.adoc[layout], xref:manual/api.adoc#loggers[logger], or xref:manual/filters.adoc[filter]), following requirements must be met: + +* The `@Namespace` annotation of the `@Plugin`-annotated class must be set to link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Node.html#CORE_NAMESPACE[`Node.CORE_NAMESPACE`] (`Core`) or it must use the link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Configurable.html[`@Configurable`] annotation +* It must have a xref:declare-plugin-factory[plugin factory] + +See {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example and notice these details: + +* There are two plugin declarations: `JsonTemplateLayout` and `JsonTemplateLayout.EventTemplateAdditionalField` +* Both plugin declarations +** Use a `@Configurable` and `@Plugin` annotation +** Provide a `@Factory`-annotated static method + +[#declare-plugin-factory] +=== Declaring plugin factories + +A *plugin factory* is responsible for + +* Creating an instance of the plugin +* Receiving values (`Configuration` instance, configuration attributes, etc.) available in the context + +Plugin factories are comparable to more general dependency injection frameworks and consist of strategies for binding configuration data to a plugin class instance. +A plugin factory typically consists of a static method in the plugin class annotated with a `@FactoryType`-annotated annotation such as link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Factory.html[`@Factory`] or `@PluginFactory`. +Alternatively, a plugin can include a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Inject.html[`@Inject`]-annotated constructor (along with optional `@Inject`-annotated methods or fields). +This plugin factory executable (constructor or method) may include parameters for binding configuration data or injecting other shared instances as described below. + +If a static method returns an instance that implements `java.util.function.Supplier` (which includes both link:../javadoc.adoc/log4j-plugins/org/apache/logging/log4j/plugins/util/Builder.html[`Builder`] and link:../javadoc/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Builder.java[`Builder`]), then this instance is further processed for dependency injection. +This makes it possible to create a builder class with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Inject.html[`@Inject`]-annotated methods whose parameters can be bound to injected data in the same fashion as parameters of the static factory method. + +If a plugin class implements `Collection` or `Map`, then no factory method is used. +Instead, the class is instantiated using the default constructor, and all child configuration nodes are added to the `Collection` or `Map`. + +[#attribute-types] +==== Plugin factory attribute types + +To allow the current `Configuration` to populate the correct arguments for the `@Factory`-annotated method or `@Inject`-annotated constructor or method, every argument to the executable must be annotated using one of the following attribute types. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginAliases.html[`@PluginAliases`]:: +Identifies a list of aliases for a `@Plugin`, `@PluginAttribute`, or `@PluginBuilderAttribute`. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginAttribute.html[`@PluginAttribute`]:: +Denotes a configuration element attribute. +The parameter must be convertible from a `String` using a `TypeConverter`. +Most built-in types are already supported, but custom `TypeConverter` plugins may also be provided for more type support. +Note that `PluginBuilderAttribute` can be used in builder class fields as an easier way to provide default values. + +link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.html[`@PluginConfiguration` (optional)]:: +The current `Configuration` object will be passed to the plugin as a parameter. +Note that this annotation is _optional_ as long as the type of the parameter or field is `Configuration`. + +[[PluginElement]] link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginElement.html[`@PluginElement`]:: +The parameter may represent a complex object that itself has parameters that can be configured. +This also supports injecting an array of elements. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginNode.html[`@PluginNode`]:: +The current `Node` being parsed will be passed to the plugin as a parameter. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/PluginValue.html[`@PluginValue`]:: +The value of the current `Node` or its attribute named `value`. +If `@PluginValue::value` is overridden, then the value from the annotation is used as the attribute name to look up a value for. + +Each attribute or element annotation must include the name that must be present in the configuration in order to match the configuration item to its respective parameter if the parameter name (or field name) does not match the expected name. + +[#type-converters] +==== Plugin factory attribute type converters + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/convert/TypeConverter.html[`TypeConverter`]s are a certain group of plugins for converting ``String``s read from configuration file elements into the types used in plugin factory attributes. +Other plugins can already be injected via <>; now, any type supported by ``TypeConverter``s can be used in a `@PluginAttribute`-annotated factory attribute. + +Conversion of enum types are supported on demand and do not require custom ``TypeConverter``s. +A large number of built-in Java classes (`int`, `long`, `BigDecimal`, etc.) are already supported; see link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/CoreTypeConverters.html[`CoreTypeConverters`] for a more exhaustive listing. +Note that type converters for primitive types and their respective boxed types are handled by anonymous type converters in {project-github-url}/log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/convert/TypeConverterFactory.java[`TypeConverterFactory`]. + +You can create custom ``TypeConverter``s as follows: + +* Extend from link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/convert/TypeConverter.html[the `TypeConverter` interface] + +* Add a link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Plugin.html[`@Plugin`] and link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/convert/TypeConverters.html[`@TypeConverters`] annotation to the class (which is an alias for the `TypeConverter` namespace). + +* Have a default constructor + +* Optionally, extend from `Comparable>`, which will be used for determining the order in case of multiple `TypeConverter` candidates for a certain type + +See {project-github-url}/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/convert/CoreTypeConverters.java[`CoreTypeConverters.java`] for example implementations. + +[#constraint-validators] +==== Plugin factory attribute validators + +Plugin factory fields and parameters can be automatically validated at runtime using constraint validators inspired by https://beanvalidation.org[Bean Validation]. +The following annotations are bundled in Log4j, but custom ``ConstraintValidator`` can be created as well. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/validation/constraints/Required.html[`@Required`]:: +This annotation validates that a value is non-empty. +This covers a check for null as well as several other scenarios: empty `CharSequence` objects, empty arrays, empty `Collection` instances, and empty `Map` instances. + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/validation/constraints/RequiredClass.html[`@RequiredClass`]:: +This annotation validates that a class can be loaded by name. +This can be used on a plugin class to avoid loading the plugin unless the required class is present. + +link:../javadoc/log4j-core/org/apache/logging/log4j/plugins/validation/constraints/ValidHost.html[`@ValidHost`]:: +This annotation validates that a value corresponds to a valid host name. +This uses the same validation as https://docs.oracle.com/javase/{java-target-version}/docs/api/java/net/InetAddress.html#getByName-java.lang.String-[`InetAddress.getByName(String)`]. + +link:../javadoc/log4j-core/org/apache/logging/log4j/plugins/validation/constraints/ValidPort.html[`@ValidPort`]:: +This annotation validates that a value corresponds to a valid port number between 0 and 65535. + +[#plugin-registry] +== Registering plugins + +Registering plugins are done by placing a *Log4j plugin class* (i.e., a generated `Log4jPlugins.java` extending link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/model/PluginService.html[`PluginService`]) into the classpath. +This class is generated using the link:../javadoc/log4j-plugin-processor/org/apache/logging/log4j/plugin/processor/PluginProcessor.html[`PluginProcessor`] annotation processor at compile-time. +You need to configure your build tool as follows to employ `PluginProcessor` by the Java compiler: + +[tabs] +==== +Maven:: ++ +[source,xml,subs="+attributes"] ---- org.apache.maven.plugins maven-compiler-plugin - 3.1 + ${maven-compiler-plugin.version} - log4j-plugin-processor + generate-log4j-plugin-descriptor compile process-classes only + + + + org.apache.logging.log4j + log4j-plugin-processor + {log4j-core-version} + + - org.apache.logging.log4j.plugin.processor.PluginProcessor + + org.apache.logging.log4j.plugin.processor.PluginProcessor @@ -93,166 +225,33 @@ _maven-compiler-plugin_ (version 2.2 or higher) build plugin: ---- -As the configuration is processed the appropriate plugins will be -automatically configured and initialized. - -Log4j 3 utilizes a few different namespaces of plugins which are described in the following sections. - -[#Core] -== Core - -Core plugins are those that are directly represented by an element in a -configuration file and correspond to `@Configurable` namespaced `@Plugin` classes such as an `Appender`, `Layout`, `Logger` or `Filter`. -Custom plugins that conform to the rules laid out in the next paragraph may simply be referenced in the configuration. - -Core plugins support a few different dependency injection rules for binding configuration node data and other injectable classes into a plugin instance. -Each plugin must declare a static method annotated with `@Factory` or `@PluginFactory` or contain an `@Inject`-annotated constructor or no-args constructor. -These static factory method or `@Inject` constructor parameters are used for initial dependency injection. -When a static factory method returns a type that implements `java.util.function.Supplier` such as through `Builder`, then that supplier instance first has its members injected before invoking `Supplier::get` to obtain the resulting plugin instance. -See xref:manual/dependencyinjection.adoc[Dependency Injection] for further details on how dependency injection works. -Plugins are configured using additional plugin qualifier annotations such as `@PluginAttribute`, `@PluginBuilderAttribute`, `@PluginElement`, `@PluginValue`, etc., as described below. - -There are dozens of plugins in Log4j Core that can be -used as examples for more complex scenarios including hierarchical -builder classes (e.g., see `FileAppender`). See -link:extending.html#Plugin_Builders[Extending Log4j with Plugin -Builders] for more details. - -=== Attribute Types - -`PluginAttribute`:: - The parameter must be convertible from a String using a - link:#TypeConverters[TypeConverter]. Most built-in types are already - supported, but custom `TypeConverter` plugins may also be provided for - more type support. Note that `PluginBuilderAttribute` can be used in - builder class fields as an easier way to provide default values. -`PluginElement`:: - The parameter may represent a complex object that itself has - parameters that can be configured. This also supports injecting an - array of elements. -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginConfiguration.html[`PluginConfiguration`]:: - The current `Configuration` object will be passed to the plugin as a - parameter. -`PluginNode`:: - The current `Node` being parsed will be passed to the plugin as a - parameter. -`PluginValue`:: - The value of the current `Node` or its attribute named `value`. - -=== Constraint Validators - -Plugin factory fields, methods, and parameters can be automatically validated at -runtime using constraint validators inspired by the -http://beanvalidation.org/[Bean Validation spec]. The following -annotations are bundled in Log4j, but custom -`ConstraintValidators` -can be created as well. - -`Required`:: - This annotation validates that a value is non-empty. This covers a - check for `null` as well as several other scenarios: empty - `CharSequence` objects, empty arrays, empty `Collection` instances, - and empty `Map` instances. -`RequiredClass`:: - This annotation validates that a class name can be loaded. This is useful for plugins that should only be loaded when an optional class is present. -`ValidHost`:: - This annotation validates that a value corresponds to a valid - hostname. This uses the same validation as - https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/net/InetAddress.html#getByName(java.lang.String)[`InetAddress::getByName`]. -`ValidPort`:: - This annotation validates that a value corresponds to a valid port - number between 0 and 65535. - -[#Converters] -== Converters - -Converters are used by xref:manual/layouts.adoc#PatternLayout[`PatternLayout`] -to render the elements identified by the conversion pattern. Every -converter must specify its category as "Converter" on the `@Plugin` -annotation, have a static `newInstance` method that accepts an array of -`String` as its only parameter and returns an instance of the -Converter, and must have a `@ConverterKeys` annotation present that -contains the array of converter patterns that will cause the Converter -to be selected. Converters that are meant to handle `LogEvent` must -extend the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/layout/LogEventPatternConverter.html[`LogEventPatternConverter`] -class and must implement a format method that accepts a `LogEvent` and a -`StringBuilder` as arguments. The Converter should append the result of -its operation to the `StringBuilder`. - -A second type of Converter is the FileConverter - which must have -"FileConverter" specified in the category attribute of the `@Plugin` -annotation. While similar to a `LogEventPatternConverter`, instead of a -single format method these Converters will have two variations; one that -takes an `Object` and one that takes an array of `Object` instead of -the `LogEvent`. Both append to the provided `StringBuilder` in the same -fashion as a `LogEventPatternConverter`. These Converters are typically -used by the `RollingFileAppender` to construct the name of the file to -log to. - -If multiple Converters specify the same `ConverterKeys`, then the load -order above determines which one will be used. For example, to override -the `%date` converter which is provided by the built-in -`DatePatternConverter` class, you would need to place your plugin in a -JAR file in the CLASSPATH ahead of `log4j-core.jar`. This is not -recommended; pattern ConverterKeys collisions will cause a warning to be -emitted. Try to use unique ConverterKeys for your custom pattern -converters. - -[#KeyProviders] -== KeyProviders - -Some components within Log4j may provide the ability to perform data -encryption. These components require a secret key to perform the -encryption. Applications may provide the key by creating a class that -implements the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/util/SecretKeyProvider.html[`SecretKeyProvider`] -interface. - -[#Lookups] -== Lookups - -Lookups are perhaps the simplest plugins of all. They must declare their -type as "Lookup" on the plugin annotation and must implement the -link:../javadoc/log4j-core/org/apache/logging/log4j/core/lookup/StrLookup.html[`StrLookup`] -interface. They will have two methods; a `lookup` method that accepts a -`String` key and returns a `String` value and a second `lookup` method that -accepts both a `LogEvent` and a `String` key and returns a `String`. Lookups -may be referenced by specifying $\{name:key} where name is the name -specified in the Plugin annotation and key is the name of the item to -locate. - -[#TypeConverters] -== TypeConverters - -``TypeConverter``s -are a sort of meta-plugin used for converting strings into other types -in a plugin factory method parameter. Other plugins can already be -injected via the `@PluginElement` annotation; now, any type supported by -the type conversion system can be used in a `@PluginAttribute` -parameter. Conversion of enum types are supported on demand and do not -require custom `TypeConverter` classes. A large number of built-in Java -classes are already supported; see -`TypeConverters` -and -link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/convert/CoreConverters.html[`CoreConverters`] -for a more exhaustive listing. - -Unlike other plugins, the plugin name of a `TypeConverter` is purely -cosmetic. Appropriate type converters are looked up via the `Type` -interface rather than via `Class` objects only. Do note that -`TypeConverter` plugins must have a default constructor. - -When multiple converters match for a type, the first will be returned. -If any extends from `Comparable>`, it will be used for determining the order. - -[#DeveloperNotes] -== Developer Notes - -If a plugin class implements -https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/util/Collection.html[`Collection`] -or -https://docs.oracle.com/en/java/javase/{java-target-version}/docs/api/java.base/java/util/Map.html[`Map`], -then no factory method is used. Instead, the class is instantiated using -the default constructor, and all child configuration nodes are added to -the `Collection` or `Map`. +Gradle:: ++ +[source,groovy,subs="+attributes"] +---- +dependencies { + // Process sources using `log4j-plugin-processor` providing `org.apache.logging.log4j.plugin.processor.PluginProcessor` that generates `Log4jPlugins.java` --> + annotationProcessor('org.apache.logging.log4j:log4j-plugin-processor:{log4j-core-version}') +} +---- +==== + +[#plugin-discovery] +== Discovering plugins + +link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/model/PluginRegistry.html[`PluginRegistry`] is responsible for discovering plugins and loading their descriptions. + +* When using a module path, these plugin services must be included in the `module-info.java` file via a `provides org.apache.logging.log4j.plugins.model.PluginService with org.example.plugins.Log4jPlugins` line. + +* When using a classpath, these plugin services must be included in a `META-INF/services/org.apache.logging.log4j.plugins.model.PluginService` file (which is automatically generated by the plugin annotation processor). + +See <> for details. + +[#plugin-load] +== Loading plugins + +It is pretty common that a plugin uses other plugins; appenders accept layouts, some layouts accept key-value pairs, etc. +You can do this as follows: + +* If your plugin has a <<#declare-plugin-factory,plugin factory>> (i.e., it is represented by a configuration file element), you can use <<#PluginElement, the `@PluginElement` annotation>> to receive other plugins. +See `@PluginElement("EventTemplateAdditionalField")` usage in {project-github-url}/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayout.java[`JsonTemplateLayout.java`] for an example. diff --git a/src/site/antora/modules/ROOT/pages/thanks.adoc b/src/site/antora/modules/ROOT/pages/thanks.adoc deleted file mode 100644 index d4d59566397..00000000000 --- a/src/site/antora/modules/ROOT/pages/thanks.adoc +++ /dev/null @@ -1,52 +0,0 @@ -//// - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with - the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -//// -= Project Thanks - -Log4j 2 is a successful project because of the large and diverse -community that contributes to it. - -There are a handful of tools that developers in the community use; some -are open-source and some are commercial. We'd like to extend a special -thanks to these communities/companies: - -|=== -|Who |What - -|image:YourKitLogo.png["YourKit", link="https://www.yourkit.com/features/"] -|YourKit supports the Log4j 2 project with its full-featured Java Profiler. -YourKit, LLC is the creator of innovative and intelligent tools for profiling Java and .NET applications. -See https://www.yourkit.com/features/[YourKit's leading software products]. - -|image:IntelliJ-IDEA-logo.png["IntelliJ IDEA", link="https://www.jetbrains.com/idea/"] -|JetBrains supports the Log4j 2 project with https://www.jetbrains.com/idea/[IntelliJ IDEA]. - -|image:https://www.jclarity.com/wp-content/uploads/2015/02/JClarity_logo_tagline3.png["jClarity", link="https://www.jclarity.com/"] -|jClarity supports the Log4j 2 project with its https://www.jclarity.com/censum/[Censum] GC log parsing tool. - -|image:https://www.apache.org/images/feather-small.gif["Apache Feather Logo", link="https://www.apache.org/foundation/thanks.html"] -|All of the Platinum/Gold/Silver/Bronze sponsors of the ASF. -|=== - -== Individual Donations to the Apache Software Foundation - -The Apache Software Foundation receives many smaller donations from -individuals via our PayPal and Amazon Payments services (details -https://www.apache.org/foundation/contributing.html[here]). - -We would like to thank all of our individual donors for their support of -our work, and for their willingness to contribute with only this as -recognition for their generosity. diff --git a/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc b/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc new file mode 100644 index 00000000000..39392abd951 --- /dev/null +++ b/src/site/antora/modules/ROOT/partials/manual/composite-configuration.adoc @@ -0,0 +1,50 @@ +//// + 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. +//// + +// tag::intro[] +There are occasions where multiple configurations might need to be combined. +For instance, + +* You have a common Log4j Core configuration that should always be present, and an environment-specific one that extends the common one depending on the environment (test, production, etc.) the application is running on. +* You develop a framework, and it contains a predefined Log4j Core configuration. +Yet you want to allow users to extend it whenever necessary. +* You collect Log4j Core configurations from multiple sources. +// end::intro[] + +// tag::how[] +`CompositeConfiguration` merges multiple configurations into a single one using a link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/composite/MergeStrategy.html[`MergeStrategy`], which can be customized using xref:manual/systemproperties.adoc#log4j.configuration.mergeStrategy[the `log4j.configuration.mergeStrategy` configuration property]. +The default merge strategy works as follows: + +* xref:manual/configuration.adoc#global-configuration-attributes[Global configuration attributes] in later configurations replace those in previous configurations. +The only exception is the `monitorInterval` attribute: the lowest positive value from all the configuration files will be used. + +* xref:manual/configuration.adoc#property-substitution[Properties] are aggregated. +Duplicate properties override those in previous configurations. + +* xref:manual/filters.adoc[Filters] are aggregated under xref:manual/filters.adoc#CompositeFilter[`CompositeFilter`], if more than one filter is defined. + +* xref:manual/scripts.adoc[] are aggregated. +Duplicate definitions override those in previous configurations. + +* xref:manual/appenders.adoc[Appenders] are aggregated. +Appenders with the same name are **overridden** by those in later configurations, including all their elements. + +* xref:manual/configuration.adoc#configuring-loggers[Loggers] are aggregated. +Logger attributes are individually merged, and those in later configurations replace duplicates. +Appender references on a logger are aggregated, and those in later configurations replace duplicates. +The strategy merges filters on loggers using the rule above. +// end::how[] diff --git a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc index ce00948b590..a4b99344e09 100644 --- a/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc +++ b/src/site/antora/modules/ROOT/partials/manual/plugin-preliminaries.adoc @@ -16,10 +16,10 @@ //// Log4j plugin system is the de facto extension mechanism embraced by various Log4j components. -Plugins make it possible for extensible components to _receive_ feature implementations without any explicit links in between. +Plugins provide extension points to components, that can be used to implement new features, without modifying the original component. It is analogous to a https://en.wikipedia.org/wiki/Dependency_injection[dependency injection] framework, but curated for Log4j-specific needs. -In a nutshell, you annotate your classes with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/Plugin.html[`@Plugin`] and their (`static`) factory methods with link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/PluginFactory.html[`@PluginFactory`]. +In a nutshell, you annotate your classes with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Plugin.html[`@Plugin`] and their (`static`) factory methods with link:../javadoc/log4j-plugins/org/apache/logging/log4j/plugins/Factory.html[`@Factory`]. Last, you inform the Log4j plugin system to discover these custom classes. -This is done using running the link:../javadoc/log4j-core/org/apache/logging/log4j/core/config/plugins/processor/PluginProcessor.html[`PluginProcessor`] annotation processor while building your project. +This is done using running the link:../javadoc/log4j-plugin-processor/org/apache/logging/log4j/plugin/processor/PluginProcessor.html[`PluginProcessor`] annotation processor while building your project. Refer to xref:manual/plugins.adoc[] for details.