diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 0d500de..ac81662 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -28,12 +28,12 @@ steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 # JDK 11 is needed for the build. # Search `maven-toolchains-plugin` usages for details. - name: Set up JDK 11 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 11 @@ -44,7 +44,7 @@ # JDK 8 is needed for the build, and it is the primary bytecode target. # Hence, JDK 8 is set up after 11, so that JAVA_HOME used by Maven during build will point to 8. - name: Set up JDK 8 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 8 @@ -64,7 +64,7 @@ package - name: Upload built sources - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.0.0 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.1.0 with: name: benchmarks.jar path: log4j-perf/target/benchmarks.jar @@ -87,7 +87,7 @@ steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 - name: Download built sources uses: actions/download-artifact@fb598a63ae348fa914e94cd0ff38f362e927b741 # 3.0.0 @@ -96,7 +96,7 @@ path: log4j-perf/target - name: Set up JDK ${{ matrix.jdk }} - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: ${{ matrix.jdk }} @@ -192,12 +192,12 @@ steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 with: ref: gh-pages - name: Setup Python 3 - uses: actions/setup-python@d09bd5e6005b175076f227b13d9730d56e9dcfcb # 4.0.0 + uses: actions/setup-python@b55428b1882923874294fa556849718a1d7f2ca5 # 4.2.0 with: python-version: 3.x diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 573be79..cf320d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,9 @@ build: runs-on: ${{ matrix.os }} - + # Based on: https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources + env: + MAVEN_OPTS: -Xms3072m -Xmx3072m strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] @@ -36,12 +38,12 @@ steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 # JDK 11 is needed for the build. # Search `maven-toolchains-plugin` usages for details. - name: Setup JDK 11 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 11 @@ -52,7 +54,7 @@ # JDK 8 is needed for the build, and it is the primary bytecode target. # Hence, JDK 8 is set up after 11, so that JAVA_HOME used by Maven during build will point to 8. - name: Setup JDK 8 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 8 @@ -102,18 +104,20 @@ deploy: runs-on: ubuntu-latest + env: + MAVEN_OPTS: -Xms3072m -Xmx3072m needs: build if: github.repository == 'apache/logging-log4j2' && github.ref == 'refs/heads/release-2.x' steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 # JDK 11 is needed for the build. # Search `maven-toolchains-plugin` usages for details. - name: Setup JDK 11 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 11 @@ -124,7 +128,7 @@ # JDK 8 is needed for the build, and it is the primary bytecode target. # Hence, JDK 8 is set up after 11, so that JAVA_HOME used by Maven during build will point to 8. - name: Setup JDK 8 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 8 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d582015..ba96611 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,11 +45,11 @@ steps: - name: Checkout repository - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@b398f525a5587552e573b247ac661067fafa920b # 2.1.22 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -60,7 +60,7 @@ # JDK 11 is needed for the build. # Search `maven-toolchains-plugin` usages for details. - name: Setup JDK 11 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 11 @@ -71,7 +71,7 @@ # JDK 8 is needed for the build, and it is the primary bytecode target. # Hence, JDK 8 is set up after 11, so that JAVA_HOME used by Maven during build will point to 8. - name: Setup JDK 8 - uses: actions/setup-java@860f60056505705214d223b91ed7a30f173f6142 # 3.1.1 + uses: actions/setup-java@2c7a4878f5d120bd643426d54ae1209b29cc01a3 # 3.4.1 with: distribution: temurin java-version: 8 @@ -89,4 +89,4 @@ --global-toolchains ".github/workflows/maven-toolchains.xml" - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@935969c6f771d9f0a35efa2ae9cf7c10d9886ca3 # 2.8.5 + uses: github/codeql-action/analyze@b398f525a5587552e573b247ac661067fafa920b # 2.1.22 diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml index f1a5cc1..0c15cd2 100644 --- a/.github/workflows/scorecards-analysis.yml +++ b/.github/workflows/scorecards-analysis.yml @@ -38,12 +38,12 @@ steps: - name: "Checkout code" - uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.1 + uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # 3.0.2 with: persist-credentials: false - name: "Run analysis" - uses: ossf/scorecard-action@3e15ea8318eee9b333819ec77a36aca8d39df13e # 1.1.1 + uses: ossf/scorecard-action@ce330fde6b1a5c9c75b417e7efc510b822a35564 # 1.1.2 with: results_file: results.sarif results_format: sarif @@ -55,13 +55,13 @@ publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.0.0 + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # 3.1.0 with: name: SARIF file path: results.sarif retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@935969c6f771d9f0a35efa2ae9cf7c10d9886ca3 # 2.8.5 + uses: github/codeql-action/upload-sarif@b398f525a5587552e573b247ac661067fafa920b # 2.1.22 with: sarif_file: results.sarif diff --git a/BUILDING.md b/BUILDING.md index 1dd2061..0ce6664 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> + # Requirements * JDK 8 and 9+ @@ -23,23 +24,36 @@ # Configuring Maven Toolchains -Maven Toolchains is used to employ both JDKs during compilation. +Maven Toolchains is used to employ multiple JDKs required for compilation. You either need to have a user-level configuration in `~/.m2/toolchains.xml` or explicitly provide one to the Maven: `./mvnw --global-toolchains /path/to/toolchains.xml`. -See `.github/workflows/maven-toolchains.xml` used by CI for a sample Maven Toolchains configuration. +See [`.github/workflows/maven-toolchains.xml`](.github/workflows/maven-toolchains.xml) used by CI for a sample Maven Toolchains configuration. Note that this file requires `JAVA_HOME_8_X64` and `JAVA_HOME_11_X64` environment variables to be defined, though these can very well be hardcoded. + # Building the sources You can build and verify the sources as follows: ./mvnw verify -To speed up build, you can skip verification and increase concurrency: +`verify` goal runs validation and test steps next to building (i.e., compiling) the sources. +To speed up the build, you can skip verification: - ./mvwn -DskipTests -T8C package + ./mvnw -DskipTests package -If you want to install generated artifacts to your local Maven repository, replace above `veriy` and/or `package` goals with `install`. +If you want to install generated artifacts to your local Maven repository, replace above `verify` and/or `package` goals with `install`. + +## DNS lookups in tests + +Note that if your `/etc/hosts` file does not include an entry for your computer's hostname, then +many unit tests may execute slow due to DNS lookups to translate your hostname to an IP address in +[`InetAddress.getLocalHost()`](http://docs.oracle.com/javase/7/docs/api/java/net/InetAddress.html#getLocalHost()). +To remedy this, you can execute the following: + + printf '127.0.0.1 %s\n::1 %s\n' `hostname` `hostname` | sudo tee -a /etc/hosts + + # Building the website and manual You can build the website and manual as follows: diff --git a/README.md b/README.md index 6a39d2c..6fdaa2c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,20 @@ + + # [Apache Log4j 2](https://logging.apache.org/log4j/2.x/) Apache Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, @@ -17,7 +34,7 @@ ## Usage Users should refer to [Maven, Ivy, Gradle, and SBT Artifacts](http://logging.apache.org/log4j/2.x/maven-artifacts.html) -on the Log4j web site for instructions on how to include Log4j into their project using their chosen build tool. +on the Log4j website for instructions on how to include Log4j into their project using their chosen build tool. Basic usage of the `Logger` API: @@ -94,13 +111,9 @@ ## Building From Source -Log4j requires Apache Maven 3.x. To build from source and install to your local Maven repository, execute the following: - -```sh -mvn install -``` +See [the detailed build instructions](BUILDING.md) on how to build to the project and website from sources. ## Contributing -We love contributions! Take a look at -[our contributing page](https://github.com/apache/logging-log4j2/blob/master/CONTRIBUTING.md). +We love contributions! +Take a look at [our contributing page](CONTRIBUTING.md). diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 25511eb..927c555 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -14,9 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> -# Apache Log4j 2.18.0 Release Notes +# Apache Log4j 2.19.0 Release Notes -The Apache Log4j 2 team is pleased to announce the Log4j 2.18.0 release! +The Apache Log4j 2 team is pleased to announce the Log4j 2.19.0 release! Apache Log4j is a well known framework for logging application behavior. Log4j 2 is an upgrade to Log4j that provides significant improvements over its predecessor, Log4j 1.x, and provides @@ -30,117 +30,67 @@ This release primarily contains bug fixes and minor enhancements. Due to a break in compatibility in the SLF4J binding, Log4j now ships with two versions of the SLF4J to Log4j adapters. -log4j-slf4j-impl should be used with SLF4J 1.7.x and earlier and log4j-slf4j18-impl should be used with SLF4J 1.8.x and -later. SLF4J-2.0.0 alpha releases are not fully supported. See https://issues.apache.org/jira/browse/LOG4J2-2975 and -https://jira.qos.ch/browse/SLF4J-511. +log4j-slf4j-impl should be used with SLF4J 1.7.x and earlier and log4j-slf4j2-impl should be used with SLF4J 2.x and +later. SLF4J-1.8.x is no longer supported as a GA release never occurred. -The Log4j 2.18.0 API, as well as many core components, maintains binary compatibility with previous releases. +The Log4j 2.19.0 API, as well as many core components, maintains binary compatibility with previous releases. -## GA Release 2.18.0 +## GA Release 2.19.0 Changes in this version include: ### New Features -* [LOG4J2-3495](https://issues.apache.org/jira/browse/LOG4J2-3495): -Add MutableThreadContextMapFilter. -* [LOG4J2-3472](https://issues.apache.org/jira/browse/LOG4J2-3472): -Add support for custom LMAX disruptor WaitStrategy configuration. -* [LOG4J2-3419](https://issues.apache.org/jira/browse/LOG4J2-3419): -Add support for custom Log4j 1.x levels. -* [LOG4J2-3440](https://issues.apache.org/jira/browse/LOG4J2-3440): -Add support for adding and retrieving appenders in Log4j 1.x bridge. -* [LOG4J2-3362](https://issues.apache.org/jira/browse/LOG4J2-3362): -Add support for Jakarta Mail API in the SMTP appender. -* [LOG4J2-3483](https://issues.apache.org/jira/browse/LOG4J2-3483): -Add support for Apache Extras' RollingFileAppender in Log4j 1.x bridge. -* [LOG4J2-3538](https://issues.apache.org/jira/browse/LOG4J2-3538): -Add support for 24 colors in highlighting Thanks to Pavel_K. +* [LOG4J2-3583](https://issues.apache.org/jira/browse/LOG4J2-3583): +Add support for SLF4J2 stack-valued MDC. Thanks to Pierrick Terrettaz. +* [LOG4J2-2975](https://issues.apache.org/jira/browse/LOG4J2-2975): +Add implementation of SLF4J2 fluent API. Thanks to Daniel Gray. ### Fixed Bugs -* [LOG4J2-3339](https://issues.apache.org/jira/browse/LOG4J2-3339): -DirectWriteRolloverStrategy should use the current time when creating files. -* [LOG4J2-3534](https://issues.apache.org/jira/browse/LOG4J2-3534): -Fix LevelRangeFilterBuilder to align with log4j1's behavior. -* [LOG4J2-3527](https://issues.apache.org/jira/browse/LOG4J2-3527): -Don't use Paths.get() to avoid circular file systems. -* [LOG4J2-3490](https://issues.apache.org/jira/browse/LOG4J2-3490): -The DirectWriteRolloverStrategy was not detecting the correct index to use during startup. -* [LOG4J2-3432](https://issues.apache.org/jira/browse/LOG4J2-3432): -SizeBasedTriggeringPolicy would fail to rename files properly when integer pattern contained a leading zero. -* [LOG4J2-3491](https://issues.apache.org/jira/browse/LOG4J2-3491): -Async Loggers were including the location information by default. Thanks to Avihai Marchiano. -* [LOG4J2-1376](https://issues.apache.org/jira/browse/LOG4J2-1376): -Allow enterprise id to be an OID fragment. -* [LOG4J2-3493](https://issues.apache.org/jira/browse/LOG4J2-3493): -ClassArbiter's newBuilder method referenced the wrong class. Thanks to Dmytro Voloshyn. -* [LOG4J2-3481](https://issues.apache.org/jira/browse/LOG4J2-3481): -HttpWatcher did not pass credentials when polling. -* [LOG4J2-3482](https://issues.apache.org/jira/browse/LOG4J2-3482): -UrlConnectionFactory.createConnection now accepts an AuthorizationProvider as a parameter. -* [LOG4J2-3477](https://issues.apache.org/jira/browse/LOG4J2-3477): -Add the missing context stack to JsonLayout template. Thanks to filipc. -* [LOG4J2-3393](https://issues.apache.org/jira/browse/LOG4J2-3393): -Improve JsonTemplateLayout performance. -* [LOG4J2-3424](https://issues.apache.org/jira/browse/LOG4J2-3424): -Properties defined in configuration using a value attribute (as opposed to element) are read correctly. -* [LOG4J2-3413](https://issues.apache.org/jira/browse/LOG4J2-3413): -Fix resolution of non-Log4j properties. -* [LOG4J2-3423](https://issues.apache.org/jira/browse/LOG4J2-3423): -JAR file containing Log4j configuration isn't closed. Thanks to Radim Tlusty. -* [LOG4J2-3425](https://issues.apache.org/jira/browse/LOG4J2-3425): -Syslog appender lacks the SocketOptions setting. Thanks to Jiří Smolík. -* [](https://issues.apache.org/jira/browse/LOG4J2-3425): -Improve validation and reporting of configuration errors. -* [](https://issues.apache.org/jira/browse/LOG4J2-3425): -Log4j 1.2 bridge should generate Log4j 2.x messages based on the parameter runtime type. -* [LOG4J2-3426](https://issues.apache.org/jira/browse/LOG4J2-3426): -Log4j 1.2 bridge should not wrap components unnecessarily. Thanks to Pooja Pandey. -* [LOG4J2-3418](https://issues.apache.org/jira/browse/LOG4J2-3418): -Fixes Spring Boot logging system registration in a multi-application environment. -* [LOG4J2-3040](https://issues.apache.org/jira/browse/LOG4J2-3040): -Avoid ClassCastException in JeroMqManager with custom LoggerContextFactory #791. Thanks to LF-Lin. -* [](https://issues.apache.org/jira/browse/LOG4J2-3040): -Fix minor typo #792. Thanks to LF-Lin. -* [LOG4J2-3439](https://issues.apache.org/jira/browse/LOG4J2-3439): -Fixes default SslConfiguration, when a custom keystore is used. Thanks to Jayesh Netravali. -* [LOG4J2-3447](https://issues.apache.org/jira/browse/LOG4J2-3447): -Fixes appender concurrency problems in Log4j 1.x bridge. Thanks to Pooja Pandey. -* [LOG4J2-3452](https://issues.apache.org/jira/browse/LOG4J2-3452): -Fix and test for race condition in FileUtils.mkdir(). Thanks to Stefan Vodita. -* [LOG4J2-3458](https://issues.apache.org/jira/browse/LOG4J2-3458): -LocalizedMessage logs misleading errors on the console. -* [LOG4J2-3359](https://issues.apache.org/jira/browse/LOG4J2-3359): -Fixes the syslog appender in Log4j 1.x bridge, when used with a custom layout. Thanks to Tukesh. -* [LOG4J2-3359](https://issues.apache.org/jira/browse/LOG4J2-3359): -log4j-1.2-api 2.17.2 throws NullPointerException while removing appender with name as null. Thanks to Rajesh. -* [LOG4J2-2872](https://issues.apache.org/jira/browse/LOG4J2-2872): -Fix problem with non-uppercase custom levels. Thanks to Alla Gofman. -* [LOG4J2-3475](https://issues.apache.org/jira/browse/LOG4J2-3475): -Add missing message parameterization in RegexFilter. Thanks to Jeremy Lin. -* [LOG4J2-3428](https://issues.apache.org/jira/browse/LOG4J2-3428): -Update 3rd party dependencies for 2.18.0. -* [LOG4J2-3531](https://issues.apache.org/jira/browse/LOG4J2-3531): -Fix parsing error, when XInclude is disabled. Thanks to Simo Nikula. -* [LOG4J2-3537](https://issues.apache.org/jira/browse/LOG4J2-3537): -Fixes problem with wrong ANSI escape code for bright colors Thanks to Pavel_K. +* [LOG4J2-3578](https://issues.apache.org/jira/browse/LOG4J2-3578): +Generate new SSL certs for testing. +* [LOG4J2-3556](https://issues.apache.org/jira/browse/LOG4J2-3556): +Make JsonTemplateLayout stack trace truncation operate for each label block. Thanks to Arthur Gavlyukovskiy. +* [LOG4J2-3550](https://issues.apache.org/jira/browse/LOG4J2-3550): +SystemPropertyArbiter was assigning the value as the name. Thanks to DongjianPeng. +* [LOG4J2-3560](https://issues.apache.org/jira/browse/LOG4J2-3560): +Logger$PrivateConfig.filter(Level, Marker, String) was allocating empty varargs array. Thanks to David Schlosnagle. +* [LOG4J2-3561](https://issues.apache.org/jira/browse/LOG4J2-3561): +Allows a space separated list of style specifiers in the %style pattern for consistency with %highlight. Thanks to Robert Papp. +* [LOG4J2-3564](https://issues.apache.org/jira/browse/LOG4J2-3564): +Fix NPE in `log4j-to-jul` in the case the root logger level is null. +* [LOG4J2-3545](https://issues.apache.org/jira/browse/LOG4J2-3545): +Add correct manifest entries for OSGi to log4j-jcl Thanks to Johan Compagner. +* [LOG4J2-3565](https://issues.apache.org/jira/browse/LOG4J2-3565): +Fix RollingRandomAccessFileAppender with DirectWriteRolloverStrategy can't create the first log file of different directory. +* [LOG4J2-3579](https://issues.apache.org/jira/browse/LOG4J2-3579): +Fix ServiceLoaderUtil behavior in the presence of a SecurityManager. Thanks to Boris Unckel. +* [LOG4J2-3559](https://issues.apache.org/jira/browse/LOG4J2-3559): +Fix resolution of properties not starting with `log4j2.`. Thanks to Gary Gregory. +* [LOG4J2-3557](https://issues.apache.org/jira/browse/LOG4J2-3557): +Fix recursion between Log4j 1.2 LogManager and Category. Thanks to Andreas Leitgeb. +* [LOG4J2-3587](https://issues.apache.org/jira/browse/LOG4J2-3587): +Fix regression in Rfc5424Layout default values. Thanks to Tomas Micko. +* [LOG4J2-3548](https://issues.apache.org/jira/browse/LOG4J2-3548): +Improve support for passwordless keystores. Thanks to Kristof Farkas-Pall. +* [LOG4J2-708](https://issues.apache.org/jira/browse/LOG4J2-708): +Add async support to `Log4jServletFilter`. ### Changes -* [LOG4J2-3536](https://issues.apache.org/jira/browse/LOG4J2-3536): -Upgrade the Flume Appender to Flume 1.10.0 -* [LOG4J2-3516](https://issues.apache.org/jira/browse/LOG4J2-3516): -Move perf tests to log4j-core-its -* [LOG4J2-3506](https://issues.apache.org/jira/browse/LOG4J2-3506): -Support Spring 2.6.x. -* [LOG4J2-3473](https://issues.apache.org/jira/browse/LOG4J2-3473): -Make the default disruptor WaitStrategy used by Async Loggers garbage-free. -* [LOG4J2-3476](https://issues.apache.org/jira/browse/LOG4J2-3476): -Do not throw UnsupportedOperationException when JUL ApiLogger::setLevel is called. -* [LOG4J2-3427](https://issues.apache.org/jira/browse/LOG4J2-3427): -Improves ServiceLoader support on servlet containers. +* [LOG4J2-3572](https://issues.apache.org/jira/browse/LOG4J2-3572): +Add getExplicitLevel method to LoggerConfig. +* [LOG4J2-3589](https://issues.apache.org/jira/browse/LOG4J2-3589): +Allow Plugins to be injected with the LoggerContext reference. +* [LOG4J2-3588](https://issues.apache.org/jira/browse/LOG4J2-3588): +Allow PropertySources to be added. +### Removed +* [LOG4J2-3573](https://issues.apache.org/jira/browse/LOG4J2-3573): +Removed build page in favor of a single build instructions file. Thanks to Wolff Bock von Wuelfingen. +* [LOG4J2-3590](https://issues.apache.org/jira/browse/LOG4J2-3590): +Remove SLF4J 1.8.x binding. --- -Apache Log4j 2.18.0 requires a minimum of Java 8 to build and run. +Apache Log4j 2.19.0 requires a minimum of Java 8 to build and run. Log4j 2.12.4 is the last release to support Java 7. Log4j 2.3.2 is the last release to support Java 6. Java 6 and Java 7 are no longer supported by the Log4j team. diff --git a/log4j-1.2-api/pom.xml b/log4j-1.2-api/pom.xml index c21ed9f..ae17b9c 100644 --- a/log4j-1.2-api/pom.xml +++ b/log4j-1.2-api/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-1.2-api jar @@ -48,18 +48,22 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.junit.jupiter junit-jupiter-params + test org.mockito mockito-core + test org.apache.commons diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java index 0ebffc9..1f9931c 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Category.java @@ -37,7 +37,6 @@ import org.apache.log4j.spi.HierarchyEventListener; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.LoggingEvent; -import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.message.LocalizedMessage; import org.apache.logging.log4j.message.MapMessage; import org.apache.logging.log4j.message.Message; @@ -174,13 +173,10 @@ protected Category(final LoggerContext context, final String name) { this.name = name; this.logger = context.getLogger(name); - this.repository = LogManager.getLoggerRepository(); - // this.rendererMap = ((RendererSupport) repository).getRendererMap(); } Category(final org.apache.logging.log4j.Logger logger) { this.logger = logger; - // rendererMap = ((RendererSupport) LogManager.getLoggerRepository()).getRendererMap(); } /** @@ -430,7 +426,7 @@ } /** - * Gets the the {@link LoggerRepository} where this Category instance is attached. + * Gets the {@link LoggerRepository} where this Category instance is attached. * * @deprecated Please use {@link #getLoggerRepository()} instead. * @since 1.1 @@ -453,7 +449,7 @@ } /** - * Gets the the {@link LoggerRepository} where this Category is attached. + * Gets the {@link LoggerRepository} where this Category is attached. * * @since 1.2 */ @@ -475,8 +471,12 @@ return null; } final ConcurrentMap loggers = Hierarchy.getLoggersMap(loggerContext); - final Logger parentLogger = loggers.get(parent.getName()); - return parentLogger == null ? new Category(parent) : parentLogger; + Category parentLogger = loggers.get(parent.getName()); + if (parentLogger == null) { + parentLogger = new Category(parent); + parentLogger.setHierarchy(getLoggerRepository()); + } + return parentLogger; } public final Level getPriority() { diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java index 2fe2d79..203a680 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/Hierarchy.java @@ -99,26 +99,30 @@ return PrivateLogManager.getContext(); } - static Logger getInstance(final LoggerContext context, final String name) { + private Logger getInstance(final LoggerContext context, final String name) { return getInstance(context, name, LOGGER_ADAPTER); } - static Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) { - return getLoggersMap(context).computeIfAbsent(name, k -> factory.makeNewLoggerInstance(name)); - } - - static Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) { - return getLoggersMap(context).computeIfAbsent(name, k -> factory.newLogger(name, context)); + private Logger getInstance(final LoggerContext context, final String name, final LoggerFactory factory) { + return getLoggersMap(context).computeIfAbsent(name, k -> { + final Logger logger = factory.makeNewLoggerInstance(name); + logger.setHierarchy(this); + return logger; + }); + } + + private Logger getInstance(final LoggerContext context, final String name, final PrivateLoggerAdapter factory) { + return getLoggersMap(context).computeIfAbsent(name, k -> { + final Logger logger = factory.newLogger(name, context); + logger.setHierarchy(this); + return logger; + }); } static ConcurrentMap getLoggersMap(final LoggerContext context) { synchronized (CONTEXT_MAP) { return CONTEXT_MAP.computeIfAbsent(context, k -> new ConcurrentHashMap<>()); } - } - - static Logger getRootLogger(final LoggerContext context) { - return getInstance(context, org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME); } private final LoggerFactory defaultFactory; @@ -527,85 +531,4 @@ } } - /** - * We update the links for all the children that placed themselves in the provision node 'pn'. The second argument 'cat' - * is a reference for the newly created Logger, parent of all the children in 'pn' - * - * We loop on all the children 'c' in 'pn': - * - * If the child 'c' has been already linked to a child of 'cat' then there is no need to update 'c'. - * - * Otherwise, we set cat's parent field to c's parent and set c's parent field to cat. - * - */ - final private void updateChildren(final ProvisionNode pn, final Logger logger) { - // System.out.println("updateChildren called for " + logger.name); - final int last = pn.size(); - - for (int i = 0; i < last; i++) { - final Logger l = (Logger) pn.elementAt(i); - // System.out.println("Updating child " +p.name); - - // Unless this child already points to a correct (lower) parent, - // make cat.parent point to l.parent and l.parent to cat. - if (!l.parent.name.startsWith(logger.name)) { - logger.parent = l.parent; - l.parent = logger; - } - } - } - - /** - * This method loops through all the *potential* parents of 'cat'. There 3 possible cases: - * - * 1) No entry for the potential parent of 'cat' exists - * - * We create a ProvisionNode for this potential parent and insert 'cat' in that provision node. - * - * 2) There entry is of type Logger for the potential parent. - * - * The entry is 'cat's nearest existing parent. We update cat's parent field with this entry. We also break from the - * loop because updating our parent's parent is our parent's responsibility. - * - * 3) There entry is of type ProvisionNode for this potential parent. - * - * We add 'cat' to the list of children for this potential parent. - */ - final private void updateParents(final Logger cat) { - final String name = cat.name; - final int length = name.length(); - boolean parentFound = false; - - // System.out.println("UpdateParents called for " + name); - - // if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z" - for (int i = name.lastIndexOf('.', length - 1); i >= 0; i = name.lastIndexOf('.', i - 1)) { - final String substr = name.substring(0, i); - - // System.out.println("Updating parent : " + substr); - final CategoryKey key = new CategoryKey(substr); // simple constructor - final Object o = ht.get(key); - // Create a provision node for a future parent. - if (o == null) { - // System.out.println("No parent "+substr+" found. Creating ProvisionNode."); - final ProvisionNode pn = new ProvisionNode(cat); - ht.put(key, pn); - } else if (o instanceof Category) { - parentFound = true; - cat.parent = (Category) o; - // System.out.println("Linking " + cat.name + " -> " + ((Category) o).name); - break; // no need to update the ancestors of the closest ancestor - } else if (o instanceof ProvisionNode) { - ((ProvisionNode) o).addElement(cat); - } else { - final Exception e = new IllegalStateException("unexpected object type " + o.getClass() + " in ht."); - e.printStackTrace(); - } - } - // If we could not find any existing parents, then link with root. - if (!parentFound) { - cat.parent = root; - } - } - } diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java index cde42c7..9e10816 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/helpers/OptionConverter.java @@ -557,7 +557,7 @@ *

* *

- * Case of value is insignificant for the level level, but is + * Case of value is insignificant for the level, but is * significant for the class name part, if present. *

* diff --git a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java index bdc2aa9..261c5bb 100644 --- a/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java +++ b/log4j-1.2-api/src/main/java/org/apache/log4j/spi/AppenderAttachable.java @@ -48,7 +48,7 @@ /** * Returns true if the specified appender is in list of - * attached attached, false otherwise. + * attached, false otherwise. * @param appender The Appender to check. * @return true if the Appender is attached. * diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java index a1b8334..2ed9b2f 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/CategoryTest.java @@ -94,6 +94,7 @@ public void testForcedLog() { final MockCategory category = new MockCategory("org.example.foo"); category.setAdditivity(false); + category.setHierarchy(LogManager.getHierarchy()); ((org.apache.logging.log4j.core.Logger) category.getLogger()).addAppender(appender); // Logging a String category.info("Hello, World"); diff --git a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java index b0503b7..d90438c 100644 --- a/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java +++ b/log4j-1.2-api/src/test/java/org/apache/log4j/xml/XLevel.java @@ -20,7 +20,7 @@ import org.apache.log4j.Level; /** - * This class introduces a new level level called TRACE. TRACE has lower level than DEBUG. + * This class introduces a new level called TRACE. TRACE has lower level than DEBUG. */ public class XLevel extends Level { private static final long serialVersionUID = 7288304330257085144L; diff --git a/log4j-api/pom.xml b/log4j-api/pom.xml index a4c917f..255d3ea 100644 --- a/log4j-api/pom.xml +++ b/log4j-api/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-api jar @@ -47,26 +47,32 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-migrationsupport + test org.junit.jupiter junit-jupiter-params + test org.junit.jupiter junit-jupiter-engine + test uk.org.webcompere system-stubs-jupiter + test org.assertj assertj-core + test org.eclipse.tycho @@ -98,6 +104,7 @@ org.junit-pioneer junit-pioneer + test diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java b/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java new file mode 100644 index 0000000..fc3b8fd --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/BridgeAware.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j; + +/** + * Extended interface to allow bridges between logging systems to convey the + * correct location information. + * + */ +public interface BridgeAware { + + /** + * Fully qualified class name of the entry point to the logging system. This + * class will not appear in the location information. + * + * @param fqcn + * @return this + */ + public void setEntryPoint(final String fqcn); +} diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java b/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java index 63b77a1..11fb11b 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/internal/DefaultLogBuilder.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.internal; +import org.apache.logging.log4j.BridgeAware; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogBuilder; import org.apache.logging.log4j.Logger; @@ -31,7 +32,7 @@ /** * Collects data for a log event and then logs it. This class should be considered private. */ -public class DefaultLogBuilder implements LogBuilder { +public class DefaultLogBuilder implements BridgeAware, LogBuilder { private static Message EMPTY_MESSAGE = new SimpleMessage(""); private static final String FQCN = DefaultLogBuilder.class.getName(); @@ -44,6 +45,7 @@ private StackTraceElement location; private volatile boolean inUse; private long threadId; + private String fqcn = FQCN; public DefaultLogBuilder(Logger logger, Level level) { this.logger = logger; @@ -56,6 +58,11 @@ this.logger = logger; this.inUse = false; this.threadId = Thread.currentThread().getId(); + } + + @Override + public void setEntryPoint(String fqcn) { + this.fqcn = fqcn; } /** @@ -231,7 +238,7 @@ private void logMessage(Message message) { try { - logger.logMessage(level, marker, FQCN, location, message, throwable); + logger.logMessage(level, marker, fqcn, location, message, throwable); } finally { inUse = false; } diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java index d1b3329..70f025d 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/EnvironmentPropertySource.java @@ -64,13 +64,15 @@ @Override public CharSequence getNormalForm(final Iterable tokens) { final StringBuilder sb = new StringBuilder("LOG4J"); + boolean empty = true; for (final CharSequence token : tokens) { + empty = false; sb.append('_'); for (int i = 0; i < token.length(); i++) { sb.append(Character.toUpperCase(token.charAt(i))); } } - return sb.toString(); + return empty ? null : sb.toString(); } @Override diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java index 7d732a2..bb96fc4 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesPropertySource.java @@ -58,7 +58,8 @@ @Override public CharSequence getNormalForm(final Iterable tokens) { - return PREFIX + Util.joinAsCamelCase(tokens); + final CharSequence camelCase = Util.joinAsCamelCase(tokens); + return camelCase.length() > 0 ? PREFIX + camelCase : null; } @Override diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java index 668d702..47fcb71 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/PropertiesUtil.java @@ -24,7 +24,6 @@ import java.time.temporal.ChronoUnit; import java.time.temporal.TemporalUnit; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -35,6 +34,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListSet; /** * Consider this class private. @@ -123,6 +123,16 @@ } /** + * Allows a PropertySource to be added after PropertiesUtil has been created. + * @param propertySource the PropertySource to add. + */ + public void addPropertySource(PropertySource propertySource) { + if (environment != null) { + environment.addPropertySource(propertySource); + } + } + + /** * Returns {@code true} if the specified property is defined, regardless of its value (it may not have a value). * * @param name the name of the property to verify @@ -433,7 +443,7 @@ */ private static class Environment { - private final Set sources = new TreeSet<>(new PropertySource.Comparator()); + private final Set sources = new ConcurrentSkipListSet<>(new PropertySource.Comparator()); /** * Maps a key to its value in the lowest priority source that contains it. */ @@ -462,6 +472,14 @@ .forEach(sources::add); reload(); + } + + /** + * Allow a PropertySource to be added. + * @param propertySource The PropertySource to add. + */ + public void addPropertySource(PropertySource propertySource) { + sources.add(propertySource); } private synchronized void reload() { @@ -481,19 +499,19 @@ .filter(Objects::nonNull) .forEach(key -> { final List tokens = PropertySource.Util.tokenize(key); + final boolean hasTokens = !tokens.isEmpty(); sources.forEach(source -> { - final String value = source.getProperty(key); - if (value != null) { + if (source.containsProperty(key)) { + final String value = source.getProperty(key); literal.putIfAbsent(key, value); - if (!tokens.isEmpty()) { + if (hasTokens) { tokenized.putIfAbsent(tokens, value); } } - final CharSequence normalKey = source.getNormalForm(tokens); - if (normalKey != null) { - final String normalValue = source.getProperty(normalKey.toString()); - if (normalValue != null) { - normalized.putIfAbsent(key, normalValue); + if (hasTokens) { + final String normalKey = Objects.toString(source.getNormalForm(tokens), null); + if (normalKey != null && source.containsProperty(normalKey)) { + normalized.putIfAbsent(key, source.getProperty(normalKey)); } } }); @@ -508,18 +526,16 @@ return literal.get(key); } final List tokens = PropertySource.Util.tokenize(key); + final boolean hasTokens = !tokens.isEmpty(); for (final PropertySource source : sources) { - final String normalKey = Objects.toString(source.getNormalForm(tokens), null); - if (normalKey != null && source.containsProperty(normalKey)) { - final String normalValue = source.getProperty(normalKey); - // Caching previously unknown keys breaks many tests which set and unset system properties - // normalized.put(key, normalValue); - return normalValue; + if (hasTokens) { + final String normalKey = Objects.toString(source.getNormalForm(tokens), null); + if (normalKey != null && source.containsProperty(normalKey)) { + return source.getProperty(normalKey); + } } if (source.containsProperty(key)) { - final String value = source.getProperty(key); - // literal.put(key, value); - return value; + return source.getProperty(key); } } return tokenized.get(tokens); diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java new file mode 100644 index 0000000..2e561ca --- /dev/null +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ProviderActivator.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.util; + +import java.util.Hashtable; + +import org.apache.logging.log4j.spi.Provider; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; + +/** + * Utility class to register Log4j2 providers in an OSGI environment. + */ +public abstract class ProviderActivator implements BundleActivator { + + public static final String API_VERSION = "APIVersion"; + + private final Provider provider; + private ServiceRegistration providerRegistration = null; + + protected ProviderActivator(final Provider provider) { + this.provider = provider; + } + + @Override + public void start(final BundleContext context) throws Exception { + final Hashtable props = new Hashtable<>(); + props.put(API_VERSION, provider.getVersions()); + providerRegistration = context.registerService(Provider.class, provider, props); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (providerRegistration != null) { + providerRegistration.unregister(); + } + } + +} diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java index ab2ddd3..0230bf4 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java @@ -17,9 +17,13 @@ package org.apache.logging.log4j.util; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -38,9 +42,6 @@ * This class should be considered internal. */ public final class ServiceLoaderUtil { - - private static final MethodType LOAD_CLASS_CLASSLOADER = MethodType.methodType(ServiceLoader.class, Class.class, - ClassLoader.class); private ServiceLoaderUtil() { } @@ -102,8 +103,28 @@ static Iterable callServiceLoader(Lookup lookup, Class serviceType, ClassLoader classLoader, boolean verbose) { try { - final MethodHandle handle = lookup.findStatic(ServiceLoader.class, "load", LOAD_CLASS_CLASSLOADER); - final ServiceLoader serviceLoader = (ServiceLoader) handle.invokeExact(serviceType, classLoader); + // Creates a lambda in the caller's domain that calls `ServiceLoader` + final MethodHandle loadHandle = lookup.findStatic(ServiceLoader.class, "load", + MethodType.methodType(ServiceLoader.class, Class.class, ClassLoader.class)); + final CallSite callSite = LambdaMetafactory.metafactory(lookup, + "run", + MethodType.methodType(PrivilegedAction.class, Class.class, ClassLoader.class), + MethodType.methodType(Object.class), + loadHandle, + MethodType.methodType(ServiceLoader.class)); + final PrivilegedAction> action = (PrivilegedAction>) callSite + .getTarget()// + .bindTo(serviceType) + .bindTo(classLoader) + .invoke(); + final ServiceLoader serviceLoader; + if (System.getSecurityManager() == null) { + serviceLoader = action.run(); + } else { + final MethodHandle privilegedHandle = lookup.findStatic(AccessController.class, "doPrivileged", + MethodType.methodType(Object.class, PrivilegedAction.class)); + serviceLoader = (ServiceLoader) privilegedHandle.invoke(action); + } return serviceLoader; } catch (Throwable e) { if (verbose) { @@ -111,7 +132,6 @@ } } return Collections.emptyList(); - } private static class ServiceLoaderSpliterator implements Spliterator { diff --git a/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java b/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java index c11b9c9..fa2ce33 100644 --- a/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java +++ b/log4j-api/src/main/java/org/apache/logging/log4j/util/Strings.java @@ -62,7 +62,7 @@ * {@code null}, empty, or all characters are {@link Character#isWhitespace(char)}. * * @param s the String to check, may be {@code null} - * @return {@code true} if the String is {@code null}, empty, or or all characters are {@link Character#isWhitespace(char)} + * @return {@code true} if the String is {@code null}, empty, or all characters are {@link Character#isWhitespace(char)} */ public static boolean isBlank(final String s) { if (s == null || s.isEmpty()) { diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java index 045c595..57b50e7 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/EnvironmentPropertySourceTest.java @@ -35,6 +35,7 @@ {"LOG4J_FOO_BAR_PROPERTY", Arrays.asList("foo", "bar", "property")}, {"LOG4J_EXACT", Collections.singletonList("EXACT")}, {"LOG4J_TEST_PROPERTY_NAME", PropertySource.Util.tokenize("Log4jTestPropertyName")}, + {null, Collections.emptyList()} }; } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java index 4c71bab..5666fa3 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/Log4jCharsetsPropertiesTest.java @@ -42,7 +42,7 @@ String value = resourceBundle.getString(key); assertTrue( Charset.isSupported(value), - String.format("The Charset %s is is not available and is mapped from %s", value, key)); + String.format("The Charset %s is not available and is mapped from %s", value, key)); } } } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java index 41b0287..51365c1 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesPropertySourceTest.java @@ -36,6 +36,7 @@ {"log4j2.fooBarProperty", Arrays.asList("foo", "bar", "property")}, {"log4j2.EXACT", Collections.singletonList("EXACT")}, {"log4j2.testPropertyName", PropertySource.Util.tokenize("Log4jTestPropertyName")}, + {null, Collections.emptyList()} }; } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java index 6cb3831..4133668 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilOrderTest.java @@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.InputStream; import java.util.Properties; import org.junit.jupiter.api.BeforeEach; @@ -54,7 +55,8 @@ @Override public CharSequence getNormalForm(Iterable tokens) { - return "log4j2." + PropertySource.Util.joinAsCamelCase(tokens); + final CharSequence camelCase = PropertySource.Util.joinAsCamelCase(tokens); + return camelCase.length() > 0 ? "log4j2." + camelCase : null; } @Override @@ -82,11 +84,13 @@ @BeforeEach public void setUp() throws Exception { - properties.load(ClassLoader.getSystemResourceAsStream("PropertiesUtilOrderTest.properties")); - } - - @Test - public void normalizedOverrideLegacy() { + try (final InputStream is = ClassLoader.getSystemResourceAsStream("PropertiesUtilOrderTest.properties")) { + properties.load(is); + } + } + + @Test + public void testNormalizedOverrideLegacy() { final PropertiesUtil util = new PropertiesUtil(properties); final String legacy = "props.legacy"; final String normalized = "props.normalized"; @@ -105,7 +109,7 @@ } @Test - public void fallsBackToTokenMatching() { + public void testFallsBackToTokenMatching() { final PropertiesUtil util = new PropertiesUtil(properties); for (int i = 1; i <= 4; i++) { final String key = "log4j2.tokenBasedProperty" + i; @@ -118,7 +122,7 @@ } @Test - public void orderOfNormalizedProperties(EnvironmentVariables env, SystemProperties sysProps) { + public void testOrderOfNormalizedProperties(EnvironmentVariables env, SystemProperties sysProps) { properties.remove("log4j2.normalizedProperty"); properties.remove("LOG4J_normalized.property"); final PropertiesUtil util = new PropertiesUtil(properties); @@ -151,7 +155,7 @@ } @Test - public void highPriorityNonEnumerableSource(SystemProperties sysProps) { + public void testHighPriorityNonEnumerableSource(SystemProperties sysProps) { // In both datasources assertNotNull(properties.getProperty("log4j2.normalizedProperty")); assertNotNull(properties.getProperty("log4j.onlyLegacy")); @@ -188,7 +192,7 @@ * @param sysProps */ @Test - public void nullChecks(SystemProperties sysProps) { + public void testNullChecks(SystemProperties sysProps) { sysProps.set("log4j2.someProperty", "sysProps"); sysProps.set("Log4jLegacyProperty", "sysProps"); final PropertiesUtil util = new PropertiesUtil(new NullPropertySource()); diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java index e54cd49..540a06a 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/PropertiesUtilTest.java @@ -22,6 +22,8 @@ import org.junit.jupiter.api.parallel.ResourceAccessMode; import org.junit.jupiter.api.parallel.ResourceLock; import org.junit.jupiter.api.parallel.Resources; +import org.junitpioneer.jupiter.ReadsSystemProperty; +import org.junitpioneer.jupiter.SetSystemProperty; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -148,4 +150,20 @@ assertEquals(pair[0], util.getStringProperty(pair[1])); } } + + /** + * LOG4J2-3559: the fix for LOG4J2-3413 returns the value of 'log4j2.' for each + * property not starting with 'log4j'. + */ + @Test + @ReadsSystemProperty + public void testLog4jProperty() { + final Properties props = new Properties(); + final String incorrect = "log4j2."; + final String correct = "not.starting.with.log4j"; + props.setProperty(incorrect, incorrect); + props.setProperty(correct, correct); + final PropertiesUtil util = new PropertiesUtil(props); + assertEquals(correct, util.getStringProperty(correct)); + } } diff --git a/log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java b/log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java index bd7932d..eabf5bd 100644 --- a/log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java +++ b/log4j-api/src/test/java/org/apache/logging/log4j/util/SystemPropertiesPropertySourceSecurityManagerIT.java @@ -53,8 +53,9 @@ private static class TestSecurityManager extends SecurityManager { @Override public void checkPermission(final Permission permission) { - if (permission instanceof PropertyPermission) { - throw new SecurityException(); + if (permission instanceof PropertyPermission && + !permission.getName().equals("java.util.secureRandomSeed")) { + throw new SecurityException("Unexpected permission: " + permission); } } } diff --git a/log4j-api-java9/pom.xml b/log4j-api-java9/pom.xml index 96f5e43..aedbda2 100644 --- a/log4j-api-java9/pom.xml +++ b/log4j-api-java9/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-api-java9 pom @@ -35,10 +35,12 @@ org.junit.jupiter junit-jupiter-engine + test org.assertj assertj-core + test diff --git a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java index 9450e53..10b8cd5 100644 --- a/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java +++ b/log4j-api-java9/src/main/java/org/apache/logging/log4j/util/ServiceLoaderUtil.java @@ -17,10 +17,13 @@ package org.apache.logging.log4j.util; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.MethodType; -import java.util.Arrays; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.HashSet; import java.util.Objects; import java.util.ServiceConfigurationError; @@ -31,9 +34,6 @@ import org.apache.logging.log4j.status.StatusLogger; public final class ServiceLoaderUtil { - - private static final MethodType LOAD_CLASS_CLASSLOADER = MethodType.methodType(ServiceLoader.class, Class.class, - ClassLoader.class); private ServiceLoaderUtil() { } @@ -87,8 +87,28 @@ static Stream loadClassloaderServices(final Class serviceType, final Lookup lookup, final ClassLoader classLoader, final boolean verbose) { try { - final MethodHandle handle = lookup.findStatic(ServiceLoader.class, "load", LOAD_CLASS_CLASSLOADER); - final ServiceLoader serviceLoader = (ServiceLoader) handle.invokeExact(serviceType, classLoader); + // Creates a lambda in the caller's domain that calls `ServiceLoader` + final MethodHandle loadHandle = lookup.findStatic(ServiceLoader.class, "load", + MethodType.methodType(ServiceLoader.class, Class.class, ClassLoader.class)); + final CallSite callSite = LambdaMetafactory.metafactory(lookup, + "run", + MethodType.methodType(PrivilegedAction.class, Class.class, ClassLoader.class), + MethodType.methodType(Object.class), + loadHandle, + MethodType.methodType(ServiceLoader.class)); + final PrivilegedAction> action = (PrivilegedAction>) callSite + .getTarget()// + .bindTo(serviceType) + .bindTo(classLoader) + .invoke(); + final ServiceLoader serviceLoader; + if (System.getSecurityManager() == null) { + serviceLoader = action.run(); + } else { + final MethodHandle privilegedHandle = lookup.findStatic(AccessController.class, "doPrivileged", + MethodType.methodType(Object.class, PrivilegedAction.class)); + serviceLoader = (ServiceLoader) privilegedHandle.invoke(action); + } return serviceLoader.stream().map(provider -> { try { return provider.get(); diff --git a/log4j-appserver/pom.xml b/log4j-appserver/pom.xml index 15490ce..c350cca 100644 --- a/log4j-appserver/pom.xml +++ b/log4j-appserver/pom.xml @@ -22,7 +22,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 log4j-appserver @@ -34,7 +34,7 @@ ${basedir}/.. Web Documentation /log4j-appserver - 10.0.21 + 10.0.23 org.apache.logging.log4j.appserver true diff --git a/log4j-bom/pom.xml b/log4j-bom/pom.xml index 4f7a014..07f2650 100644 --- a/log4j-bom/pom.xml +++ b/log4j-bom/pom.xml @@ -26,7 +26,7 @@ Apache Log4j Bill of Materials org.apache.logging.log4j log4j-bom - 2.18.0 + 2.19.0 pom + + org.apache.logging.log4j + log4j-jakarta-smtp + ${project.version} + org.apache.logging.log4j @@ -95,10 +101,10 @@ log4j-slf4j-impl ${project.version} - - - org.apache.logging.log4j - log4j-slf4j18-impl + + + org.apache.logging.log4j + log4j-slf4j2-impl ${project.version} @@ -107,6 +113,12 @@ log4j-to-slf4j ${project.version} + + + org.apache.logging.log4j + log4j-to-jul + ${project.version} + org.apache.logging.log4j @@ -117,6 +129,11 @@ org.apache.logging.log4j log4j-web + ${project.version} + + + org.apache.logging.log4j + log4j-jakarta-web ${project.version} @@ -228,6 +245,6 @@ - log4j-2.18.0-rc1 + log4j-2.19.0-rc2 diff --git a/log4j-cassandra/pom.xml b/log4j-cassandra/pom.xml index 46617aa..353dd32 100644 --- a/log4j-cassandra/pom.xml +++ b/log4j-cassandra/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -51,10 +51,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.mockito diff --git a/log4j-cassandra/src/test/resources/cassandra.yaml b/log4j-cassandra/src/test/resources/cassandra.yaml index 356e43d..aab593b 100644 --- a/log4j-cassandra/src/test/resources/cassandra.yaml +++ b/log4j-cassandra/src/test/resources/cassandra.yaml @@ -380,7 +380,7 @@ # to the number of cores. #memtable_flush_writers: 8 -# A fixed memory pool size in MB for for SSTable index summaries. If left +# A fixed memory pool size in MB for SSTable index summaries. If left # empty, this will default to 5% of the heap size. If the memory usage of # all index summaries exceeds this limit, SSTables with low read rates will # shrink their index summaries in order to meet this limit. However, this @@ -549,7 +549,7 @@ # Uncomment to set socket buffer size for internode communication # Note that when setting this, the buffer size is limited by net.core.wmem_max -# and when not setting it it is defined by net.ipv4.tcp_wmem +# and when not setting it is defined by net.ipv4.tcp_wmem # See: # /proc/sys/net/core/wmem_max # /proc/sys/net/core/rmem_max diff --git a/log4j-core/pom.xml b/log4j-core/pom.xml index d8316e9..b8e46f6 100644 --- a/log4j-core/pom.xml +++ b/log4j-core/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-core jar @@ -172,7 +172,7 @@ org.jmdns jmdns - 3.5.7 + 3.5.8 test @@ -185,14 +185,17 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.junit.jupiter junit-jupiter-params + test org.junit.platform @@ -200,6 +203,11 @@ test + org.assertj + assertj-core + test + + org.hamcrest hamcrest test @@ -207,15 +215,18 @@ org.junit-pioneer junit-pioneer + test org.mockito mockito-core + test org.mockito mockito-junit-jupiter + test diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Filter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/Filter.java index 054d67b..978dba4 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Filter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Filter.java @@ -20,6 +20,7 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.util.Constants; import org.apache.logging.log4j.util.EnglishEnums; /** @@ -301,6 +302,18 @@ /** * Filter an event. + * @param logger The Logger. + * @param level The event logging Level. + * @param marker The Marker for the event or null. + * @param msg The Message + * @return the Result. + */ + default Result filter(Logger logger, Level level, Marker marker, String msg) { + return filter(logger, level, marker, msg, Constants.EMPTY_OBJECT_ARRAY); + } + + /** + * Filter an event. * @param event The Event to filter on. * @return the Result. */ diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java index 6db76b2..a6fbb0a 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/Logger.java @@ -19,6 +19,7 @@ import java.io.ObjectStreamException; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -299,7 +300,7 @@ public Iterator getFilters() { final Filter filter = privateConfig.loggerConfig.getFilter(); if (filter == null) { - return new ArrayList().iterator(); + return Collections.emptyIterator(); } else if (filter instanceof CompositeFilter) { return ((CompositeFilter) filter).iterator(); } else { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java index 533a604..b546172 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/AppenderSet.java @@ -65,7 +65,7 @@ for (final Node childNode : children) { final String key = childNode.getAttributes().get("name"); if (key == null) { - LOGGER.error("The attribute 'name' is missing from from the node {} in AppenderSet {}", + LOGGER.error("The attribute 'name' is missing from the node {} in AppenderSet {}", childNode, children); } else { map.put(key, childNode); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java index e43a58d..992741c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/SyslogAppender.java @@ -57,7 +57,7 @@ private String id; @PluginBuilderAttribute(value = "enterpriseNumber") - private String enterpriseNumber = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER; + private String enterpriseNumber = String.valueOf(Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER); @PluginBuilderAttribute(value = "includeMdc") private boolean includeMdc = true; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java index 30d797e..fa3e976 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java @@ -139,10 +139,7 @@ protected synchronized void writeToDestination(final byte[] bytes, final int offset, final int length) { try { if (randomAccessFile == null) { - final String fileName = getFileName(); - final File file = new File(fileName); - FileUtils.makeParentDirs(file); - createFileAfterRollover(fileName); + createFileAfterRollover(); } randomAccessFile.write(bytes, offset, length); size += length; @@ -154,7 +151,10 @@ @Override protected void createFileAfterRollover() throws IOException { - createFileAfterRollover(getFileName()); + final String fileName = getFileName(); + final File file = new File(fileName); + FileUtils.makeParentDirs(file); + createFileAfterRollover(fileName); } private void createFileAfterRollover(final String fileName) throws IOException { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java index 738bac9..6cfb8c2 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/routing/RoutingAppender.java @@ -422,7 +422,7 @@ * LOG4J2-2629: PurgePolicy implementations can invoke {@link #deleteAppender(String)} after we have looked up * an instance of a target appender but before events are appended, which could result in events not being * recorded to any appender. - * This extension of {@link AppenderControl} allows to to mark usage of an appender, allowing deferral of + * This extension of {@link AppenderControl} allows to mark usage of an appender, allowing deferral of * {@link Appender#stop()} until events have successfully been recorded. * Alternative approaches considered: * - More aggressive synchronization: Appenders may do expensive I/O that shouldn't block routing. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java index 90ce160..32f7ea6 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/async/BasicAsyncLoggerContextSelector.java @@ -29,7 +29,7 @@ /** * Returns either this Thread's context or the default {@link AsyncLoggerContext}. * Single-application instances should prefer this implementation over the {@link AsyncLoggerContextSelector} - * due the the reduced overhead avoiding classloader lookups. + * due to the reduced overhead avoiding classloader lookups. */ public class BasicAsyncLoggerContextSelector implements ContextSelector { diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java index ab51e5d..2fa049b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/AbstractConfiguration.java @@ -132,7 +132,7 @@ private ConcurrentMap loggerConfigs = new ConcurrentHashMap<>(); private List customLevels = Collections.emptyList(); private final ConcurrentMap propertyMap = new ConcurrentHashMap<>(); - private final StrLookup tempLookup = new Interpolator(propertyMap); + private final Interpolator tempLookup = new Interpolator(propertyMap); private final StrSubstitutor runtimeStrSubstitutor = new RuntimeStrSubstitutor(tempLookup); private final StrSubstitutor configurationStrSubstitutor = new ConfigurationStrSubstitutor(runtimeStrSubstitutor); private LoggerConfig root = new LoggerConfig(); @@ -150,6 +150,7 @@ */ protected AbstractConfiguration(final LoggerContext loggerContext, final ConfigurationSource configurationSource) { this.loggerContext = new WeakReference<>(loggerContext); + tempLookup.setLoggerContext(loggerContext); // The loggerContext is null for the NullConfiguration class. // this.loggerContext = new WeakReference(Objects.requireNonNull(loggerContext, "loggerContext is null")); this.configurationSource = Objects.requireNonNull(configurationSource, "configurationSource is null"); @@ -636,6 +637,9 @@ createConfiguration(first, null); if (first.getObject() != null) { StrLookup lookup = (StrLookup) first.getObject(); + if (lookup instanceof LoggerContextAware) { + ((LoggerContextAware) lookup).setLoggerContext(loggerContext.get()); + } runtimeStrSubstitutor.setVariableResolver(lookup); configurationStrSubstitutor.setVariableResolver(lookup); } @@ -643,6 +647,7 @@ final Map map = this.getComponent(CONTEXT_PROPERTIES); final StrLookup lookup = map == null ? null : new PropertiesLookup(map); Interpolator interpolator = new Interpolator(lookup, pluginPackages); + interpolator.setLoggerContext(loggerContext.get()); runtimeStrSubstitutor.setVariableResolver(interpolator); configurationStrSubstitutor.setVariableResolver(interpolator); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java index 5886e67..b9be60b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerConfig.java @@ -410,6 +410,14 @@ } /** + * Allows callers to determine the Level assigned to this LoggerConfig. + * @return the Level associated with this LoggerConfig or null if none is set. + */ + public Level getExplicitLevel() { + return level; + } + + /** * Returns the LogEventFactory. * * @return the LogEventFactory. @@ -942,11 +950,11 @@ } else { LOGGER.warn("levelAndRefs are only allowed in a properties configuration. The value is ignored."); result.level = level; - result.refs = Arrays.asList(refs); + result.refs = refs != null ? Arrays.asList(refs) : new ArrayList<>(); } } else { result.level = level; - result.refs = Arrays.asList(refs); + result.refs = refs != null ? Arrays.asList(refs) : new ArrayList<>(); } return result; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java new file mode 100644 index 0000000..e68af4c --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/LoggerContextAware.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.core.config; + +import org.apache.logging.log4j.core.LoggerContext; + +/** + * Indicates that a class requests the current LoggerContext to be injected. + * + * @since 2.19.0 + */ +public interface LoggerContextAware { + + /** + * Injects the current LoggerContext into this object. + * + * @param loggerContext the current LoggerContext + */ + void setLoggerContext(LoggerContext loggerContext); +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java index 25212a8..82b725b 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/arbiters/SystemPropertyArbiter.java @@ -74,11 +74,11 @@ /** * Sets the Property Value. - * @param propertyValue the property name. + * @param propertyValue the property value. * @return this */ public Builder setPropertyValue(final String propertyValue) { - this.propertyName = propertyValue; + this.propertyValue = propertyValue; return asBuilder(); } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java new file mode 100644 index 0000000..9594ff4 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/PluginLoggerContext.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.config.plugins; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.apache.logging.log4j.core.config.plugins.visitors.PluginLoggerContextVisitor; + +/** + * Identifies a parameter or field as a LoggerContext. + * @see org.apache.logging.log4j.core.LoggerContext + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER, ElementType.FIELD}) +@PluginVisitorStrategy(PluginLoggerContextVisitor.class) +public @interface PluginLoggerContext { + // empty +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginLoggerContextVisitor.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginLoggerContextVisitor.java new file mode 100644 index 0000000..6166831 --- /dev/null +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/config/plugins/visitors/PluginLoggerContextVisitor.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 org.apache.logging.log4j.core.config.plugins.visitors; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.Configuration; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; + +/** + * PluginVisitor implementation for {@link PluginConfiguration}. + */ +public class PluginLoggerContextVisitor extends AbstractPluginVisitor { + public PluginLoggerContextVisitor() { + super(PluginConfiguration.class); + } + + @Override + public Object visit(final Configuration configuration, final Node node, final LogEvent event, + final StringBuilder log) { + if (this.conversionType.isAssignableFrom(LoggerContext.class)) { + if (configuration.getLoggerContext() != null) { + return configuration.getLoggerContext(); + } else { + LOGGER.warn("Configuration {} is not assigned a LoggerContext", configuration.getName()); + } + } + LOGGER.warn("Variable annotated with @PluginLoggerContext does not reference a LoggerContext"); + return null; + } +} diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java index 731643c..5d3b2da 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/layout/Rfc5424Layout.java @@ -73,7 +73,7 @@ /** * The default example enterprise number from RFC5424. */ - public static final String DEFAULT_ENTERPRISE_NUMBER = "32473"; + public static final int DEFAULT_ENTERPRISE_NUMBER = 32473; /** * The default event id. */ @@ -616,7 +616,7 @@ // @formatter:off @PluginAttribute(value = "facility", defaultString = "LOCAL0") final Facility facility, @PluginAttribute("id") final String id, - @PluginAttribute(value = "enterpriseNumber", defaultInt = -1) + @PluginAttribute(value = "enterpriseNumber", defaultInt = DEFAULT_ENTERPRISE_NUMBER) final int enterpriseNumber, @PluginAttribute(value = "includeMDC", defaultBoolean = true) final boolean includeMDC, @PluginAttribute(value = "mdcId", defaultString = DEFAULT_MDCID) final String mdcId, @@ -647,13 +647,13 @@ public static class Rfc5424LayoutBuilder { private Configuration config; - private Facility facility; + private Facility facility = Facility.LOCAL0; private String id; - private String ein; - private boolean includeMDC; + private String ein = String.valueOf(DEFAULT_ENTERPRISE_NUMBER); + private boolean includeMDC = true; private boolean includeNL; private String escapeNL; - private String mdcId; + private String mdcId = DEFAULT_MDCID; private String mdcPrefix; private String eventPrefix; private String appName; @@ -843,4 +843,21 @@ public Facility getFacility() { return facility; } + + public String getDefaultId() { + return defaultId; + } + + public String getEnterpriseNumber() { + return enterpriseNumber; + } + + public boolean isIncludeMdc() { + return includeMdc; + } + + public String getMdcId() { + return mdcId; + } + } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java index 17a34f1..a5141ec 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/DateLookup.java @@ -48,7 +48,7 @@ } /** - * Looks up the the current date or the date in the LogEvent. + * Looks up d the current date or the date in the LogEvent. * @param event The LogEvent for which the date is returned. If null, current date is returned. * @param key the format to use. If null, the default DateFormat will be used. * @return The formatted date, never null. diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java index 44c72b7..f815836 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java @@ -16,6 +16,7 @@ */ package org.apache.logging.log4j.core.lookup; +import java.lang.ref.WeakReference; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -24,7 +25,9 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.ConfigurationAware; +import org.apache.logging.log4j.core.config.LoggerContextAware; import org.apache.logging.log4j.core.config.plugins.util.PluginManager; import org.apache.logging.log4j.core.config.plugins.util.PluginType; import org.apache.logging.log4j.core.net.JndiManager; @@ -34,7 +37,7 @@ /** * Proxies all the other {@link StrLookup}s. */ -public class Interpolator extends AbstractConfigurationAwareLookup { +public class Interpolator extends AbstractConfigurationAwareLookup implements LoggerContextAware { /** Constant for the prefix separator. */ public static final char PREFIX_SEPARATOR = ':'; @@ -56,6 +59,8 @@ private final Map strLookupMap = new HashMap<>(); private final StrLookup defaultLookup; + + protected WeakReference loggerContext; public Interpolator(final StrLookup defaultLookup) { this(defaultLookup, null); @@ -185,6 +190,9 @@ if (lookup instanceof ConfigurationAware) { ((ConfigurationAware) lookup).setConfiguration(configuration); } + if (lookup instanceof LoggerContextAware) { + ((LoggerContextAware) lookup).setLoggerContext(loggerContext.get()); + } LookupResult value = null; if (lookup != null) { value = event == null ? lookup.evaluate(name) : lookup.evaluate(event, name); @@ -199,6 +207,14 @@ return event == null ? defaultLookup.evaluate(var) : defaultLookup.evaluate(event, var); } return null; + } + + @Override + public void setLoggerContext(LoggerContext loggerContext) { + if (loggerContext == null) { + return; + } + this.loggerContext = new WeakReference<>(loggerContext); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java index c15c4ae..565c21f 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/AbstractKeyStoreConfiguration.java @@ -33,7 +33,6 @@ * Configuration of the KeyStore */ public class AbstractKeyStoreConfiguration extends StoreConfiguration { - static final char[] DEFAULT_PASSWORD = "changeit".toCharArray(); private final KeyStore keyStore; private final String keyStoreType; @@ -66,21 +65,20 @@ @Override protected KeyStore load() throws StoreConfigurationException { final String loadLocation = this.getLocation(); + final char[] password = this.getPasswordAsCharArray(); LOGGER.debug("Loading keystore from location {}", loadLocation); try { + final KeyStore ks = KeyStore.getInstance(this.keyStoreType); if (loadLocation == null) { - throw new IOException("The location is null"); + if (keyStoreType.equalsIgnoreCase(JKS) || keyStoreType.equalsIgnoreCase(PKCS12)) { + throw new IOException("The location is null"); + } + ks.load(null, password); + LOGGER.debug("KeyStore successfully loaded"); + return ks; } try (final InputStream fin = openInputStream(loadLocation)) { - final KeyStore ks = KeyStore.getInstance(this.keyStoreType); - final char[] password = this.getPasswordAsCharArray(); - try { - ks.load(fin, password != null ? password : DEFAULT_PASSWORD); - } finally { - if (password != null) { - Arrays.fill(password, '\0'); - } - } + ks.load(fin, password); LOGGER.debug("KeyStore successfully loaded from location {}", loadLocation); return ks; } @@ -99,6 +97,10 @@ } catch (final IOException e) { LOGGER.error("Something is wrong with the format of the keystore or the given password for location {}", loadLocation, e); throw new StoreConfigurationException(loadLocation, e); + } finally { + if (password != null) { + Arrays.fill(password, '\0'); + } } } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java index bbb325a..edd2554 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfiguration.java @@ -169,7 +169,7 @@ final KeyManagerFactory kmFactory = KeyManagerFactory.getInstance(this.keyManagerFactoryAlgorithm); final char[] password = this.getPasswordAsCharArray(); try { - kmFactory.init(this.getKeyStore(), password != null ? password : DEFAULT_PASSWORD); + kmFactory.init(this.getKeyStore(), password); } finally { if (password != null) { Arrays.fill(password, '\0'); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java index 7a8e5d0..552a269 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactory.java @@ -19,6 +19,7 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.Strings; /** * Creates an SSL configuration from Log4j properties. @@ -46,32 +47,28 @@ KeyStoreConfiguration keyStoreConfiguration = null; TrustStoreConfiguration trustStoreConfiguration = null; String location = props.getStringProperty(trustStorelocation); - if (location != null) { + String storeType = props.getStringProperty(trustStoreKeyStoreType); + if (Strings.isNotEmpty(location) || storeType != null) { String password = props.getStringProperty(trustStorePassword); - char[] passwordChars = null; - if (password != null) { - passwordChars = password.toCharArray(); - } + char[] passwordChars = getPassword(password, storeType); try { - trustStoreConfiguration = TrustStoreConfiguration.createKeyStoreConfiguration(location, passwordChars, + trustStoreConfiguration = TrustStoreConfiguration.createKeyStoreConfiguration(Strings.trimToNull(location), passwordChars, props.getStringProperty(trustStorePasswordEnvVar), props.getStringProperty(trustStorePasswordFile), - props.getStringProperty(trustStoreKeyStoreType), props.getStringProperty(trustStoreKeyManagerFactoryAlgorithm)); + storeType, props.getStringProperty(trustStoreKeyManagerFactoryAlgorithm)); } catch (Exception ex) { LOGGER.warn("Unable to create trust store configuration due to: {} {}", ex.getClass().getName(), ex.getMessage()); } } location = props.getStringProperty(keyStoreLocation); - if (location != null) { + storeType = props.getStringProperty(keyStoreType); + if (Strings.isNotEmpty(location) || storeType != null) { String password = props.getStringProperty(keyStorePassword); - char[] passwordChars = null; - if (password != null) { - passwordChars = password.toCharArray(); - } + char[] passwordChars = getPassword(password, storeType); try { - keyStoreConfiguration = KeyStoreConfiguration.createKeyStoreConfiguration(location, passwordChars, + keyStoreConfiguration = KeyStoreConfiguration.createKeyStoreConfiguration(Strings.trimToNull(location), passwordChars, props.getStringProperty(keyStorePasswordEnvVar), props.getStringProperty(keyStorePasswordFile), - props.getStringProperty(keyStoreType), props.getStringProperty(keyStoreKeyManagerFactoryAlgorithm)); + storeType, props.getStringProperty(keyStoreKeyManagerFactoryAlgorithm)); } catch (Exception ex) { LOGGER.warn("Unable to create key store configuration due to: {} {}", ex.getClass().getName(), ex.getMessage()); @@ -85,6 +82,22 @@ return null; } + private static char[] getPassword(final String password, final String keyStoreType) { + // Note from Tomcat's SSLUtiBase#getStore: + // + // JKS key stores treat null and "" interchangeably. + // PKCS12 key stores don't return the cert if null is used. + // Key stores that do not use passwords expect null + // Therefore: + // - generally use null if pass is null or "" + // - for JKS or PKCS12 only use null if pass is null + // (because JKS will auto-switch to PKCS12) + if (keyStoreType.equals(StoreConfiguration.JKS) || keyStoreType.equals(StoreConfiguration.PKCS12)) { + return password != null ? password.toCharArray() : null; + } + return Strings.isEmpty(password) ? null : password.toCharArray(); + } + public static SslConfiguration getSslConfiguration() { return sslConfiguration; } diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/StoreConfiguration.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/StoreConfiguration.java index 6d4e686..d995bb5 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/StoreConfiguration.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/net/ssl/StoreConfiguration.java @@ -25,6 +25,9 @@ * */ public class StoreConfiguration { + + static final String PKCS12 = "PKCS12"; + static final String JKS = "JKS"; protected static final StatusLogger LOGGER = StatusLogger.getLogger(); private String location; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java index a00e484..9c1288c 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/osgi/Activator.java @@ -18,7 +18,6 @@ package org.apache.logging.log4j.core.osgi; import java.util.Collection; -import java.util.Hashtable; import java.util.concurrent.atomic.AtomicReference; import org.apache.logging.log4j.LogManager; @@ -29,11 +28,10 @@ import org.apache.logging.log4j.core.impl.ThreadContextDataProvider; import org.apache.logging.log4j.core.util.Constants; import org.apache.logging.log4j.core.util.ContextDataProvider; -import org.apache.logging.log4j.spi.Provider; import org.apache.logging.log4j.status.StatusLogger; import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.ProviderActivator; import org.osgi.framework.Bundle; -import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.InvalidSyntaxException; @@ -45,24 +43,23 @@ /** * OSGi BundleActivator. */ -public final class Activator implements BundleActivator, SynchronousBundleListener { +public final class Activator extends ProviderActivator implements SynchronousBundleListener { private static final Logger LOGGER = StatusLogger.getLogger(); private final AtomicReference contextRef = new AtomicReference<>(); - ServiceRegistration provideRegistration = null; - ServiceRegistration contextDataRegistration = null; + private ServiceRegistration contextDataRegistration = null; + + public Activator() { + super(new Log4jProvider()); + } @Override public void start(final BundleContext context) throws Exception { - final Provider provider = new Log4jProvider(); - final Hashtable props = new Hashtable<>(); - props.put("APIVersion", "2.60"); + super.start(context); final ContextDataProvider threadContextProvider = new ThreadContextDataProvider(); - provideRegistration = context.registerService(Provider.class.getName(), provider, props); - contextDataRegistration = context.registerService(ContextDataProvider.class.getName(), threadContextProvider, - null); + contextDataRegistration = context.registerService(ContextDataProvider.class, threadContextProvider, null); loadContextProviders(context); // allow the user to override the default ContextSelector (e.g., by using BasicContextSelector for a global cfg) if (PropertiesUtil.getProperties().getStringProperty(Constants.LOG4J_CONTEXT_SELECTOR) == null) { @@ -114,10 +111,10 @@ @Override public void stop(final BundleContext context) throws Exception { - provideRegistration.unregister(); contextDataRegistration.unregister(); this.contextRef.compareAndSet(context, null); LogManager.shutdown(); + super.stop(context); } @Override diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java index acbf2b6..dfc5675 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/pattern/StyleConverter.java @@ -93,7 +93,7 @@ } final PatternParser parser = PatternLayout.createPatternParser(config); final List formatters = parser.parse(options[0]); - final String style = AnsiEscape.createSequence(options[1].split(Patterns.COMMA_SEPARATOR)); + final String style = AnsiEscape.createSequence(options[1].split(Patterns.COMMA_SPACE_SEPARATOR)); final boolean disableAnsi = Arrays.toString(options).contains(PatternParser.DISABLE_ANSI + "=true"); final boolean noConsoleNoAnsi = Arrays.toString(options).contains(PatternParser.NO_CONSOLE_NO_ANSI + "=true"); final boolean hideAnsi = disableAnsi || (noConsoleNoAnsi && System.console() == null); diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java index 4aaca77..2642981 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Loader.java @@ -80,7 +80,7 @@ * class ({@code Loader}). Under JDK 1.1, only the class * loader that loaded this class ({@code Loader}) is used. *
  • Try one last time with - * {@code ClassLoader.getSystemResource(resource)}, that is is + * {@code ClassLoader.getSystemResource(resource)}, that is * using the system class loader in JDK 1.2 and virtual machine's * built-in class loader in JDK 1.1.
  • * @@ -142,7 +142,7 @@ * class ({@code Loader}). Under JDK 1.1, only the class * loader that loaded this class ({@code Loader}) is used. *
  • Try one last time with - * {@code ClassLoader.getSystemResource(resource)}, that is is + * {@code ClassLoader.getSystemResource(resource)}, that is * using the system class loader in JDK 1.2 and virtual machine's * built-in class loader in JDK 1.1.
  • * diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Patterns.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Patterns.java index 5427197..cd329f9 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Patterns.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Patterns.java @@ -29,6 +29,11 @@ public static final String COMMA_SEPARATOR = toWhitespaceSeparator(","); /** + * A pattern string for lists separated by commas with optional whitespace or just whitespace. + */ + public static final String COMMA_SPACE_SEPARATOR = toWhitespaceSeparator("[,\\s]"); + + /** * The whitespace pattern string. */ public static final String WHITESPACE = "\\s*"; diff --git a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Source.java b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Source.java index e612634..8d45900 100644 --- a/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Source.java +++ b/log4j-core/src/main/java/org/apache/logging/log4j/core/util/Source.java @@ -145,7 +145,7 @@ * Constructs a new {@code Source} from the specified URL. * * @param url the URL where the input stream originated - * @throws IllegalArgumentException if this URL is not formatted strictly according to to RFC2396 and cannot be + * @throws IllegalArgumentException if this URL is not formatted strictly according to RFC2396 and cannot be * converted to a URI. */ public Source(final URL url) { diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/GcFreeLoggingTestUtil.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/GcFreeLoggingTestUtil.java index f505fdb..f5df803 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/GcFreeLoggingTestUtil.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/GcFreeLoggingTestUtil.java @@ -18,6 +18,7 @@ import com.google.monitoring.runtime.instrumentation.AllocationRecorder; import com.google.monitoring.runtime.instrumentation.Sampler; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; @@ -57,6 +58,7 @@ final Marker testGrandParent = MarkerManager.getMarker("testGrandParent"); final Marker testParent = MarkerManager.getMarker("testParent").setParents(testGrandParent); final Marker test = MarkerManager.getMarker("test").setParents(testParent); // initial creation, value is cached + final StringMapMessage mapMessage = new StringMapMessage().with("eventId", "Login"); // initialize LoggerContext etc. // This is not steady-state logging and will allocate objects. @@ -69,6 +71,7 @@ logger.error("Sample error message"); logger.error("Test parameterized message {}", "param"); logger.error(new StringMapMessage().with("eventId", "Login")); // initialize GelfLayout's messageStringBuilder + singleLoggingIteration(logger, myCharSeq, mapMessage); for (int i = 0; i < 256; i++) { logger.debug("ensure all ringbuffer slots have been used once"); // allocate MutableLogEvent.messageText } @@ -100,7 +103,6 @@ new RuntimeException().printStackTrace(); }; Thread.sleep(500); - final StringMapMessage mapMessage = new StringMapMessage().with("eventId", "Login"); AllocationRecorder.addSampler(sampler); // now do some steady-state logging @@ -110,13 +112,7 @@ final int ITERATIONS = 5; for (int i = 0; i < ITERATIONS; i++) { - logger.error(myCharSeq); - logger.error(MarkerManager.getMarker("test"), myCharSeq); - logger.error("Test message"); - logger.error("Test parameterized message {}", "param"); - logger.error("Test parameterized message {}{}", "param", "param2"); - logger.error("Test parameterized message {}{}{}", "param", "param2", "abc"); - logger.error(mapMessage); // LOG4J2-1683 + singleLoggingIteration(logger, myCharSeq, mapMessage); ThreadContext.remove("aKey"); ThreadContext.put("aKey", "value1"); } @@ -124,6 +120,89 @@ samplingEnabled.set(false); // reliably ignore all allocations from now on AllocationRecorder.removeSampler(sampler); Thread.sleep(100); + } + + private static void singleLoggingIteration( + final org.apache.logging.log4j.Logger logger, + final MyCharSeq myCharSeq, + final StringMapMessage mapMessage) { + logger.isEnabled(Level.TRACE); + logger.isEnabled(Level.TRACE, MarkerManager.getMarker("test")); + logger.isTraceEnabled(); + logger.isTraceEnabled(MarkerManager.getMarker("test")); + logger.trace(myCharSeq); + logger.trace(MarkerManager.getMarker("test"), myCharSeq); + logger.trace("Test message"); + logger.trace("Test parameterized message {}", "param"); + logger.trace("Test parameterized message {}{}", "param", "param2"); + logger.trace("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.trace(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.trace(mapMessage); // LOG4J2-1683 + + logger.isEnabled(Level.DEBUG); + logger.isEnabled(Level.DEBUG, MarkerManager.getMarker("test")); + logger.isDebugEnabled(); + logger.isDebugEnabled(MarkerManager.getMarker("test")); + logger.debug(myCharSeq); + logger.debug(MarkerManager.getMarker("test"), myCharSeq); + logger.debug("Test message"); + logger.debug("Test parameterized message {}", "param"); + logger.debug("Test parameterized message {}{}", "param", "param2"); + logger.debug("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.debug(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.debug(mapMessage); // LOG4J2-1683 + + logger.isEnabled(Level.INFO); + logger.isEnabled(Level.INFO, MarkerManager.getMarker("test")); + logger.isInfoEnabled(); + logger.isInfoEnabled(MarkerManager.getMarker("test")); + logger.info(myCharSeq); + logger.info(MarkerManager.getMarker("test"), myCharSeq); + logger.info("Test message"); + logger.info("Test parameterized message {}", "param"); + logger.info("Test parameterized message {}{}", "param", "param2"); + logger.info("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.info(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.info(mapMessage); // LOG4J2-1683 + + logger.isEnabled(Level.WARN); + logger.isEnabled(Level.WARN, MarkerManager.getMarker("test")); + logger.isWarnEnabled(); + logger.isWarnEnabled(MarkerManager.getMarker("test")); + logger.warn(myCharSeq); + logger.warn(MarkerManager.getMarker("test"), myCharSeq); + logger.warn("Test message"); + logger.warn("Test parameterized message {}", "param"); + logger.warn("Test parameterized message {}{}", "param", "param2"); + logger.warn("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.warn(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.warn(mapMessage); // LOG4J2-1683 + + logger.isEnabled(Level.ERROR); + logger.isEnabled(Level.ERROR, MarkerManager.getMarker("test")); + logger.isErrorEnabled(); + logger.isErrorEnabled(MarkerManager.getMarker("test")); + logger.error(myCharSeq); + logger.error(MarkerManager.getMarker("test"), myCharSeq); + logger.error("Test message"); + logger.error("Test parameterized message {}", "param"); + logger.error("Test parameterized message {}{}", "param", "param2"); + logger.error("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.error(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.error(mapMessage); // LOG4J2-1683 + + logger.isEnabled(Level.FATAL); + logger.isEnabled(Level.FATAL, MarkerManager.getMarker("test")); + logger.isFatalEnabled(); + logger.isFatalEnabled(MarkerManager.getMarker("test")); + logger.fatal(myCharSeq); + logger.fatal(MarkerManager.getMarker("test"), myCharSeq); + logger.fatal("Test message"); + logger.fatal("Test parameterized message {}", "param"); + logger.fatal("Test parameterized message {}{}", "param", "param2"); + logger.fatal("Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.fatal(MarkerManager.getMarker("test"), "Test parameterized message {}{}{}", "param", "param2", "abc"); + logger.fatal(mapMessage); // LOG4J2-1683 } public static void runTest(final Class cls) throws Exception { diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderTest.java index 9dcf88e..c5934b6 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/MemoryMappedFileAppenderTest.java @@ -35,7 +35,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; /** - * Tests that logged strings appear in the file, that the initial file size is the specified specified region length, + * Tests that logged strings appear in the file, that the initial file size is the specified region length, * that the file is extended by region length when necessary, and that the file is shrunk to its actual usage when done. * * @since 2.1 diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java index 8f8e06e..8d3f776 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/ReconfigureAppenderTest.java @@ -46,7 +46,7 @@ this.createAndAddAppender(); // let's write something to the logger to ensure the output stream is opened. - // We expect this call to create a a new output stream (which is does). + // We expect this call to create a new output stream (which is does). // see OutputStreamManager.writeToDestination(...). Logger logger = (Logger)LogManager.getLogger(this.getClass()); logger.info("test message 1"); diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCustomDeleteActionTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCustomDeleteActionTest.java new file mode 100644 index 0000000..03a8bb6 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCustomDeleteActionTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender.rolling; + +import java.io.File; +import java.util.Arrays; +import java.util.regex.Pattern; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.appender.RollingFileAppender; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * + */ +public class RollingAppenderDirectCustomDeleteActionTest implements RolloverListener { + + private static final String CONFIG = "log4j-rolling-direct-with-custom-delete.xml"; + private static final String DIR = "target/rolling-direct-with-delete/test"; + + private final LoggerContextRule loggerContextRule = LoggerContextRule.createShutdownTimeoutLoggerContextRule(CONFIG); + + @Rule + public RuleChain chain = loggerContextRule.withCleanFoldersRule(DIR); + + private boolean fileFound = false; + + @Test + public void testAppender() throws Exception { + final Logger logger = loggerContextRule.getLogger(); + RollingFileAppender app = loggerContextRule.getAppender("RollingFile"); + assertNotNull(app,"No RollingFileAppender"); + app.getManager().addRolloverListener(this); + // Trigger the rollover + for (int i = 0; i < 10; ++i) { + // 30 chars per message: each message triggers a rollover + logger.debug("This is a test message number " + i); // 30 chars: + } + Thread.sleep(100); // Allow time for rollover to complete + + final File dir = new File(DIR); + assertTrue("Dir " + DIR + " should exist", dir.exists()); + assertTrue("Dir " + DIR + " should contain files", dir.listFiles().length > 0); + + final File[] files = dir.listFiles(); + assertNotNull(files, "No fiels"); + System.out.println(files[0].getName()); + long count = Arrays.stream(files).filter((f) -> f.getName().endsWith("test-4.log")).count(); + assertTrue("Deleted file was not created", fileFound); + assertEquals("File count expected: 0, actual: " + count, 0, count); + } + + public static void main(final String[] args) { + final Pattern p = Pattern.compile("test-.?[2,4,6,8,0]\\.log\\.gz"); + for (int i = 0; i < 16; i++) { + final String str = "test-" + i + ".log.gz"; + final java.util.regex.Matcher m = p.matcher(str); + System.out.println(m.matches() + ": " + str); + } + } + + @Override + public void rolloverTriggered(String fileName) { + if (fileName.endsWith("test-4.log")) { + fileFound = true; + } + } + + @Override + public void rolloverComplete(String fileName) { + + } +} diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAppenderDirectWriteAndSwitchDirectorTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAppenderDirectWriteAndSwitchDirectorTest.java new file mode 100644 index 0000000..942cee8 --- /dev/null +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAppenderDirectWriteAndSwitchDirectorTest.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.core.appender.rolling; + +import org.apache.logging.log4j.core.Logger; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.junit.CleanUpDirectories; +import org.apache.logging.log4j.junit.LoggerContextSource; +import org.junit.jupiter.api.Test; + +import java.io.File; +import java.time.LocalTime; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +@CleanUpDirectories(RollingRandomAppenderDirectWriteAndSwitchDirectorTest.DIR) +public class RollingRandomAppenderDirectWriteAndSwitchDirectorTest { + public static final String DIR = "target/rolling-random-direct-switch-director"; + @Test + @LoggerContextSource(value= "log4j-rolling-random-direct-switch-director.xml", timeout = 10) + public void testAppender(final LoggerContext context) throws Exception { + Logger logger = context.getLogger(RollingRandomAppenderDirectWriteAndSwitchDirectorTest.class.getName()); + LocalTime start = LocalTime.now(); + LocalTime end; + do { + end = LocalTime.now(); + logger.info("test log"); + Thread.sleep(100); + } while (start.getSecond() == end.getSecond()); + + File nextLogFile = new File(String.format("%s/%d/%d.log", DIR, end.getSecond(), end.getSecond())); + assertTrue(nextLogFile.exists(), "nextLogFile not created"); + } +} diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/LoggerConfigTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/LoggerConfigTest.java index 935777d..200aa6f 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/config/LoggerConfigTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/config/LoggerConfigTest.java @@ -87,4 +87,25 @@ assertEquals(value, actualList.get(i).getValue(), "value[" + i + "]"); } } + + @Test + public void testLevel() { + Configuration configuration = new DefaultConfiguration(); + LoggerConfig config1 = LoggerConfig.newBuilder() + .withLoggerName("org.apache.logging.log4j.test") + .withLevel(Level.ERROR) + .withAdditivity(false) + .withConfig(configuration) + .build(); + LoggerConfig config2 = LoggerConfig.newBuilder() + .withLoggerName("org.apache.logging.log4j") + .withAdditivity(false) + .withConfig(configuration) + .build(); + config1.setParent(config2); + assertEquals(config1.getLevel(), Level.ERROR, "Unexpected Level"); + assertEquals(config1.getExplicitLevel(), Level.ERROR, "Unexpected explicit level"); + assertEquals(config2.getLevel(), Level.ERROR, "Unexpected Level"); + assertNull(config2.getExplicitLevel(),"Unexpected explicit level"); + } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java index 4a6d08d..c12ecbb 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/GelfLayoutTest.java @@ -16,7 +16,17 @@ */ package org.apache.logging.log4j.core.layout; -import com.fasterxml.jackson.core.io.JsonStringEncoder; +import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.List; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + import org.apache.commons.io.IOUtils; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.ThreadContext; @@ -33,19 +43,12 @@ import org.apache.logging.log4j.junit.UsingAnyThreadContext; import org.apache.logging.log4j.test.appender.EncodingListAppender; import org.apache.logging.log4j.test.appender.ListAppender; +import org.apache.logging.log4j.util.Chars; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.zip.GZIPInputStream; -import java.util.zip.InflaterInputStream; - -import static net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals; -import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.core.io.JsonStringEncoder; @UsingAnyThreadContext public class GelfLayoutTest { @@ -141,6 +144,11 @@ final String threadName = Thread.currentThread().getName(); //@formatter:off + String message = messages.get(0); + if (includeNullDelimiter) { + assertThat(message.indexOf(Chars.NUL)).isEqualTo(message.length() - 1); + message = message.replace(Chars.NUL, Chars.LF); + } assertJsonEquals("{" + "\"version\": \"1.1\"," + "\"host\": \"" + host + "\"," + @@ -152,8 +160,13 @@ "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," + "\"_" + KEY2 + "\": \"" + javaLookup.getRuntime() + "\"" + "}", - messages.get(0)); - + message); + + message = messages.get(1); + if (includeNullDelimiter) { + assertThat(message.indexOf(Chars.NUL)).isEqualTo(message.length() - 1); + message = message.replace(Chars.NUL, Chars.LF); + } assertJsonEquals("{" + "\"version\": \"1.1\"," + "\"host\": \"" + host + "\"," + @@ -170,7 +183,7 @@ "\"_" + KEY1 + "\": \"" + VALUE1 + "\"," + "\"_" + KEY2 + "\": \"" + javaLookup.getRuntime() + "\"" + "}", - messages.get(1)); + message); //@formatter:on final byte[] compressed = raw.get(2); final byte[] compressed2 = raw2.get(2); @@ -198,8 +211,8 @@ final byte[] uncompressed2 = IOUtils.toByteArray(inflaterStream2); inflaterStream.close(); inflaterStream2.close(); - final String uncompressedString = new String(uncompressed, layout.getCharset()); - final String uncompressedString2 = new String(uncompressed2, layout.getCharset()); + String uncompressedString = new String(uncompressed, layout.getCharset()); + String uncompressedString2 = new String(uncompressed2, layout.getCharset()); //@formatter:off final String expected = "{" + "\"version\": \"1.1\"," + @@ -219,16 +232,18 @@ "\"_" + KEY2 + "\": \"" + javaLookup.getRuntime() + "\"" + "}"; //@formatter:on + if (includeNullDelimiter) { + assertEquals(uncompressedString.indexOf(Chars.NUL), uncompressedString.length() - 1); + assertEquals(uncompressedString2.indexOf(Chars.NUL), uncompressedString2.length() - 1); + uncompressedString = uncompressedString.replace(Chars.NUL, Chars.LF); + uncompressedString2 = uncompressedString2.replace(Chars.NUL, Chars.LF); + } + if (includeNewLineDelimiter) { + assertEquals(uncompressedString.indexOf(Chars.LF), uncompressedString.length() - 1); + assertEquals(uncompressedString2.indexOf(Chars.LF), uncompressedString2.length() - 1); + } assertJsonEquals(expected, uncompressedString); assertJsonEquals(expected, uncompressedString2); - if (includeNullDelimiter) { - assertEquals(uncompressedString.indexOf('\0'), uncompressedString.length() - 1); - assertEquals(uncompressedString2.indexOf('\0'), uncompressedString2.length() - 1); - } - if (includeNewLineDelimiter) { - assertEquals(uncompressedString.indexOf('\n'), uncompressedString.length() - 1); - assertEquals(uncompressedString2.indexOf('\n'), uncompressedString2.length() - 1); - } } @Test diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java index 3e0b435..9279af1 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/layout/Rfc5424LayoutTest.java @@ -37,6 +37,11 @@ import org.apache.logging.log4j.core.Logger; import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.core.config.DefaultConfiguration; +import org.apache.logging.log4j.core.config.Node; +import org.apache.logging.log4j.core.config.plugins.util.PluginBuilder; +import org.apache.logging.log4j.core.config.plugins.util.PluginManager; +import org.apache.logging.log4j.core.config.plugins.util.PluginType; import org.apache.logging.log4j.core.net.Facility; import org.apache.logging.log4j.core.util.Integers; import org.apache.logging.log4j.core.util.KeyValuePair; @@ -569,6 +574,29 @@ } } + @Test + public void testLayoutBuilderDefaultValues() { + final Rfc5424Layout layout = new Rfc5424Layout.Rfc5424LayoutBuilder().build(); + checkDefaultValues(layout); + + final PluginManager manager = new PluginManager(Node.CATEGORY); + manager.collectPlugins(); + final Object obj = new PluginBuilder(manager.getPluginType("Rfc5424Layout")).withConfigurationNode(new Node()) + .withConfiguration(new DefaultConfiguration()) + .build(); + assertTrue(obj instanceof Rfc5424Layout); + checkDefaultValues((Rfc5424Layout) obj); + } + + private void checkDefaultValues(final Rfc5424Layout layout) { + assertNotNull(layout); + assertEquals(Facility.LOCAL0, layout.getFacility()); + assertEquals(String.valueOf(Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER), layout.getEnterpriseNumber()); + assertEquals(true, layout.isIncludeMdc()); + assertEquals(Rfc5424Layout.DEFAULT_MDCID, layout.getMdcId()); + assertEquals(Rfc5424Layout.DEFAULT_ID, layout.getDefaultId()); + } + @ParameterizedTest @ValueSource(strings = { "123456789", "0", "2147483647", "123.45.6.78.9", "0.0.0.0.0.0.0.0.0.0.0.0.0.0" }) void testLayoutBuilderValidEids(String eid) { diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfigurationTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfigurationTest.java index cb76930..c957771 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfigurationTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/KeyStoreConfigurationTest.java @@ -16,13 +16,26 @@ */ package org.apache.logging.log4j.core.net.ssl; -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.security.KeyStore; +import java.util.Collections; +import java.util.function.Supplier; +import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junitpioneer.jupiter.SetSystemProperty; +@SetSystemProperty(key = "sun.security.mscapi.keyStoreCompatibilityMode", value = "false") public class KeyStoreConfigurationTest { + @SuppressWarnings("deprecation") @Test public void loadEmptyConfigurationDeprecated() { @@ -43,14 +56,34 @@ TestConstants.KEYSTORE_TYPE, null); final KeyStore ks = ksc.getKeyStore(); assertNotNull(ks); + checkKeystoreConfiguration(ksc); } - @Test - public void loadNotEmptyConfiguration() throws StoreConfigurationException { - final KeyStoreConfiguration ksc = new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE, new MemoryPasswordProvider(TestConstants.KEYSTORE_PWD()), - TestConstants.KEYSTORE_TYPE, null); + static Stream configurations() { + final Stream.Builder builder = Stream.builder(); + builder.add(Arguments.of(TestConstants.KEYSTORE_FILE, (Supplier) TestConstants::KEYSTORE_PWD, + TestConstants.KEYSTORE_TYPE)) + .add(Arguments.of(TestConstants.KEYSTORE_PKCS12_FILE, + (Supplier) TestConstants::KEYSTORE_PKCS12_PWD, TestConstants.KEYSTORE_PKCS12_TYPE)) + .add(Arguments.of(TestConstants.KEYSTORE_EMPTYPASS_FILE, + (Supplier) TestConstants::KEYSTORE_EMPTYPASS_PWD, + TestConstants.KEYSTORE_EMPTYPASS_TYPE)); + if (OS.WINDOWS.isCurrentOs()) { + builder.add(Arguments.of(null, (Supplier) () -> null, "Windows-MY")) + .add(Arguments.of(null, (Supplier) () -> null, "Windows-ROOT")); + } + return builder.build(); + } + + @ParameterizedTest + @MethodSource("configurations") + public void loadNotEmptyConfiguration(final String keystoreFile, final Supplier password, + final String keystoreType) throws StoreConfigurationException { + final KeyStoreConfiguration ksc = new KeyStoreConfiguration(keystoreFile, + new MemoryPasswordProvider(password.get()), keystoreType, null); final KeyStore ks = ksc.getKeyStore(); assertNotNull(ks); + checkKeystoreConfiguration(ksc); } @Test @@ -79,9 +112,42 @@ () -> new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE, "wrongPassword!", null, null)); } - @Test - public void wrongPassword() { - assertThrows(StoreConfigurationException.class, () -> new KeyStoreConfiguration(TestConstants.KEYSTORE_FILE, - new MemoryPasswordProvider("wrongPassword!".toCharArray()), null, null)); + static Stream wrongConfigurations() { + final Stream.Builder builder = Stream.builder(); + builder.add(Arguments.of(TestConstants.KEYSTORE_FILE, (Supplier) TestConstants::KEYSTORE_EMPTYPASS_PWD, + TestConstants.KEYSTORE_TYPE)) + .add(Arguments.of(TestConstants.KEYSTORE_FILE, (Supplier) () -> "wrongPassword!".toCharArray(), + TestConstants.KEYSTORE_TYPE)) + .add(Arguments.of(TestConstants.KEYSTORE_PKCS12_FILE, + (Supplier) TestConstants::KEYSTORE_EMPTYPASS_PWD, TestConstants.KEYSTORE_PKCS12_TYPE)) + .add(Arguments.of(TestConstants.KEYSTORE_PKCS12_FILE, + (Supplier) TestConstants::KEYSTORE_EMPTYPASS_PWD, TestConstants.KEYSTORE_PKCS12_TYPE)); + if (OS.WINDOWS.isCurrentOs()) { + builder.add(Arguments.of(null, (Supplier) () -> new char[0], "Windows-MY")) + .add(Arguments.of(null, (Supplier) () -> new char[0], "Windows-ROOT")); + } + return builder.build(); + } + + @ParameterizedTest + @MethodSource("wrongConfigurations") + public void wrongPassword(final String keystoreFile, final Supplier password, final String keystoreType) { + assertThrows(StoreConfigurationException.class, () -> new KeyStoreConfiguration(keystoreFile, + new MemoryPasswordProvider(password.get()), keystoreType, null)); + } + + static void checkKeystoreConfiguration(final AbstractKeyStoreConfiguration config) { + // Not all keystores throw immediately if the password is wrong + assertDoesNotThrow(() -> { + final KeyStore ks = config.load(); + for (final String alias : Collections.list(ks.aliases())) { + if (ks.isCertificateEntry(alias)) { + ks.getCertificate(alias); + } + if (ks.isKeyEntry(alias)) { + ks.getKey(alias, config.getPasswordAsCharArray()); + } + } + }); } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactoryTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactoryTest.java index a153b73..55bd0b8 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactoryTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/SslConfigurationFactoryTest.java @@ -20,20 +20,33 @@ import static org.junit.jupiter.api.Assertions.assertNull; import java.util.Properties; +import java.util.stream.Stream; import org.apache.logging.log4j.util.PropertiesUtil; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnOs; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; public class SslConfigurationFactoryTest { + private static final String trustStorelocation = "log4j2.trustStoreLocation"; + private static final String trustStorePassword = "log4j2.trustStorePassword"; + private static final String trustStoreKeyStoreType = "log4j2.trustStoreKeyStoreType"; + private static final String keyStoreLocation = "log4j2.keyStoreLocation"; + private static final String keyStorePassword = "log4j2.keyStorePassword"; + private static final String keyStoreType = "log4j2.keyStoreType"; + private static void addKeystoreConfiguration(Properties props) { - props.setProperty("log4j2.keyStoreLocation", TestConstants.KEYSTORE_FILE_RESOURCE); - props.setProperty("log4j2.keyStoreKeyStoreType", TestConstants.KEYSTORE_TYPE); + props.setProperty(keyStoreLocation, TestConstants.KEYSTORE_FILE_RESOURCE); + props.setProperty(keyStoreType, TestConstants.KEYSTORE_TYPE); } private static void addTruststoreConfiguration(Properties props) { - props.setProperty("log4j2.trustStoreLocation", TestConstants.TRUSTSTORE_FILE_RESOURCE); - props.setProperty("log4j2.trustStoreKeyStoreType", TestConstants.TRUSTSTORE_TYPE); + props.setProperty(trustStorelocation, TestConstants.TRUSTSTORE_FILE_RESOURCE); + props.setProperty(trustStoreKeyStoreType, TestConstants.TRUSTSTORE_TYPE); } @Test @@ -69,4 +82,41 @@ assertNotNull(sslConfiguration.getKeyStoreConfig()); assertNotNull(sslConfiguration.getTrustStoreConfig()); } + + static Stream windowsKeystoreConfigs() { + final String[] emptyOrNull = { "", null }; + final Stream.Builder builder = Stream.builder(); + for (final String location : emptyOrNull) { + for (final String password : emptyOrNull) { + builder.add(Arguments.of(location, password)); + } + } + return builder.build(); + } + + @EnabledOnOs(OS.WINDOWS) + @ParameterizedTest + @MethodSource("windowsKeystoreConfigs") + public void testPasswordLessStores(final String location, final String password) { + final Properties props = new Properties(); + props.setProperty(keyStoreType, "Windows-MY"); + props.setProperty(trustStoreKeyStoreType, "Windows-ROOT"); + if (location != null) { + props.setProperty(keyStoreLocation, location); + props.setProperty(trustStorelocation, location); + } + if (password != null) { + props.setProperty(keyStorePassword, password); + props.setProperty(trustStorePassword, password); + } + final PropertiesUtil util = new PropertiesUtil(props); + final SslConfiguration config = SslConfigurationFactory.createSslConfiguration(util); + assertNotNull(config); + final KeyStoreConfiguration keyStoreConfig = config.getKeyStoreConfig(); + assertNotNull(keyStoreConfig); + KeyStoreConfigurationTest.checkKeystoreConfiguration(keyStoreConfig); + final TrustStoreConfiguration trustStoreConfig = config.getTrustStoreConfig(); + assertNotNull(trustStoreConfig); + KeyStoreConfigurationTest.checkKeystoreConfiguration(trustStoreConfig); + } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java index 90b3bed..a258f6f 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/net/ssl/TestConstants.java @@ -36,5 +36,19 @@ public static final char[] KEYSTORE_PWD() { return "changeit".toCharArray(); } public static final String KEYSTORE_TYPE = "JKS"; + public static final String KEYSTORE_PKCS12_PATH = PATH; + public static final String KEYSTORE_PKCS12_RESOURCE = RESOURCE_ROOT; + public static final String KEYSTORE_PKCS12_FILE = KEYSTORE_PKCS12_PATH + "client.log4j2-keystore.p12"; + public static final String KEYSTORE_PKCS12_FILE_RESOURCE = KEYSTORE_PKCS12_RESOURCE + "client.log4j2-keystore.p12"; + public static final char[] KEYSTORE_PKCS12_PWD() { return "changeit".toCharArray(); } + public static final String KEYSTORE_PKCS12_TYPE = "PKCS12"; + + public static final String KEYSTORE_EMPTYPASS_PATH = PATH; + public static final String KEYSTORE_EMPTYPASS_RESOURCE = RESOURCE_ROOT; + public static final String KEYSTORE_EMPTYPASS_FILE = KEYSTORE_EMPTYPASS_PATH + "client.log4j2-keystore-nopass.p12"; + public static final String KEYSTORE_EMPTYPASS_FILE_RESOURCE = KEYSTORE_EMPTYPASS_RESOURCE + "client.log4j-keystore-nopass.p12"; + public static final char[] KEYSTORE_EMPTYPASS_PWD() { return new char[0]; } + public static final String KEYSTORE_EMPTYPASS_TYPE = "PKCS12"; + public static final char[] NULL_PWD = null; } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java index 1217510..3878e17 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/HighlightConverterTest.java @@ -20,8 +20,13 @@ import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.SimpleMessage; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; + +import java.util.stream.Stream; /** * Tests the HighlightConverter. @@ -76,36 +81,28 @@ assertEquals(AnsiEscape.createSequence(colorName), converter.getLevelStyle(Level.DEBUG)); } - private void testLevelNames(final String colorName, final String expectedOutput) { - final String[] options = {"%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=false, " - + PatternParser.DISABLE_ANSI + "=false, " + "INFO=" + colorName}; + static Stream colors() { + return Stream.of( + Arguments.of("bright red","\u001B[1;31m"), + Arguments.of("red bright", "\u001B[31;1m"), + Arguments.of("bright_red", "\u001B[91m"), + Arguments.of("#1cd42b", "\u001B[38;2;28;212;43m"), + Arguments.of("fg_bright_red bg_bright_blue bold", "\u001B[91;104;1m"), + Arguments.of("FG_#1cd42b BG_#000000", "\u001B[38;2;28;212;43;48;2;0;0;0m")); + } + + @ParameterizedTest + @MethodSource("colors") + public void testColors(final String colorName, final String escape) { + final String[] options = { "%-5level: %msg", PatternParser.NO_CONSOLE_NO_ANSI + "=false, " + + PatternParser.DISABLE_ANSI + "=false, " + "INFO=" + colorName }; final HighlightConverter converter = HighlightConverter.newInstance(null, options); assertNotNull(converter); - final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).setLoggerName("a.b.c").setMessage( - new SimpleMessage("")).build(); + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).build(); final StringBuilder buffer = new StringBuilder(); converter.format(event, buffer); - assertEquals(expectedOutput, buffer.toString()); - } - - @Test - public void testLevelNamesBrightShort() { - testLevelNames("bright_red", "\u001B[91mINFO : \u001B[m"); - } - - public void testLevelNamesHexShort() { - testLevelNames("#1cd42b", "\u001B[38;2;28;212;43mINFO : \u001B[m"); - } - - @Test - public void testLevelNamesBrightFull() { - testLevelNames("fg_bright_red bg_bright_blue bold", "\u001B[91;104;1mINFO : \u001B[m"); - } - - @Test - public void testLevelNamesHexFull() { - testLevelNames("FG_#1cd42b BG_#000000", "\u001B[38;2;28;212;43;48;2;0;0;0mINFO : \u001B[m"); + assertEquals(escape + "INFO : \u001B[m", buffer.toString()); } @Test diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java index f82be20..95c4240 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/core/pattern/StyleConverterTest.java @@ -18,14 +18,19 @@ import java.util.List; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.junit.LoggerContextSource; import org.apache.logging.log4j.junit.Named; import org.apache.logging.log4j.test.appender.ListAppender; import org.apache.logging.log4j.util.Strings; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; @@ -57,4 +62,25 @@ public void testNull() { assertNull(StyleConverter.newInstance(null, null)); } + + @ParameterizedTest + @MethodSource("org.apache.logging.log4j.core.pattern.HighlightConverterTest#colors") + public void testHighlightConverterCompatibility(final String color, final String escape) { + final StyleConverter converter = StyleConverter.newInstance(null, new String[] { "Hello!", color }); + final StringBuilder sb = new StringBuilder(); + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).build(); + converter.format(event, sb); + assertEquals(escape + "Hello!" + AnsiEscape.getDefaultStyle(), sb.toString()); + } + + @ParameterizedTest + @MethodSource("org.apache.logging.log4j.core.pattern.HighlightConverterTest#colors") + public void testLegacyCommaSeparator(final String color, final String escape) { + final StyleConverter converter = StyleConverter.newInstance(null, + new String[] { "Hello!", color.replaceAll("\\s+", ",") }); + final StringBuilder sb = new StringBuilder(); + final LogEvent event = Log4jLogEvent.newBuilder().setLevel(Level.INFO).build(); + converter.format(event, sb); + assertEquals(escape + "Hello!" + AnsiEscape.getDefaultStyle(), sb.toString()); + } } diff --git a/log4j-core/src/test/java/org/apache/logging/log4j/test/appender/EncodingListAppender.java b/log4j-core/src/test/java/org/apache/logging/log4j/test/appender/EncodingListAppender.java index 680645a..698e6ff 100644 --- a/log4j-core/src/test/java/org/apache/logging/log4j/test/appender/EncodingListAppender.java +++ b/log4j-core/src/test/java/org/apache/logging/log4j/test/appender/EncodingListAppender.java @@ -43,7 +43,7 @@ private static class Destination implements ByteBufferDestination { // JUnit 5 stack traces can start to get looooong - ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[8192]); + ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[16384]); @Override public ByteBuffer getByteBuffer() { @@ -52,7 +52,7 @@ @Override public ByteBuffer drain(final ByteBuffer buf) { - throw new IllegalStateException("Unexpected message larger than 4096 bytes"); + throw new IllegalStateException("Unexpected message larger than 16384 bytes"); } @Override diff --git a/log4j-core/src/test/resources/log4j-rolling-direct-with-custom-delete.xml b/log4j-core/src/test/resources/log4j-rolling-direct-with-custom-delete.xml new file mode 100644 index 0000000..083b658 --- /dev/null +++ b/log4j-core/src/test/resources/log4j-rolling-direct-with-custom-delete.xml @@ -0,0 +1,46 @@ + + + + + target/rolling-direct-with-delete/ + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/log4j-core/src/test/resources/log4j-rolling-random-direct-switch-director.xml b/log4j-core/src/test/resources/log4j-rolling-random-direct-switch-director.xml new file mode 100644 index 0000000..bc07b1c --- /dev/null +++ b/log4j-core/src/test/resources/log4j-rolling-random-direct-switch-director.xml @@ -0,0 +1,49 @@ + + + + + start log4j-rolling-random-direct-switch-director test + %d %p %C{1.} [%t] %m%n + target/rolling-random-direct-switch-director + + + + + ${LOG_PATTERN} + + + + + ${LOG_PATTERN} + + + + + + + + + + + > + + + + + \ No newline at end of file diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/gencerts.sh b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/gencerts.sh new file mode 100755 index 0000000..e970f1c --- /dev/null +++ b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/gencerts.sh @@ -0,0 +1,28 @@ +mkdir tmp +# Create the CA key and certificate +openssl req -config rootca.conf -new -x509 -nodes -keyout tmp/log4j2-cacert.key -out tmp/log4j2-ca.crt -days 7302 +# Create the trust store and import the certificate +keytool -keystore ../truststore.jks -storetype jks -importcert -file 'tmp/log4j2-ca.crt' -keypass changeit -storepass changeit -alias log4j2-cacert -noprompt +#Import the root certificate +keytool -keystore ../client.log4j2-keystore.jks -alias log4j2-ca -importcert -file tmp/log4j2-ca.crt -keypass changeit -storepass changeit -noprompt +# Create the client private key in the client key store +keytool -genkeypair -keyalg RSA -alias client -keystore ../client.log4j2-keystore.jks -storepass changeit -keypass changeit -validity 7302 -keysize 2048 -dname "CN=client.log4j2, C=US" +# Create a signing request for the client # +keytool -keystore ../client.log4j2-keystore.jks -alias client -certreq -file tmp/client.csr -keypass changeit -storepass changeit +# Sign the client certificate +openssl x509 -req -CA 'tmp/log4j2-ca.crt' -CAkey 'tmp/log4j2-cacert.key' -in tmp/client.csr -out tmp/client.crt_signed -days 7302 -CAcreateserial -passin pass:changeit +# Verify the signed certificate +openssl verify -CAfile 'tmp/log4j2-ca.crt' tmp/client.crt_signed +#Import the client's signed certificate +keytool -keystore ../client.log4j2-keystore.jks -alias client -importcert -file tmp/client.crt_signed -keypass changeit -storepass changeit -noprompt +#Verify the keystore +keytool -list -keystore ../client.log4j2-keystore.jks -storepass changeit +# Create the server private key in the server key store +keytool -genkeypair -keyalg RSA -alias server -keystore tmp/server.log4j2-keystore.p12 -storepass changeit -storetype PKCS12 -keypass changeit -validity 7302 -keysize 2048 -dname "CN=server.log4j2, C=US" +# Create a signing request for the server # +keytool -keystore tmp/server.log4j2-keystore.p12 -alias server -certreq -file tmp/server.csr -keypass changeit -storepass changeit +# Sign the server certificate +openssl x509 -req -CA 'tmp/log4j2-ca.crt' -CAkey 'tmp/log4j2-cacert.key' -in tmp/server.csr -out ../server.log4j2-crt.pem -days 7302 -CAcreateserial -passin pass:changeit +# Extract the private key +openssl pkcs12 -in tmp/server.log4j2-keystore.p12 -passin pass:changeit -nokeys -out ../server.log4j2.pem +rm -rf tmp diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/rootca.conf b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/rootca.conf new file mode 100644 index 0000000..7d26446 --- /dev/null +++ b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/rootca.conf @@ -0,0 +1,9 @@ +[ req ] +distinguished_name = CA_DN +prompt = no +output_password = changeit +default_bits = 2048 + +[ CA_DN ] +C = US +CN = log4j2-ca diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/server.conf b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/server.conf new file mode 100644 index 0000000..00fa79b --- /dev/null +++ b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/build/server.conf @@ -0,0 +1,9 @@ +[ req ] +distinguished_name = CA_DN +prompt = no +output_password = changeit +default_bits = 2048 + +[ CA_DN ] +C = US +CN = iserver.log4j2 diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-crt.pem b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-crt.pem deleted file mode 100644 index b43ecab..0000000 --- a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-crt.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmDCCA4CgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAhMQswCQYDVQQGEwJVUzES -MBAGA1UEAxMJbG9nNGoyLWNhMB4XDTEzMDgwNzE1MDA1M1oXDTIyMDgwNTE1MDA1 -M1owJTELMAkGA1UEBhMCVVMxFjAUBgNVBAMTDWNsaWVudC5sb2c0ajIwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCWTzL0JcyH78O1JBPhgAytdtauJr6a -azQyaCSUzNqrEPQFzmcafMjaRvmHzduTnSSS6c5HwqU6nrVTOTkLdmGIiv1R60T4 -Jvhkvj2wX5SNSAn75T6C5Y9tvr6W0mPgKH0HovXMTOiwZ3rRp6FR8ivvhtVgd1/k -cBigxVR3rGpKO4eS84MRmWRnl/X/OEakE0DyKN15MwjrG/Vqw6ATApP1purYcWrc -Iw1pnL9rw6KcMUIftfvv0VHvJSh0Vlis3GVNTLcudrNtelW9bzc83HWLCUvu1JMx -HnMsYszVjZlQfH4z1Zwa/cnlEGn+YgxHL+QsYm3OW6kf5qT/JdH/nT1sTB0APErk -NSd0+gfR2cv/ijKpHJUJieIVN2NmTHQFnh18ZhXWRXQcwtAxtYsSlCJKNRoftYJ5 -VJsm0Ol7urT2ZM1LbfULC8pYgUE38zwolC86yupemKUb/2ZZiWSvWYqNwphQwr2P -Byv3VdRuinQjqkJ4DFZW/o6uqUBYxkSCJa53pEJhlBFPyK5tUXrQC+FVtI/Ddwn7 -Ajt5N9fEuxGLI9IBnaGAHpdTPhypJLhaJ+1sYI8h+t4C5OS1Tcy8549HGqVDEn1C -+mbBgiha+o4Pyayr44/8tis7WVHsG4b9OzzmqtyfZ3BSOQkIwgcpLqtV5RZh8eG6 -elVMDcbJkEFEnwIDAQABo4HWMIHTMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD -AgZAMCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRl -MB0GA1UdDgQWBBSV6z9LMG/560CWaF17MPtb2kD0DzBRBgNVHSMESjBIgBTiibBz -IjuHGFCXQrPY1v9l0ckTBaElpCMwITELMAkGA1UEBhMCVVMxEjAQBgNVBAMTCWxv -ZzRqMi1jYYIJAIPJp0IKQBHEMAkGA1UdEgQCMAAwCQYDVR0RBAIwADANBgkqhkiG -9w0BAQsFAAOCAgEAPGSXD9Pp6RYMIvAxKuaNNzUwm8ol35WI5hWgPpxDq0yGJ9ER -W5U/V4K5xqFuum3Q94u6K23aSJYYsm3YIR5QOkg5q/1wibvFgGzyd+TfBEvGieHR -NZ1FPLCLWA1oia/oYJchEk8V1fBTqmIb3a++JmtUk0tYtBKIplwVeOxxWWyVzFkm -kZ07P4lVBPLkT1Xkff2Jb1g+1kBDz+Esh1b8sOd7z2Gu8BHeF4THwpSaTS3lD6Y1 -2Jg4FOQ6/SYVoGghTBQp7+e8ROPeosNMUGrxhnjlITUyVsMPMszKBm5z4YGC25yj -vTzz311fTMm7ylhDIk0PAQHsNz2xntBy0/NiJxVaZI3DluwcfJR0/C5ZLZ7m4VvF -SnjNzedY1v8N/o1FD5x71bfvAJ97O46a7H80qPlTo5wxgYh0MwRle8xSJkD1JGvc -LPeLTifSsFDJ6N3lbkjc1McclIoG49sCFFShUftwxGBw4Urfmtg/wbj8FETEqBwp -buhxYYx+K91dwb+RDu/N1a6F0kbNwvnsH2Zi0SOmh1Otzif27ZUl++eqCxZxM7KO -ECI3NEhlbdxaYUwXnAc0rr/OTYV+rSsq5tmXXc+0PyMSFg9PhR/nyrDOXo2rkxg4 -UVaU5TXuVvI6rIsh+sauSDKzO4/5n3GlZ3QBB+wpvdSeD+eXKMlI9Tw/i/M= ------END CERTIFICATE----- diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore-nopass.p12 b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore-nopass.p12 new file mode 100644 index 0000000..0621849 Binary files /dev/null and b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore-nopass.p12 differ diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks index 36f11b6..80892ba 100644 Binary files a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks and b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.jks differ diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.p12 b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.p12 new file mode 100644 index 0000000..e2b1358 Binary files /dev/null and b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/client.log4j2-keystore.p12 differ diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/log4j2-cacert.pem b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/log4j2-cacert.pem deleted file mode 100644 index 24d3709..0000000 --- a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/log4j2-cacert.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFhTCCA22gAwIBAgIJAIPJp0IKQBHEMA0GCSqGSIb3DQEBBQUAMCExCzAJBgNV -BAYTAlVTMRIwEAYDVQQDEwlsb2c0ajItY2EwHhcNMTMwODA3MTQ1MzA3WhcNMjMw -ODA1MTQ1MzA3WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAxMJbG9nNGoyLWNhMIIC -IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvlYBNYEtVS2Wy4YaZc+SSgft -TLi25vFtc5UH3kJ62EPrcOxynOJIWbBybPkpL27Vnz+t/KZWTDnxOD2QuwCskm/U -XfpQe6V6t0geM1u7HuKYT/X7z4l8WU7w6QzmGe/OWxYj3tE8M85f/HwhlDTVRG/g -npLzYCo/oJAvE+IM7FQFG4it/gkt3F/6ekUmc431wMLrKIDBEys78Mx6Ez+G1IAN -kSLv8e59XVeWsmgIIpI192VvsvuEPrqpICCE3PG2XKqmbRcDJOxlRRUHgOF8WzTw -WFnyGA20xcJ4L9+9+Z8HLTCLKL6+m60lqypdHnd3dp9iTfWVuYvM/7PLORaOEURb -WAVp1dXG01p2cuSbjw5+/0BRHFwdcBfW9Vl7R6b7uI1+O80xJK7g6DdYzSfIFpDO -88FfGaEwuz+bMVE7MI8wFqXMlwXV6zE9NwWIcl3lKhzgJNtU+lczLyHAEPR0/SRp -X3PGp4W4sml3/e4xAZs9Ae4njDzqBMod5EAG/F5WyrIAIwYDD3DGQfcdKrXSCH1K -38YFxmm+Awg5ijjr+Y+SbMTee3tKHzCEtDtbIpUXRPraODp00CNA+7qvMI/z+3eW -MP/b+84rI/GYkxykLratJ0tSOoJdn7ZOWKL4k/vAkIM43Ie4Paru8aurowi/xFIz -8Y94R1LPWqsB7iM2arECAwEAAaOBvzCBvDAdBgNVHQ4EFgQU4omwcyI7hxhQl0Kz -2Nb/ZdHJEwUwUQYDVR0jBEowSIAU4omwcyI7hxhQl0Kz2Nb/ZdHJEwWhJaQjMCEx -CzAJBgNVBAYTAlVTMRIwEAYDVQQDEwlsb2c0ajItY2GCCQCDyadCCkARxDAPBgNV -HRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIEAjAAMAkGA1Ud -EQQCMAAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4ICAQB0rkEVllwI -ajaqyDajCKxKAl2euPixzQh3w4Hauip0QwxHwV5lrQUixQ5Qu/7gCxTQL25/K6Zw -gwtQ+n6CESL+QTDgL34JOMXXoE+SfEGwcTlyeYKpPzs3fH5rKqzTzGeB4ZnoOzhZ -TiHvdUXvLjIKuBEnO8Ho9YG2BnGKCCsCQXaUrf1fT3yKcz4trb8AYzOdJVI93ykH -qaYgZGnvnPs8Nafdm8DLF3HloKsofdGjnB1UWtBBX6Rg+wCXS8/Twrc1YYEs2mrq -tyKOekvCJvNMZjlIiBN4OPG2N5jW03N3vW2fEsgALj37sI54x7PpNODcFutDWUvJ -UsvFofK8Ik2IreA6A6GO1kWEP6VIkYf9RePEVm6+d274AANEUzws2JAj4jKquiNf -foaUbuXLC7NIeZ85Jaw0QtNUPiR/jR5wYNZEhWtRZBlME4bH6wYUGywUXzIfInaz -NVIUkbyb7R1ljO461miZMxPZgnHy8MW6D2218/KQrERLDjrM/ggFTw7hv5gJWzUQ -h0R/VblnkZZPAwNYBM8Q11mCAtt/sU5wEQA6SFJpkqAxzQS438gFYC5QMo7Lb1vp -obY84mniOFcpCbY9O2DcXoZTXz22ixMvLu3t2rhLMe+O8RnjCZWh6ynFWa0zFb6E -9oCOdepDTGL48lxcOEpnfNYtkn7BG/pS0g== ------END CERTIFICATE----- diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-crt.pem b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-crt.pem index df75f2a..e7bba63 100644 --- a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-crt.pem +++ b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-crt.pem @@ -1,32 +1,17 @@ -----BEGIN CERTIFICATE----- -MIIFmDCCA4CgAwIBAgIBATANBgkqhkiG9w0BAQUFADAhMQswCQYDVQQGEwJVUzES -MBAGA1UEAxMJbG9nNGoyLWNhMB4XDTEzMDgwNzE0NTcxMFoXDTIyMDgwNTE0NTcx -MFowJTELMAkGA1UEBhMCVVMxFjAUBgNVBAMMDXNlcnZlci5sb2c0ajIwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ7tnl+byEnkJSePcj9XZ6y1C3eUOo -AsI70fgPIemUP+139VGCxa0uvYpE4tri4ZQV9zLq2VLPr9Cu5+ZBvA9ziEoeU3Rg -vWIFN1wm+iTJy8mfNI2x/q9W5NiUqjYWZVAJhdImO+wWfKXfhE07CLY/pUrv/QiY -EsBXQ3dWGussWu1mVS46B/ehHkNJUZCxbtwvaIZUJwBr0CPVIcv55hwAgD9pyXMn -Mfr1ze0iIpvH3gKZEi8Qzlj8HIxjnVOCBGU6r1DhvfNGaGMkmMwclxxhK19mam2t -AyscnJJ4HJ+uQZ6jT2CTj5xfeX5kwMy5Eb1gS0cwZrNQYSBdXI1/SNqNIsyI3uZd -plZ00X6D9cYTtc8pnSbaCbriDnod5o3e1GgKwhbYZfG92NWR5/XEh7YVqR8gGq+v -4I0Zd7ncGPVOkBRzQJpXxzrn+r8bbregqcbFlpR6xtSoJjsjLzl4965/BpipaT9S -txO4NS2G5ETmCDViRsmWhNvXPkbhzigNW95p5p33tX7fryWM3kw49qG1u24RbdC2 -R49nH2xslrKo96IYCCakiu0RJI6kfES7VDukN5bt3kI7jOOzH1GD2TlTIXHKbTkr -XpMzl2T7QbmHy2vEtIoEOFNbme7QJ5s3fScyE9IveRfTPyYNDsWXWGSHqPlHUeD7 -/EKkWaeniZlUIQIDAQABo4HWMIHTMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQD -AgZAMCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRl -MB0GA1UdDgQWBBSOJ6srlIpZgJHLL23jrfzRgq5uvjBRBgNVHSMESjBIgBTiibBz -IjuHGFCXQrPY1v9l0ckTBaElpCMwITELMAkGA1UEBhMCVVMxEjAQBgNVBAMTCWxv -ZzRqMi1jYYIJAIPJp0IKQBHEMAkGA1UdEgQCMAAwCQYDVR0RBAIwADANBgkqhkiG -9w0BAQUFAAOCAgEAXxhzp8omz5kdHOgHTZF2a1QNVf1CiXjRjS/7WHKeykKrZNi0 -vqdlJIrH62g2l4JC9JaBlVh38PsH/RLuabwWplT8yjos+Tqhh5Vp4Fipm9qEDZh9 -htlGiitYQD3/H1vtEb2VGL2+pSsFYwCAaztrwO/edfIDjmL2hlA+9MA9YdmH6WhE -Vb+uth4vTYeW7VevvX8R4LHLP/sGA4XiMsMlJmuehRsdeRbec9BfmGFhZZ5qOOrL -rfBoFlYoyvIRBp1ed9sBmaSJyurwPuZq9uOmbv7QjbaYJmLu/0nBB9yA+loXVIxV -l1+y+OV6khatzNOf2hrvTpCwxtlBtyJ5IZXR2Up3tid5+2oaGYW++h5D1QnJ8WAB -ORF7IEXiMZCVEwi97IuajZnm5CxgVsKpuELnYQmWZrQvzYuQ8kFTZZ7DEElHgtCT -oHl0YSKbfYz5MWxF6cmZGUbePyXPDT4iyh+fl/lfQIt04JUd6EO6hfgTKmqY8iR+ -RaVLlzGpSzwzNJ8rg4J8JN7IYr9VBX3o52xR9PmdomJBmNFqf17gfNgoulZORngZ -JnxgAXSv41k5w+cmi747YUHaGvy7lqb4rfSEoGiENuicQ7g+tDvb0TvhGO8A5jsy -kdlWKkvDeCZeG52ctwia5DuhF6KCskCq/F2XJS3qntwOEu0BV4lmmu4hxek= +MIICwjCCAaoCCQCZurToALecATANBgkqhkiG9w0BAQUFADAhMQswCQYDVQQGEwJV +UzESMBAGA1UEAwwJbG9nNGoyLWNhMB4XDTIyMDgyMjE2NDI0MFoXDTQyMDgxOTE2 +NDI0MFowJTELMAkGA1UEBhMCVVMxFjAUBgNVBAMTDXNlcnZlci5sb2c0ajIwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCOqJsHWe/IHWm2vGuZHkKccPeE +VsF6cQWzmqSAo29kWRWb7QYlJzoat9W++VnQ1M6x27tnphBBwnh9YTJ6eZpQ+y9h ++FnH8/8nIyB8pyP7VZTn993YvmpkdZwT7LMeANJFrPLmYUl0r0tHJEQWbULq8czF +B/57/hZVfYBs0/Hj1wnURmQ0RGaARhFYKg1TW2AFLUKrWNw0aiZbcjOcVf61Ysmh +99TK1IhL+aOByTB07x9t0xk9caeyfARmHvCh6SFzFGXAc8ov5omZqcBKZS6HS1Ym +kd7oqX1p/ZMugc9i35nn2tqQCQxg/+F31H82IhuTAFvRaHwrCJxXA+mApeI/AgMB +AAEwDQYJKoZIhvcNAQEFBQADggEBAES9R3yiKuFo901AOpR0EYGM8gPwujiUP4O2 +whYqlBhPwhrYGlDUzxgt6VmPYaxuD9xhed99U/LThC6CjJFXnGKJL33BQksyjTM5 +vphUyVXWUiMDhtLdijNOBoI6y3pEL6+k90pfDy8j6SqalukukNfSjNJvPXBpiDyh +aLgoixuul0jwZi7vu8k+IKXjRt0NzQaKPLmOFkoccSB0qFkTA+WTd2vzhgS0hH+C +k9gCS3XtJhPPWNS3JZ6+UWHbJiGLjm/SfhZifKIuoW5S394p78DIhBk3otopdPrk +gz0WZFCA/7m0AtOpmz9YZsS7JocJEvD3RLDZ0owl+9VobKa1kB8= -----END CERTIFICATE----- diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-key.pem b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-key.pem deleted file mode 100644 index de804ee..0000000 --- a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2-key.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKgIBAAKCAgEAye7Z5fm8hJ5CUnj3I/V2estQt3lDqALCO9H4DyHplD/td/VR -gsWtLr2KROLa4uGUFfcy6tlSz6/QrufmQbwPc4hKHlN0YL1iBTdcJvokycvJnzSN -sf6vVuTYlKo2FmVQCYXSJjvsFnyl34RNOwi2P6VK7/0ImBLAV0N3VhrrLFrtZlUu -Ogf3oR5DSVGQsW7cL2iGVCcAa9Aj1SHL+eYcAIA/aclzJzH69c3tIiKbx94CmRIv -EM5Y/ByMY51TggRlOq9Q4b3zRmhjJJjMHJccYStfZmptrQMrHJySeByfrkGeo09g -k4+cX3l+ZMDMuRG9YEtHMGazUGEgXVyNf0jajSLMiN7mXaZWdNF+g/XGE7XPKZ0m -2gm64g56HeaN3tRoCsIW2GXxvdjVkef1xIe2FakfIBqvr+CNGXe53Bj1TpAUc0Ca -V8c65/q/G263oKnGxZaUesbUqCY7Iy85ePeufwaYqWk/UrcTuDUthuRE5gg1YkbJ -loTb1z5G4c4oDVveaead97V+368ljN5MOPahtbtuEW3QtkePZx9sbJayqPeiGAgm -pIrtESSOpHxEu1Q7pDeW7d5CO4zjsx9Rg9k5UyFxym05K16TM5dk+0G5h8trxLSK -BDhTW5nu0CebN30nMhPSL3kX0z8mDQ7Fl1hkh6j5R1Hg+/xCpFmnp4mZVCECAwEA -AQKCAgEAyXx6Du5RHEKNCp2Ie2jA/2U+9NMantmh1O59BRxhZHslBzzQSBvV4X1e -Kb3xidBrYj91nr+Z0YEsSFk7dvuerziePAQpax2MYIgMexe8/V1JoIFfoOrvKVTO -hggQT+hnJBlSxrOjrgxRteTc6rqWnorfavafTJ3pLSk0OcZCQc+4cRMtPNoWS71W -hiHMrj5flS5GWlFQKkNUfaVrUb440ockvvky3TkRn5IJurWtmo/7J6DtNWevgl85 -9WtVl7WrDBNCMPzHcMlXo3ySrTRaLNqKJjarmKZArhvMSezFp6nyECFzy8jPzrP7 -WazGmSMKQrraHYFcCiffk0h2JzsaVVI2trX/czU/rqk9WaJOJkMviaT/wSYoopG5 -j/GCdYf1wjAWYvys5jLOsvpoPdgPHInbwZRf+BN+Hnp2H/M7tVzJfkOEjU1UVZm1 -j+qRyEbYA3jel29ghg0YzbXptz47mR+UMt7/jesQL4RMDb2XGzpVSuR0LcGDO3BO -i0X8ftawVf6y83RDo1/ngh8zBXUnMNaMqSFoF3AtAqKzmodYWMC7Ihcgo8iKhh3i -PdyNGnHIYqB8MFP+O8n1GchOjXdyqzHNo9hLOMdwx075h/PSLSbA3fiVda5UQE/b -z36GarSGdRB+H8kUELsAbyK60u7ZVG77+hZFeqHA6s4zZ81VJcECggEBAPQA+W+a -BETE6ohHfQup39ii2UFfQX+aafb5sb5CtEw/M/DGQWrW+NTpX95jEdpEv0K4Il7G -cB4KgG1nC7hWWZhptuTsQyuV7GCAVz3J1ojFyyRzmQsBrjNeUCak31mNvo31JcSV -jJqOooMmgHMz3jcy2iBuTroFrhDcLbrmqZ1IzrMQDaxzIRsp5bS6gr5F7eVa5Q+s -cgH2hnsZA+L3tRf7DN/Zop0gKTm0bCP3ikA50vb8DGg4+BLMq9bwLHS8X8ZUyRmS -w3rCfuONP/631PPpyNMmobhjsEYEWebwTm6GewbT2ct3+LKThwuEsuq/nckoSxyD -Zv4qfk/9MwAJ02cCggEBANPcX/fjBO1dSPuPVbbHi607jBjYi+0eXi6RQ/ac0Wud -fkmrtyolWBbx/4QMXt3/seDjmGLFjzvAmfJAq8KqctX47Uoxbavcd+IcBwZkKd47 -INbXeCJ0v4Rf0ek7il2ZnRmQtp4jrcQ1Tj1Z0vp5eb+oQCQa8WSLok4DDjFqlhe4 -ki9yqIz+37MSQdnfvXRA0q5vYI1tpLOSSh/ytRO+FYmCs+T0ZSxx1ghieT4DcJjZ -tkiQitUgMJdGjkboLXbnLHbDuXVKQvxk+Vq0wKiwxFhsew954OM9mIU6E67i/2A9 -daE+iYOUWkVZS69HspfwcRONrUa3xtLkTFAT2dHLLzcCggEAYQGuKT9bKRf8gheh -0CxGMTN1GjdLq/Zw4F6e2pxcX9/Uv4miYl566PKrSZerdlJUk6rvByzvUBIuws/v -6eZyklSdAOApSD+/jRIZHRiMcS3puGE43BZb8lIxnVt47Hqc4oUu8bve/WdtlqSM -9sANPXa7H5+bLvupG0zd5WtkzYIvoyLjvMa8x7bdev+XkRvuG3wKizOs07j1+CSW -1fpTBeiqUqiMrNknaqifQaFdVnFmvQixyhG0Fo1GWBp+Ih7uGMSGeuNNUhXXKpJy -ecsOH7a3P7MhIlSHtR5vu4+YLvrKGInLEchtmFlsnBiypP9vNJUPX5OiM7QjKZvp -Vb+SSwKCAQEAvIYn59/eLJKMNT5RGbrrshWBD0LzEOerC2k8vyDAwjB9hnUFIr3k -a1ag9xJDAO1dji85FPuT/6nd09J0hV9DnOBL12+wlfQzwcMLBUbwcs7O5B8Myix1 -mAfkNNVExRHC2FMMDiDVqfl9S964fSbHjiLcIMTQ9ZXDsgPKB3M/aJBXlV1EW2Ma -ELE4Y/ZTEjX0xEBb/L4Kh7ivuUoylATcCWeDSUOpHCmYOhHWrbZF5d4l9OCaihiA -5LxmMva8Lkh8Kpr0V0jj3tDDi1G49uOFdOHjh9v+oHfZVwRdK4zXjv5hxEBwAeRr -h7Z0QN9eLxJIrQuvm3RBurxopAFmHOffdQKCAQEA24awG89Oydyr9DBSu1oMkdWz -mx9Kz8ppuDz/nx21CabAGh0J9yahEA8RvUqgWuLwaSMEMCJaSP+Ed9FSFhTHJMbU -LiGV5Xm9/GD5NO3L4o0hvCKmdRKmpoUD5t5IIx6cKsiW4/f3sQW+kTVlr/M488kb -Oc5VwnMPqNaP1dtbXhe9uJi6InRubMytkY3/xwPX7GjqrfFMOYyUGCkk7wt11d1h -4lm4dVfhPtlSzZnjAV1+YGwlaJzThFbCEpbg3WRXHu0yDKVQUPTijAC9XFUE1Og8 -FuuRrp2B+lOAhVi/8OiDBwUZTZ/pSWysOzY20fWq2rKEqW19ov8hHN/NFDGhSw== ------END RSA PRIVATE KEY----- diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2.pem b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2.pem new file mode 100644 index 0000000..dc4339c --- /dev/null +++ b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/server.log4j2.pem @@ -0,0 +1,23 @@ +Bag Attributes + friendlyName: server + localKeyID: 54 69 6D 65 20 31 36 36 31 31 38 36 35 35 39 37 37 37 +subject=/C=US/CN=server.log4j2 +issuer=/C=US/CN=server.log4j2 +-----BEGIN CERTIFICATE----- +MIIC6TCCAdGgAwIBAgIEdDynVTANBgkqhkiG9w0BAQsFADAlMQswCQYDVQQGEwJV +UzEWMBQGA1UEAxMNc2VydmVyLmxvZzRqMjAeFw0yMjA4MjIxNjQyMzlaFw00MjA4 +MTkxNjQyMzlaMCUxCzAJBgNVBAYTAlVTMRYwFAYDVQQDEw1zZXJ2ZXIubG9nNGoy +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjqibB1nvyB1ptrxrmR5C +nHD3hFbBenEFs5qkgKNvZFkVm+0GJSc6GrfVvvlZ0NTOsdu7Z6YQQcJ4fWEyenma +UPsvYfhZx/P/JyMgfKcj+1WU5/fd2L5qZHWcE+yzHgDSRazy5mFJdK9LRyREFm1C +6vHMxQf+e/4WVX2AbNPx49cJ1EZkNERmgEYRWCoNU1tgBS1Cq1jcNGomW3IznFX+ +tWLJoffUytSIS/mjgckwdO8fbdMZPXGnsnwEZh7woekhcxRlwHPKL+aJmanASmUu +h0tWJpHe6Kl9af2TLoHPYt+Z59rakAkMYP/hd9R/NiIbkwBb0Wh8KwicVwPpgKXi +PwIDAQABoyEwHzAdBgNVHQ4EFgQU0VkuBNTMtKknjRJcZ7IiU1UNOOAwDQYJKoZI +hvcNAQELBQADggEBAFCKDMn065xXejfo+lLEXenVIUn4moX/KCibGZwqhcYWXjrZ +BxSblxrLn3KSeuY3NBlaw/GTXLypNgZlr1skwhl4Zy4B1Yd97m5rHBmlxyWODL3g +ZgjSXWA8zc84wps196YJPP6mrP05bAgoRPEsXIBolFvC/M/NvzWukh5UbGGactJG +eMdVQ88HksimnWHrkWXMURqD/KqCwWvXc7Ppy9XZwb4+tEJ2S/6MgCOApSQU+uol +qxOrgg8rl74JF9/RXLode/KWdIPuYF+FIkXUt49S6s/diIwX6YkGLjAk8rFoaBmf +5sFkbGStttNaZfhkDGLwV9KWE++BbdYy5IpksP0= +-----END CERTIFICATE----- diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/syslog-ng-sample.conf b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/syslog-ng-sample.conf deleted file mode 100644 index 267862c..0000000 --- a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/syslog-ng-sample.conf +++ /dev/null @@ -1,33 +0,0 @@ -source demo_tls_source { - tcp(ip(0.0.0.0) port(6515) - tls( - ca_dir("/etc/ssl/certs") - key_file("/home/btibi/ca/log4j2/server.log4j2-key.pem") - cert_file("/home/btibi/ca/log4j2/server.log4j2-crt.pem") - ) - ); -}; - -source demo_tls_syslog_source { - syslog(ip(0.0.0.0) port(6514) - transport("tls") - tls( - ca_dir("/etc/ssl/certs") - key_file("/home/btibi/ca/log4j2/server.log4j2-key.pem") - cert_file("/home/btibi/ca/log4j2/server.log4j2-crt.pem") - ) - ); -}; - -destination d_structuredOverTLS { file("/var/log/structuredOverTLS"); }; -destination d_legacyOverTLS { file("/var/log/legacyOverTLS"); }; - -log { - source(demo_tls_source); - destination(d_legacyOverTLS); -}; - -log { - source(demo_tls_syslog_source); - destination(d_structuredOverTLS); -}; diff --git a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks index 0e6aaf2..2ad25d5 100644 Binary files a/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks and b/log4j-core/src/test/resources/org/apache/logging/log4j/core/net/ssl/truststore.jks differ diff --git a/log4j-core-its/pom.xml b/log4j-core-its/pom.xml index 3a78439..ee58d60 100644 --- a/log4j-core-its/pom.xml +++ b/log4j-core-its/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-core-its jar @@ -125,10 +125,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.hamcrest diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/Histogram.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/Histogram.java index 89cf29f..417fb51 100644 --- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/Histogram.java +++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/Histogram.java @@ -120,7 +120,7 @@ * Add an observation to the histogram and increment the counter for the interval it matches. * * @param value for the observation to be added. - * @return return true if in the range of intervals and successfully added observation; otherwise false. + * @return true if in the range of intervals and successfully added observation; otherwise false. */ public boolean addObservation(final long value) { diff --git a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/IdleStrategy.java b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/IdleStrategy.java index 65b97df..f73e331 100644 --- a/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/IdleStrategy.java +++ b/log4j-core-its/src/test/java/org/apache/logging/log4j/core/async/perftest/IdleStrategy.java @@ -21,7 +21,7 @@ * *

    Note regarding potential for TTSP(Time To Safe Point) issues

    * - * If the caller spins in a 'counted' loop, and the implementation does not include a a safepoint poll this may cause a TTSP + * If the caller spins in a 'counted' loop, and the implementation does not include a safepoint poll this may cause a TTSP * (Time To SafePoint) problem. If this is the case for your application you can solve it by preventing the idle method from * being inlined by using a Hotspot compiler command as a JVM argument e.g: * -XX:CompileCommand=dontinline,org.apache.logging.log4j.core.async.perftest.NoOpIdleStrategy::idle diff --git a/log4j-core-java9/pom.xml b/log4j-core-java9/pom.xml index 528889c..5fc9bd5 100644 --- a/log4j-core-java9/pom.xml +++ b/log4j-core-java9/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-core-java9 pom diff --git a/log4j-couchdb/pom.xml b/log4j-couchdb/pom.xml index be7eb8f..fb2292b 100644 --- a/log4j-couchdb/pom.xml +++ b/log4j-couchdb/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -50,10 +50,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.mockito diff --git a/log4j-distribution/pom.xml b/log4j-distribution/pom.xml index f984317..8b81346 100644 --- a/log4j-distribution/pom.xml +++ b/log4j-distribution/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-distribution pom @@ -171,18 +171,18 @@ org.apache.logging.log4j - log4j-slf4j18-impl - ${project.version} - - - org.apache.logging.log4j - log4j-slf4j18-impl - ${project.version} - sources - - - org.apache.logging.log4j - log4j-slf4j18-impl + log4j-slf4j2-impl + ${project.version} + + + org.apache.logging.log4j + log4j-slf4j2-impl + ${project.version} + sources + + + org.apache.logging.log4j + log4j-slf4j2-impl ${project.version} javadoc @@ -205,6 +205,23 @@
    org.apache.logging.log4j + log4j-to-jul + ${project.version} + + + org.apache.logging.log4j + log4j-to-jul + ${project.version} + sources + + + org.apache.logging.log4j + log4j-to-jul + ${project.version} + javadoc + + + org.apache.logging.log4j log4j-jmx-gui ${project.version} @@ -256,6 +273,40 @@
    org.apache.logging.log4j + log4j-jakarta-web + ${project.version} + + + org.apache.logging.log4j + log4j-jakarta-web + ${project.version} + sources + + + org.apache.logging.log4j + log4j-jakarta-web + ${project.version} + javadoc + + + org.apache.logging.log4j + log4j-jakarta-smtp + ${project.version} + + + org.apache.logging.log4j + log4j-jakarta-smtp + ${project.version} + sources + + + org.apache.logging.log4j + log4j-jakarta-smtp + ${project.version} + javadoc + + + org.apache.logging.log4j log4j-couchdb ${project.version} @@ -336,6 +387,23 @@ org.apache.logging.log4j log4j-jpa + ${project.version} + javadoc + + + org.apache.logging.log4j + log4j-jpl + ${project.version} + + + org.apache.logging.log4j + log4j-jpl + ${project.version} + sources + + + org.apache.logging.log4j + log4j-jpl ${project.version} javadoc @@ -409,6 +477,40 @@
    org.apache.logging.log4j + log4j-kubernetes + ${project.version} + + + org.apache.logging.log4j + log4j-kubernetes + ${project.version} + sources + + + org.apache.logging.log4j + log4j-kubernetes + ${project.version} + javadoc + + + org.apache.logging.log4j + log4j-layout-template-json + ${project.version} + + + org.apache.logging.log4j + log4j-layout-template-json + ${project.version} + sources + + + org.apache.logging.log4j + log4j-layout-template-json + ${project.version} + javadoc + + + org.apache.logging.log4j log4j-spring-boot ${project.version} @@ -440,6 +542,17 @@ log4j-spring-cloud-config-client ${project.version} javadoc +
    + + org.apache.logging.log4j + log4j-osgi + ${project.version} + + + org.apache.logging.log4j + log4j-osgi + ${project.version} + sources diff --git a/log4j-docker/pom.xml b/log4j-docker/pom.xml index 3ea1438..4c0fbf6 100644 --- a/log4j-docker/pom.xml +++ b/log4j-docker/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-docker jar diff --git a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java index a9cc1e2..a546a84 100644 --- a/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java +++ b/log4j-docker/src/main/java/org/apache/logging/log4j/docker/DockerLookup.java @@ -35,7 +35,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; /** - * Lookups up keys for for a Docker container. + * Lookups up keys for a Docker container. */ @Plugin(name = "docker", category = StrLookup.CATEGORY) public class DockerLookup extends AbstractLookup { diff --git a/log4j-flume-ng/pom.xml b/log4j-flume-ng/pom.xml index dc5ae12..bea5227 100644 --- a/log4j-flume-ng/pom.xml +++ b/log4j-flume-ng/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-flume-ng jar @@ -70,10 +70,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.apache.flume diff --git a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java index e926139..936028f 100644 --- a/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java +++ b/log4j-flume-ng/src/main/java/org/apache/logging/log4j/flume/appender/FlumeAppender.java @@ -249,10 +249,10 @@ final int delayMillis = Integers.parseInt(maxDelayMillis, DEFAULT_MAX_DELAY); if (layout == null) { - final String enterpriseNumber = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER; + final int enterpriseNumber = Rfc5424Layout.DEFAULT_ENTERPRISE_NUMBER; layout = new Rfc5424Layout.Rfc5424LayoutBuilder() .setFacility(Facility.LOCAL0) - .setEin(enterpriseNumber) + .setEin(String.valueOf(enterpriseNumber)) .setIncludeMDC(true) .setMdcId(Rfc5424Layout.DEFAULT_MDCID) .setMdcPrefix(mdcPrefix) diff --git a/log4j-iostreams/pom.xml b/log4j-iostreams/pom.xml index 8e4f1ae..ff95885 100644 --- a/log4j-iostreams/pom.xml +++ b/log4j-iostreams/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-iostreams jar @@ -56,10 +56,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.hamcrest diff --git a/log4j-jakarta-smtp/pom.xml b/log4j-jakarta-smtp/pom.xml index 35aab7e..535503f 100644 --- a/log4j-jakarta-smtp/pom.xml +++ b/log4j-jakarta-smtp/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-jakarta-smtp @@ -69,10 +69,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.hamcrest diff --git a/log4j-jakarta-web/pom.xml b/log4j-jakarta-web/pom.xml index c3eb1ae..b01df14 100644 --- a/log4j-jakarta-web/pom.xml +++ b/log4j-jakarta-web/pom.xml @@ -20,7 +20,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 4.0.0 @@ -61,17 +61,18 @@ test - org.hamcrest - hamcrest + org.hamcrest + hamcrest org.junit.jupiter junit-jupiter-engine + test org.mockito mockito-junit-jupiter - + org.springframework spring-test diff --git a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java index f0f5881..15de4a2 100644 --- a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java +++ b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java @@ -35,13 +35,13 @@ */ public class Log4jServletContextListener implements ServletContextListener { - private static final int DEFAULT_STOP_TIMEOUT = 30; + private static final int DEFAULT_STOP_TIMEOUT = 30; private static final TimeUnit DEFAULT_STOP_TIMEOUT_TIMEUNIT = TimeUnit.SECONDS; - private static final String KEY_STOP_TIMEOUT = "log4j.stop.timeout"; - private static final String KEY_STOP_TIMEOUT_TIMEUNIT = "log4j.stop.timeout.timeunit"; + private static final String KEY_STOP_TIMEOUT = "log4j.stop.timeout"; + private static final String KEY_STOP_TIMEOUT_TIMEUNIT = "log4j.stop.timeout.timeunit"; - private static final Logger LOGGER = StatusLogger.getLogger(); + private static final Logger LOGGER = StatusLogger.getLogger(); private ServletContext servletContext; private Log4jWebLifeCycle initializer; @@ -53,10 +53,10 @@ if ("true".equalsIgnoreCase(servletContext.getInitParameter( Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED))) { - throw new IllegalStateException("Do not use " + getClass().getSimpleName() + " when " - + Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED + " is true. Please use " - + Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + " instead of " - + getClass().getSimpleName() + "."); + throw new IllegalStateException("Do not use " + getClass().getSimpleName() + " when " + + Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED + " is true. Please use " + + Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + " instead of " + + getClass().getSimpleName() + "."); } this.initializer = WebLoggerContextUtils.getWebLifeCycle(this.servletContext); @@ -69,25 +69,25 @@ } @Override - public void contextDestroyed(final ServletContextEvent event) { - if (this.servletContext == null || this.initializer == null) { - LOGGER.warn("Context destroyed before it was initialized."); - return; - } - LOGGER.debug("Log4jServletContextListener ensuring that Log4j shuts down properly."); + public void contextDestroyed(final ServletContextEvent event) { + if (this.servletContext == null || this.initializer == null) { + LOGGER.warn("Context destroyed before it was initialized."); + return; + } + LOGGER.debug("Log4jServletContextListener ensuring that Log4j shuts down properly."); - this.initializer.clearLoggerContext(); // the application is finished - // shutting down now - if (initializer instanceof LifeCycle2) { - final String stopTimeoutStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT); - final long stopTimeout = Strings.isEmpty(stopTimeoutStr) ? DEFAULT_STOP_TIMEOUT - : Long.parseLong(stopTimeoutStr); - final String timeoutTimeUnitStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT_TIMEUNIT); - final TimeUnit timeoutTimeUnit = Strings.isEmpty(timeoutTimeUnitStr) ? DEFAULT_STOP_TIMEOUT_TIMEUNIT - : TimeUnit.valueOf(timeoutTimeUnitStr.toUpperCase(Locale.ROOT)); - ((LifeCycle2) this.initializer).stop(stopTimeout, timeoutTimeUnit); - } else { - this.initializer.stop(); - } - } + this.initializer.clearLoggerContext(); // the application is finished + // shutting down now + if (initializer instanceof LifeCycle2) { + final String stopTimeoutStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT); + final long stopTimeout = Strings.isEmpty(stopTimeoutStr) ? DEFAULT_STOP_TIMEOUT + : Long.parseLong(stopTimeoutStr); + final String timeoutTimeUnitStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT_TIMEUNIT); + final TimeUnit timeoutTimeUnit = Strings.isEmpty(timeoutTimeUnitStr) ? DEFAULT_STOP_TIMEOUT_TIMEUNIT + : TimeUnit.valueOf(timeoutTimeUnitStr.toUpperCase(Locale.ROOT)); + ((LifeCycle2) this.initializer).stop(stopTimeout, timeoutTimeUnit); + } else { + this.initializer.stop(); + } + } } diff --git a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java index 8a59dae..6c208df 100644 --- a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java +++ b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java @@ -71,6 +71,8 @@ chain.doFilter(request, response); } finally { this.initializer.clearLoggerContext(); + // Execute once per thread + request.removeAttribute(ALREADY_FILTERED_ATTRIBUTE); } } } diff --git a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java index 910670a..7ac4e77 100644 --- a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java +++ b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java @@ -44,12 +44,12 @@ @Override public void contextInitialized(final ServletContextEvent event) { LOGGER.debug(Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + - " ensuring that Log4j started up properly."); + " ensuring that Log4j started up properly."); servletContext = event.getServletContext(); if (null == servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) { - throw new IllegalStateException( - "Context did not contain required Log4jWebLifeCycle in the " - + Log4jWebSupport.SUPPORT_ATTRIBUTE + " attribute."); + throw new IllegalStateException( + "Context did not contain required Log4jWebLifeCycle in the " + + Log4jWebSupport.SUPPORT_ATTRIBUTE + " attribute."); } this.initializer = WebLoggerContextUtils.getWebLifeCycle(servletContext); } @@ -61,7 +61,7 @@ return; } LOGGER.debug(Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + - " ensuring that Log4j shuts down properly."); + " ensuring that Log4j shuts down properly."); this.initializer.clearLoggerContext(); // the application is finished // shutting down now diff --git a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java index 4fc5bc9..d58e887 100644 --- a/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java +++ b/log4j-jakarta-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java @@ -38,30 +38,30 @@ @Plugin(name = "Servlet", category = "Core", elementType = "appender", printObject = true) public class ServletAppender extends AbstractAppender { - public static class Builder> extends AbstractAppender.Builder - implements org.apache.logging.log4j.core.util.Builder { + public static class Builder> extends AbstractAppender.Builder + implements org.apache.logging.log4j.core.util.Builder { @PluginBuilderAttribute private boolean logThrowables; - @Override - public ServletAppender build() { - final String name = getName(); - if (name == null) { - LOGGER.error("No name provided for ServletAppender"); - } - final ServletContext servletContext = WebLoggerContextUtils.getServletContext(); - if (servletContext == null) { - LOGGER.error("No servlet context is available"); - return null; - } - Layout layout = getOrCreateLayout(); - if (!(layout instanceof StringLayout)) { - LOGGER.error("Layout must be a StringLayout to log to ServletContext"); - return null; - } - return new ServletAppender(name, layout, getFilter(), servletContext, isIgnoreExceptions(), logThrowables); - } + @Override + public ServletAppender build() { + final String name = getName(); + if (name == null) { + LOGGER.error("No name provided for ServletAppender"); + } + final ServletContext servletContext = WebLoggerContextUtils.getServletContext(); + if (servletContext == null) { + LOGGER.error("No servlet context is available"); + return null; + } + Layout layout = getOrCreateLayout(); + if (!(layout instanceof StringLayout)) { + LOGGER.error("Layout must be a StringLayout to log to ServletContext"); + return null; + } + return new ServletAppender(name, layout, getFilter(), servletContext, isIgnoreExceptions(), logThrowables); + } /** * Logs with {@link ServletContext#log(String, Throwable)} if true and with {@link ServletContext#log(String)} if false. @@ -79,7 +79,7 @@ this.logThrowables = logThrowables; } - } + } @PluginBuilderFactory public static > B newBuilder() { @@ -120,9 +120,9 @@ public static ServletAppender createAppender(final Layout layout, final Filter filter, final String name, final boolean ignoreExceptions) { // @formatter:off - return newBuilder().setFilter(filter).setIgnoreExceptions(ignoreExceptions).setLayout(layout).setName(name) - .build(); - // @formatter:on + return newBuilder().setFilter(filter).setIgnoreExceptions(ignoreExceptions).setLayout(layout).setName(name) + .build(); + // @formatter:on } } diff --git a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java index bfdae99..b3e908b 100644 --- a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java +++ b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java @@ -34,12 +34,12 @@ @ExtendWith(MockitoExtension.class) public class Log4jServletContextListenerTest { - /* event and servletContext are marked lenient because they aren't used in the - * testDestroyWithNoInit but are only accessed during initialization - */ - @Mock(lenient = true) - private ServletContextEvent event; - @Mock(lenient = true) + /* event and servletContext are marked lenient because they aren't used in the + * testDestroyWithNoInit but are only accessed during initialization + */ + @Mock(lenient = true) + private ServletContextEvent event; + @Mock(lenient = true) private ServletContext servletContext; @Mock private Log4jWebLifeCycle initializer; diff --git a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java index 0997f85..6cc54d9 100644 --- a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java +++ b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java @@ -72,9 +72,9 @@ @Test public void testDestroy() { - assertThrows(IllegalStateException.class, () -> { - this.filter.destroy(); - }); + assertThrows(IllegalStateException.class, () -> { + this.filter.destroy(); + }); } @Test @@ -93,6 +93,7 @@ then(chain).should().doFilter(same(request), same(response)); then(chain).shouldHaveNoMoreInteractions(); then(initializer).should().clearLoggerContext(); + then(request).should().removeAttribute(Log4jServletFilter.ALREADY_FILTERED_ATTRIBUTE); } @Test diff --git a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java index 76c5de3..37f105e 100644 --- a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java +++ b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java @@ -43,15 +43,15 @@ public void setUp(boolean mockInitializer) { this.listener = new Log4jShutdownOnContextDestroyedListener(); given(event.getServletContext()).willReturn(servletContext); - if (mockInitializer) { - given(servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) - .willReturn(initializer); + if (mockInitializer) { + given(servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) + .willReturn(initializer); } } - + @Test public void testInitAndDestroy() throws Exception { - setUp(true); + setUp(true); this.listener.contextInitialized(this.event); then(initializer).should(never()).start(); @@ -65,7 +65,7 @@ @Test public void testDestroy() throws Exception { - setUp(true); + setUp(true); this.listener.contextDestroyed(this.event); then(initializer).should(never()).clearLoggerContext(); @@ -74,10 +74,10 @@ @Test public void whenNoInitializerInContextTheContextInitializedShouldThrowAnException() { - setUp(false); - - assertThrows(IllegalStateException.class, () -> { - this.listener.contextInitialized(this.event); - }); + setUp(false); + + assertThrows(IllegalStateException.class, () -> { + this.listener.contextInitialized(this.event); + }); } } diff --git a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java index 808b98e..16eb74d 100644 --- a/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java +++ b/log4j-jakarta-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java @@ -45,11 +45,11 @@ @ExtendWith(MockitoExtension.class) public class Log4jWebInitializerImplTest { - /* Marking servletContext lenient because otherwise testCompositeLocationParameterWithEmptyUriListSetsDefaultConfiguration fails - * when null is passed in as the initial param because Mockito deciced null isn't a String rather than the absence of a string. - */ - @Mock(lenient = true) - private ServletContext servletContext; + /* Marking servletContext lenient because otherwise testCompositeLocationParameterWithEmptyUriListSetsDefaultConfiguration fails + * when null is passed in as the initial param because Mockito deciced null isn't a String rather than the absence of a string. + */ + @Mock(lenient = true) + private ServletContext servletContext; @Captor private ArgumentCaptor initializerCaptor; @Captor @@ -73,9 +73,9 @@ @Test public void testDeinitializeBeforeInitialize() { - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.stop(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.stop(); + }); } @Test @@ -214,9 +214,9 @@ then(servletContext).should().removeAttribute(eq(Log4jWebSupport.CONTEXT_ATTRIBUTE)); - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.start(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.start(); + }); } @Test @@ -248,9 +248,9 @@ given(servletContext.getResourcePaths("/WEB-INF/")).willReturn(null); assertNull(ContextAnchor.THREAD_CONTEXT.get(), "The context should be null."); - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.start(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.start(); + }); } @Test diff --git a/log4j-jcl/pom.xml b/log4j-jcl/pom.xml index fa4f2d1..3af18af 100644 --- a/log4j-jcl/pom.xml +++ b/log4j-jcl/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-jcl jar @@ -37,10 +37,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.hamcrest @@ -90,6 +92,8 @@ org.apache.logging.log4j.jcl + osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional + osgi.serviceloader;osgi.serviceloader=org.apache.commons.logging.LogFactory diff --git a/log4j-jdbc-dbcp2/pom.xml b/log4j-jdbc-dbcp2/pom.xml index 3f5605c..f4e1c37 100644 --- a/log4j-jdbc-dbcp2/pom.xml +++ b/log4j-jdbc-dbcp2/pom.xml @@ -11,7 +11,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -41,10 +41,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.apache.logging.log4j diff --git a/log4j-jmx-gui/pom.xml b/log4j-jmx-gui/pom.xml index 2ee0cc4..a852df1 100644 --- a/log4j-jmx-gui/pom.xml +++ b/log4j-jmx-gui/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-jmx-gui jar diff --git a/log4j-jpa/pom.xml b/log4j-jpa/pom.xml index 68aa84b..5236b83 100644 --- a/log4j-jpa/pom.xml +++ b/log4j-jpa/pom.xml @@ -11,7 +11,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -50,10 +50,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.apache.logging.log4j diff --git a/log4j-jpl/pom.xml b/log4j-jpl/pom.xml index 5d58d16..afb6ec5 100644 --- a/log4j-jpl/pom.xml +++ b/log4j-jpl/pom.xml @@ -20,7 +20,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 4.0.0 @@ -58,10 +58,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test diff --git a/log4j-jul/pom.xml b/log4j-jul/pom.xml index 919b81e..28174af 100644 --- a/log4j-jul/pom.xml +++ b/log4j-jul/pom.xml @@ -20,7 +20,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 4.0.0 @@ -63,6 +63,7 @@ junit junit + test diff --git a/log4j-kubernetes/pom.xml b/log4j-kubernetes/pom.xml index 3049dc7..1d1eece 100644 --- a/log4j-kubernetes/pom.xml +++ b/log4j-kubernetes/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-kubernetes jar @@ -52,10 +52,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test diff --git a/log4j-layout-template-json/pom.xml b/log4j-layout-template-json/pom.xml index 5090ab1..c36c29c 100644 --- a/log4j-layout-template-json/pom.xml +++ b/log4j-layout-template-json/pom.xml @@ -22,7 +22,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-layout-template-json diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java index e2a538a..5c31e00 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/ExceptionResolver.java @@ -71,8 +71,13 @@ * first. *

    * If a stringified stack trace truncation takes place, it will be indicated - * with suffix, which by default is set to the configured + * with a suffix, which by default is set to the configured * truncatedStringSuffix in the layout, unless explicitly provided. + * Every truncation suffix is prefixed with a newline. + *

    + * Stringified stack trace truncation operates in Caused by: and + * Suppressed: label blocks. That is, matchers are executed against + * each label in isolation. *

    * elementTemplate is an object describing the template to be used * while resolving the {@link StackTraceElement} array. If stringified @@ -138,7 +143,7 @@ * "stackTrace": { * "stringified": { * "truncation": { - * "suffix": ">", + * "suffix": "... [truncated]", * "pointMatcherStrings": ["at javax.servlet.http.HttpServlet.service"] * } * } diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java index 0dee8d0..92c2d33 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolver.java @@ -16,9 +16,7 @@ */ package org.apache.logging.log4j.layout.template.json.resolver; -import org.apache.logging.log4j.layout.template.json.util.TruncatingBufferedPrintWriter; -import org.apache.logging.log4j.layout.template.json.util.JsonWriter; -import org.apache.logging.log4j.layout.template.json.util.Recycler; +import org.apache.logging.log4j.layout.template.json.util.*; import java.util.List; import java.util.function.Supplier; @@ -31,7 +29,11 @@ */ final class StackTraceStringResolver implements StackTraceResolver { - private final Recycler writerRecycler; + private final Recycler srcWriterRecycler; + + private final Recycler dstWriterRecycler; + + private final Recycler sequencePointerRecycler; private final boolean truncationEnabled; @@ -49,9 +51,15 @@ final Supplier writerSupplier = () -> TruncatingBufferedPrintWriter.ofCapacity( context.getMaxStringByteCount()); - this.writerRecycler = context - .getRecyclerFactory() - .create(writerSupplier, TruncatingBufferedPrintWriter::close); + final RecyclerFactory recyclerFactory = context.getRecyclerFactory(); + this.srcWriterRecycler = + recyclerFactory.create( + writerSupplier, TruncatingBufferedPrintWriter::close); + this.dstWriterRecycler = + recyclerFactory.create( + writerSupplier, TruncatingBufferedPrintWriter::close); + this.sequencePointerRecycler = + recyclerFactory.create(CharSequencePointer::new); this.truncationEnabled = !truncationPointMatcherStrings.isEmpty() || !truncationPointMatcherRegexes.isEmpty(); @@ -66,8 +74,10 @@ return regexes .stream() .map(regex -> Pattern.compile( - "^.*(" + regex + ")(.*)$", - Pattern.MULTILINE | Pattern.DOTALL)) + ".*?" + // Make `.*` lazy with `?` suffix, since we want to find the _first_ match of `regex`. + regex + // Match the user input. + "(.*)", // Group that is to be truncated. + Pattern.DOTALL)) .collect(Collectors.toList()); } @@ -75,56 +85,236 @@ public void resolve( final Throwable throwable, final JsonWriter jsonWriter) { - final TruncatingBufferedPrintWriter writer = writerRecycler.acquire(); + final TruncatingBufferedPrintWriter srcWriter = srcWriterRecycler.acquire(); try { - throwable.printStackTrace(writer); - truncate(writer); - jsonWriter.writeString(writer.buffer(), 0, writer.position()); + throwable.printStackTrace(srcWriter); + final TruncatingBufferedPrintWriter dstWriter = truncate(srcWriter); + jsonWriter.writeString(dstWriter); } finally { - writerRecycler.release(writer); - } - } - - private void truncate(final TruncatingBufferedPrintWriter writer) { + srcWriterRecycler.release(srcWriter); + } + } + + private TruncatingBufferedPrintWriter truncate( + final TruncatingBufferedPrintWriter srcWriter) { // Short-circuit if truncation is not enabled. if (!truncationEnabled) { - return; - } + return srcWriter; + } + + // Allocate temporary buffers and truncate the input. + final TruncatingBufferedPrintWriter dstWriter = + dstWriterRecycler.acquire(); + try { + final CharSequencePointer sequencePointer = + sequencePointerRecycler.acquire(); + try { + truncate(srcWriter, dstWriter, sequencePointer); + } finally { + sequencePointerRecycler.release(sequencePointer); + } + } finally { + dstWriterRecycler.release(dstWriter); + } + return dstWriter; + + } + + private void truncate( + final TruncatingBufferedPrintWriter srcWriter, + final TruncatingBufferedPrintWriter dstWriter, + final CharSequencePointer sequencePointer) { + int startIndex = 0; + for (;;) { + + // Find the next label start, if present. + final int labeledLineStartIndex = + findLabeledLineStartIndex( + srcWriter, startIndex, srcWriter.length()); + final int endIndex = labeledLineStartIndex >= 0 + ? labeledLineStartIndex + : srcWriter.length(); + + // Copy up to the truncation point, if it matches. + final int truncationPointIndex = findTruncationPointIndex( + srcWriter, startIndex, endIndex, sequencePointer); + if (truncationPointIndex > 0) { + dstWriter.append(srcWriter, startIndex, truncationPointIndex); + dstWriter.append(System.lineSeparator()); + dstWriter.append(truncationSuffix); + } + + // Otherwise, copy the entire labeled block. + else { + dstWriter.append(srcWriter, startIndex, endIndex); + } + + // Copy the label to avoid stepping over it again. + if (labeledLineStartIndex > 0) { + dstWriter.append(System.lineSeparator()); + startIndex = labeledLineStartIndex; + for (;;) { + final char c = srcWriter.charAt(startIndex++); + dstWriter.append(c); + if (c == ':') { + break; + } + } + } + + // Otherwise, the source is exhausted, stop. + else { + break; + } + + } + } + + private int findTruncationPointIndex( + final TruncatingBufferedPrintWriter writer, + final int startIndex, + final int endIndex, + final CharSequencePointer sequencePointer) { // Check for string matches. // noinspection ForLoopReplaceableByForEach (avoid iterator allocation) for (int i = 0; i < truncationPointMatcherStrings.size(); i++) { final String matcher = truncationPointMatcherStrings.get(i); - final int matchIndex = writer.indexOf(matcher); + final int matchIndex = findMatchingIndex( + matcher, writer, startIndex, endIndex); if (matchIndex > 0) { - final int truncationPointIndex = matchIndex + matcher.length(); - truncate(writer, truncationPointIndex); - return; + // No need for `Math.addExact()`, since we have a match: + return matchIndex + matcher.length(); } } // Check for regex matches. + CharSequence sequence; + if (startIndex == 0 && endIndex == writer.length()) { + sequence = writer; + } else { + sequencePointer.reset(writer, startIndex, writer.length()); + sequence = sequencePointer; + } // noinspection ForLoopReplaceableByForEach (avoid iterator allocation) for (int i = 0; i < groupedTruncationPointMatcherRegexes.size(); i++) { final Pattern pattern = groupedTruncationPointMatcherRegexes.get(i); - final Matcher matcher = pattern.matcher(writer); + final Matcher matcher = pattern.matcher(sequence); final boolean matched = matcher.matches(); if (matched) { final int lastGroup = matcher.groupCount(); - final int truncationPointIndex = matcher.start(lastGroup); - truncate(writer, truncationPointIndex); - return; - } - } - - } - - private void truncate( - final TruncatingBufferedPrintWriter writer, - final int index) { - writer.position(index); - writer.print(truncationSuffix); + return matcher.start(lastGroup); + } + } + + // No matches. + return -1; + + } + + private static int findLabeledLineStartIndex( + final CharSequence buffer, + final int startIndex, + final int endIndex) { + // Note that the index arithmetic in this method is not guarded. + // That is, there are no `Math.addExact()` or `Math.subtractExact()` usages. + // Since we know a priori that we are already operating within buffer limits. + for (int bufferIndex = startIndex; bufferIndex < endIndex;) { + + // Find the next line start, if exists. + final int lineStartIndex = findLineStartIndex(buffer, bufferIndex, endIndex); + if (lineStartIndex < 0) { + break; + } + bufferIndex = lineStartIndex; + + // Skip tabs. + while (bufferIndex < endIndex && '\t' == buffer.charAt(bufferIndex)) { + bufferIndex++; + } + + // Search for the `Caused by: ` occurrence. + if (bufferIndex < (endIndex - 11) && + buffer.charAt(bufferIndex) == 'C' && + buffer.charAt(bufferIndex + 1) == 'a' && + buffer.charAt(bufferIndex + 2) == 'u' && + buffer.charAt(bufferIndex + 3) == 's' && + buffer.charAt(bufferIndex + 4) == 'e' && + buffer.charAt(bufferIndex + 5) == 'd' && + buffer.charAt(bufferIndex + 6) == ' ' && + buffer.charAt(bufferIndex + 7) == 'b' && + buffer.charAt(bufferIndex + 8) == 'y' && + buffer.charAt(bufferIndex + 9) == ':' && + buffer.charAt(bufferIndex + 10) == ' ') { + return lineStartIndex; + } + + // Search for the `Suppressed: ` occurrence. + else if (bufferIndex < (endIndex - 12) && + buffer.charAt(bufferIndex) == 'S' && + buffer.charAt(bufferIndex + 1) == 'u' && + buffer.charAt(bufferIndex + 2) == 'p' && + buffer.charAt(bufferIndex + 3) == 'p' && + buffer.charAt(bufferIndex + 4) == 'r' && + buffer.charAt(bufferIndex + 5) == 'e' && + buffer.charAt(bufferIndex + 6) == 's' && + buffer.charAt(bufferIndex + 7) == 's' && + buffer.charAt(bufferIndex + 8) == 'e' && + buffer.charAt(bufferIndex + 9) == 'd' && + buffer.charAt(bufferIndex + 10) == ':' && + buffer.charAt(bufferIndex + 11) == ' ') { + return lineStartIndex; + } + + } + return -1; + } + + private static int findLineStartIndex( + final CharSequence buffer, + final int startIndex, + final int endIndex) { + char prevChar = '-'; + for (int i = startIndex; i <= endIndex; i++) { + if (prevChar == '\n') { + return i; + } + prevChar = buffer.charAt(i); + } + return -1; + } + + private static int findMatchingIndex( + final CharSequence matcher, + final CharSequence buffer, + final int bufferStartIndex, + final int bufferEndIndex) { + + // Note that the index arithmetic in this method is not guarded. + // That is, there are no `Math.addExact()` or `Math.subtractExact()` usages. + // Since we know a priori that we are already operating within buffer limits. + + // While searching for an input of length `n`, no need to traverse the last `n-1` characters. + final int effectiveBufferEndIndex = bufferEndIndex - matcher.length() + 1; + + // Perform the search. + for (int bufferIndex = bufferStartIndex; bufferIndex <= effectiveBufferEndIndex; bufferIndex++) { + boolean found = true; + for (int matcherIndex = 0; matcherIndex < matcher.length(); matcherIndex++) { + final char matcherChar = matcher.charAt(matcherIndex); + final char bufferChar = buffer.charAt(bufferIndex + matcherIndex); + if (matcherChar != bufferChar) { + found = false; + break; + } + } + if (found) { + return bufferIndex; + } + } + return -1; + } } diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointer.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointer.java new file mode 100644 index 0000000..0495e7c --- /dev/null +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointer.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.layout.template.json.util; + +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * A {@link CharSequence} wrapper that allows mutation of the pointed delegate sequence. + */ +public final class CharSequencePointer implements CharSequence { + + private CharSequence delegate; + + private int startIndex; + + private int length = -1; + + public void reset( + final CharSequence delegate, + final int startIndex, + final int endIndex) { + + // Check & set the delegate. + Objects.requireNonNull(delegate, "delegate"); + this.delegate = delegate; + + // Check & set the start. + if (startIndex < 0) { + throw new IndexOutOfBoundsException("invalid start: " + startIndex); + } + + // Check & set length. + if (endIndex > delegate.length()) { + throw new IndexOutOfBoundsException("invalid end: " + endIndex); + } + this.length = Math.subtractExact(endIndex, startIndex); + if (length < 0) { + throw new IndexOutOfBoundsException("invalid length: " + length); + } + + // Set fields. + this.delegate = delegate; + this.startIndex = startIndex; + + } + + @Override + public int length() { + requireReset(); + return length; + } + + @Override + public char charAt(final int startIndex) { + requireReset(); + final int delegateStartIndex = Math.addExact(this.startIndex, startIndex); + return delegate.charAt(delegateStartIndex); + } + + @Override + public CharSequence subSequence(final int startIndex, final int endIndex) { + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); + } + + @Override + public IntStream chars() { + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); + } + + @Override + public IntStream codePoints() { + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); + } + + @Override + public String toString() { + requireReset(); + final int endIndex = Math.addExact(startIndex, length); + return delegate.toString().substring(startIndex, endIndex); + } + + private void requireReset() { + if (length < 0) { + throw new IllegalStateException("pointer must be reset first"); + } + } + +} diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java index 7e9aa3c..7f30ab3 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedPrintWriter.java @@ -59,11 +59,6 @@ return writer.truncated(); } - public int indexOf(final CharSequence seq) { - Objects.requireNonNull(seq, "seq"); - return writer.indexOf(seq); - } - @Override public int length() { return writer.length(); @@ -72,6 +67,18 @@ @Override public char charAt(final int index) { return writer.charAt(index); + } + + @Override + public PrintWriter append(final CharSequence seq) { + writer.append(seq); + return this; + } + + @Override + public PrintWriter append(final CharSequence seq, final int startIndex, final int endIndex) { + writer.append(seq, startIndex, endIndex); + return this; } @Override diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java index 1b88f12..62757a3 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriter.java @@ -18,6 +18,7 @@ import java.io.Writer; import java.util.Objects; +import java.util.stream.IntStream; final class TruncatingBufferedWriter extends Writer implements CharSequence { @@ -203,41 +204,9 @@ } - int indexOf(final CharSequence seq) { - - // Short-circuit if there is nothing to match. - final int seqLength = seq.length(); - if (seqLength == 0) { - return 0; - } - - // Short-circuit if the given input is longer than the buffer. - if (seqLength > position) { - return -1; - } - - // Perform the search. - for (int bufferIndex = 0; bufferIndex < position; bufferIndex++) { - boolean found = true; - for (int seqIndex = 0; seqIndex < seqLength; seqIndex++) { - final char s = seq.charAt(seqIndex); - final char b = buffer[bufferIndex + seqIndex]; - if (s != b) { - found = false; - break; - } - } - if (found) { - return bufferIndex; - } - } - return -1; - - } - @Override public int length() { - return position + 1; + return position; } @Override @@ -247,7 +216,20 @@ @Override public String subSequence(final int startIndex, final int endIndex) { - return new String(buffer, startIndex, endIndex - startIndex); + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); + } + + @Override + public IntStream chars() { + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); + } + + @Override + public IntStream codePoints() { + throw new UnsupportedOperationException( + "operation requires allocation, contradicting with the purpose of the class"); } @Override diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java index 2cd2518..f0b8b86 100644 --- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java +++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/JsonTemplateLayoutTest.java @@ -34,31 +34,14 @@ import org.apache.logging.log4j.core.net.Severity; import org.apache.logging.log4j.core.time.MutableInstant; import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout.EventTemplateAdditionalField; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolver; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolverContext; -import org.apache.logging.log4j.layout.template.json.resolver.EventResolverFactory; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolver; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverConfig; -import org.apache.logging.log4j.layout.template.json.resolver.TemplateResolverFactory; +import org.apache.logging.log4j.layout.template.json.resolver.*; import org.apache.logging.log4j.layout.template.json.util.JsonWriter; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.MessageFactory; -import org.apache.logging.log4j.message.ObjectMessage; -import org.apache.logging.log4j.message.ParameterizedMessageFactory; -import org.apache.logging.log4j.message.ReusableMessageFactory; -import org.apache.logging.log4j.message.SimpleMessage; -import org.apache.logging.log4j.message.StringMapMessage; +import org.apache.logging.log4j.message.*; import org.apache.logging.log4j.test.AvailablePortFinder; import org.apache.logging.log4j.util.Strings; -import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; -import java.io.ByteArrayOutputStream; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.math.BigDecimal; import java.net.ServerSocket; import java.net.Socket; @@ -67,11 +50,7 @@ import java.time.Instant; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; @@ -81,6 +60,7 @@ import static org.apache.logging.log4j.layout.template.json.TestHelpers.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @SuppressWarnings("DoubleBraceInitialization") class JsonTemplateLayoutTest { @@ -770,85 +750,17 @@ } - private static final class NonAsciiUtf8MethodNameContainingException extends RuntimeException { - - public static final long serialVersionUID = 0; - - private static final String NON_ASCII_UTF8_TEXT = "அஆஇฬ๘"; - - private static final NonAsciiUtf8MethodNameContainingException INSTANCE = - createInstance(); - - private static NonAsciiUtf8MethodNameContainingException createInstance() { - try { - throwException_அஆஇฬ๘(); - throw new IllegalStateException("should not have reached here"); - } catch (final NonAsciiUtf8MethodNameContainingException exception) { - return exception; - } - } - - @SuppressWarnings("NonAsciiCharacters") - private static void throwException_அஆஇฬ๘() { - throw new NonAsciiUtf8MethodNameContainingException( - "exception with non-ASCII UTF-8 method name"); - } - - private NonAsciiUtf8MethodNameContainingException(final String message) { - super(message); - } - - } - - @Test - void test_exception_with_nonAscii_utf8_method_name() { + @Test + void test_event_template_additional_fields() { // Create the log event. final SimpleMessage message = new SimpleMessage("Hello, World!"); - final RuntimeException exception = NonAsciiUtf8MethodNameContainingException.INSTANCE; - final LogEvent logEvent = Log4jLogEvent - .newBuilder() - .setLoggerName(LOGGER_NAME) - .setLevel(Level.ERROR) - .setMessage(message) - .setThrown(exception) - .build(); - - // Create the event template. - final String eventTemplate = writeJson(asMap( - "ex_stacktrace", asMap( - "$resolver", "exception", - "field", "stackTrace", - "stringified", true))); - - // Create the layout. - final JsonTemplateLayout layout = JsonTemplateLayout - .newBuilder() - .setConfiguration(CONFIGURATION) - .setStackTraceEnabled(true) - .setEventTemplate(eventTemplate) - .build(); - - // Check the serialized event. - usingSerializedLogEventAccessor(layout, logEvent, accessor -> - assertThat(accessor.getString("ex_stacktrace")) - .contains(NonAsciiUtf8MethodNameContainingException.NON_ASCII_UTF8_TEXT)); - - } - - @Test - void test_event_template_additional_fields() { - - // Create the log event. - final SimpleMessage message = new SimpleMessage("Hello, World!"); - final RuntimeException exception = NonAsciiUtf8MethodNameContainingException.INSTANCE; final Level level = Level.ERROR; final LogEvent logEvent = Log4jLogEvent .newBuilder() .setLoggerName(LOGGER_NAME) .setLevel(level) .setMessage(message) - .setThrown(exception) .build(); // Create the event template. @@ -971,8 +883,7 @@ // Verify the test case. usingSerializedLogEventAccessor(layout, logEvent, accessor -> testCase.forEach((key, expectedValue) -> - Assertions - .assertThat(accessor.getObject(key)) + assertThat(accessor.getObject(key)) .describedAs("key=%s", key) .isEqualTo(expectedValue))); @@ -1140,161 +1051,6 @@ } @Test - void test_stringified_exception_resolver_with_maxStringLength() { - - // Create the event template. - final String eventTemplate = writeJson(asMap( - "stackTrace", asMap( - "$resolver", "exception", - "field", "stackTrace", - "stringified", true))); - - // Create the layout. - final int maxStringLength = eventTemplate.length(); - final JsonTemplateLayout layout = JsonTemplateLayout - .newBuilder() - .setConfiguration(CONFIGURATION) - .setEventTemplate(eventTemplate) - .setMaxStringLength(maxStringLength) - .setStackTraceEnabled(true) - .build(); - - // Create the log event. - final SimpleMessage message = new SimpleMessage("foo"); - final LogEvent logEvent = Log4jLogEvent - .newBuilder() - .setLoggerName(LOGGER_NAME) - .setMessage(message) - .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE) - .build(); - - // Check the serialized event. - usingSerializedLogEventAccessor(layout, logEvent, accessor -> { - final int expectedLength = maxStringLength + - JsonTemplateLayoutDefaults.getTruncatedStringSuffix().length(); - assertThat(accessor.getString("stackTrace").length()).isEqualTo(expectedLength); - }); - - } - - @Test - void test_stack_trace_truncation() { - - // Create the exception to be logged. - final Exception childError = - new Exception("unique child exception message"); - final Exception parentError = - new Exception("unique parent exception message", childError); - - // Create the event template. - final String truncationSuffix = "~"; - final String eventTemplate = writeJson(asMap( - // Raw exception. - "ex", asMap( - "$resolver", "exception", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", true)), - // Exception matcher using strings. - "stringMatchedEx", asMap( - "$resolver", "exception", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", asMap( - "truncation", asMap( - "suffix", truncationSuffix, - "pointMatcherStrings", Arrays.asList( - "this string shouldn't match with anything", - parentError.getMessage()))))), - // Exception matcher using regexes. - "regexMatchedEx", asMap( - "$resolver", "exception", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", asMap( - "truncation", asMap( - "suffix", truncationSuffix, - "pointMatcherRegexes", Arrays.asList( - "this string shouldn't match with anything", - parentError - .getMessage() - .replace("unique", "[xu]n.que")))))), - // Raw exception root cause. - "rootEx", asMap( - "$resolver", "exceptionRootCause", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", true)), - // Exception root cause matcher using strings. - "stringMatchedRootEx", asMap( - "$resolver", "exceptionRootCause", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", asMap( - "truncation", asMap( - "suffix", truncationSuffix, - "pointMatcherStrings", Arrays.asList( - "this string shouldn't match with anything", - childError.getMessage()))))), - // Exception root cause matcher using regexes. - "regexMatchedRootEx", asMap( - "$resolver", "exceptionRootCause", - "field", "stackTrace", - "stackTrace", asMap( - "stringified", asMap( - "truncation", asMap( - "suffix", truncationSuffix, - "pointMatcherRegexes", Arrays.asList( - "this string shouldn't match with anything", - childError - .getMessage() - .replace("unique", "[xu]n.que")))))))); - - // Create the layout. - final JsonTemplateLayout layout = JsonTemplateLayout - .newBuilder() - .setConfiguration(CONFIGURATION) - .setEventTemplate(eventTemplate) - .setStackTraceEnabled(true) - .build(); - - // Create the log event. - final LogEvent logEvent = Log4jLogEvent - .newBuilder() - .setLoggerName(LOGGER_NAME) - .setThrown(parentError) - .build(); - - // Check the serialized event. - final String expectedMatchedExEnd = - parentError.getMessage() + truncationSuffix; - final String expectedMatchedRootExEnd = - childError.getMessage() + truncationSuffix; - usingSerializedLogEventAccessor(layout, logEvent, accessor -> { - - // Check the serialized exception. - assertThat(accessor.getString("ex")) - .doesNotEndWith(expectedMatchedExEnd) - .doesNotEndWith(expectedMatchedRootExEnd); - assertThat(accessor.getString("stringMatchedEx")) - .endsWith(expectedMatchedExEnd); - assertThat(accessor.getString("regexMatchedEx")) - .endsWith(expectedMatchedExEnd); - - // Check the serialized exception root cause. - assertThat(accessor.getString("rootEx")) - .doesNotEndWith(expectedMatchedExEnd) - .doesNotEndWith(expectedMatchedRootExEnd); - assertThat(accessor.getString("stringMatchedRootEx")) - .endsWith(expectedMatchedRootExEnd); - assertThat(accessor.getString("regexMatchedRootEx")) - .endsWith(expectedMatchedRootExEnd); - - }); - - } - - @Test void test_inline_stack_trace_element_template() { // Create the event template. @@ -1326,9 +1082,9 @@ // Check the serialized log event. final String expectedClassName = JsonTemplateLayoutTest.class.getCanonicalName(); - usingSerializedLogEventAccessor(layout, logEvent, accessor -> Assertions - .assertThat(accessor.getList("stackTrace", String.class)) - .contains(expectedClassName)); + usingSerializedLogEventAccessor(layout, logEvent, accessor -> + assertThat(accessor.getList("stackTrace", String.class)) + .contains(expectedClassName)); } @@ -1355,9 +1111,9 @@ .build(); // Check the serialized log event. - usingSerializedLogEventAccessor(layout, logEvent, accessor -> Assertions - .assertThat(accessor.getString("customField")) - .matches("CustomValue-[0-9]+")); + usingSerializedLogEventAccessor(layout, logEvent, accessor -> + assertThat(accessor.getString("customField")) + .matches("CustomValue-[0-9]+")); } @@ -1422,7 +1178,6 @@ .newBuilder() .setLoggerName(LOGGER_NAME) .setMessage(message) - .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE) .build(); // Check the serialized event. @@ -1814,9 +1569,7 @@ final String expectedSerializedLogEventJson = "{}" + JsonTemplateLayoutDefaults.getEventDelimiter(); final String actualSerializedLogEventJson = layout.toSerializable(logEvent); - Assertions - .assertThat(actualSerializedLogEventJson) - .isEqualTo(expectedSerializedLogEventJson); + assertThat(actualSerializedLogEventJson).isEqualTo(expectedSerializedLogEventJson); } @@ -1847,8 +1600,7 @@ .build(); // Check the serialized event. - Assertions - .assertThatThrownBy(() -> layout.toSerializable(logEvent)) + assertThatThrownBy(() -> layout.toSerializable(logEvent)) .isInstanceOf(StackOverflowError.class); } diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java new file mode 100644 index 0000000..6c93b19 --- /dev/null +++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/resolver/StackTraceStringResolverTest.java @@ -0,0 +1,657 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.layout.template.json.resolver; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.impl.Log4jLogEvent; +import org.apache.logging.log4j.layout.template.json.JsonTemplateLayout; +import org.apache.logging.log4j.layout.template.json.JsonTemplateLayoutDefaults; +import org.assertj.core.api.AbstractStringAssert; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.math.BigDecimal; +import java.net.ServerSocket; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.apache.logging.log4j.layout.template.json.TestHelpers.*; +import static org.assertj.core.api.Assertions.assertThat; + +class StackTraceStringResolverTest { + + //////////////////////////////////////////////////////////////////////////// + // exceptions ////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + + // Below we create arbitrary exceptions containing stack entries from non-Log4j packages. + // Non-Log4j package origin is needed to avoid the truncation (e.g., `... 58 more`) done by `Throwable#printStackTrace()`. + + private static final String EXCEPTION_REGEX_FLAGS = "(?s)"; // DOTALL + + private static final String TRUNCATION_SUFFIX = ""; + + @SuppressWarnings({"BigDecimalMethodWithoutRoundingCalled", "ResultOfMethodCallIgnored"}) + private static Throwable exception1() { + return catchException(() -> BigDecimal.ONE.divide(BigDecimal.ZERO)); + } + + private static String exception1Regex(final boolean truncated) { + final String truncationCorrectionRegex = truncationSuffixRegexOr(truncated, ".divide\\("); + return "java.lang.ArithmeticException: Division by zero\r?\n" + + "\t+at java.math.BigDecimal" + truncationCorrectionRegex + ".*"; + } + + @SuppressWarnings("ConstantConditions") + private static Throwable exception2() { + return catchException(() -> Collections.emptyList().add(0)); + } + + private static String exception2Regex(final boolean truncated) { + final String truncationCorrectionRegex = truncationSuffixRegexOr(truncated, ".add\\("); + return "java.lang.UnsupportedOperationException\r?\n" + + "\t+at java.util.AbstractList" + truncationCorrectionRegex + ".*"; + } + + private static Throwable exception3() { + return catchException(() -> new ServerSocket(-1)); + } + + private static String exception3Regex(final boolean truncated) { + final String truncationCorrectionRegex = truncationSuffixRegexOr(truncated, "."); + return "java.lang.IllegalArgumentException: Port value out of range: -1\r?\n" + + "\t+at java.net.ServerSocket" + truncationCorrectionRegex + ".*"; + } + + private static String truncationSuffixRegexOr(final boolean truncated, final String fallback) { + return truncated + ? ("\r?\n" + TRUNCATION_SUFFIX) + : fallback; + } + + private static Throwable catchException(ThrowingRunnable runnable) { + try { + runnable.run(); + throw new AssertionError("should not have reached here"); + } catch (Throwable error) { + return error; + } + } + + @FunctionalInterface + private interface ThrowingRunnable { + + void run() throws Throwable; + + } + + @Test + void exception1_regex_should_match() { + final Throwable error = exception1(); + final String stackTrace = stackTrace(error); + final String regex = exception1Regex(false); + assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex); + } + + @Test + void exception2_regex_should_match() { + final Throwable error = exception2(); + final String stackTrace = stackTrace(error); + final String regex = exception2Regex(false); + assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex); + } + + @Test + void exception3_regex_should_match() { + final Throwable error = exception3(); + final String stackTrace = stackTrace(error); + final String regex = exception3Regex(false); + assertThat(stackTrace).matches(EXCEPTION_REGEX_FLAGS + regex); + } + + private static String stackTrace(final Throwable throwable) { + final String encoding = "UTF-8"; + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + PrintStream printStream = new PrintStream(outputStream, false, encoding)) { + throwable.printStackTrace(printStream); + printStream.flush(); + return outputStream.toString(encoding); + } catch (Exception error) { + throw new RuntimeException(error); + } + } + + //////////////////////////////////////////////////////////////////////////// + // abstract tests ////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + + private static abstract class AbstractTestCases { + + private final boolean truncated; + + AbstractTestCases(boolean truncated) { + this.truncated = truncated; + } + + private String exception1Regex() { + return StackTraceStringResolverTest.exception1Regex(truncated); + } + + private String exception2Regex() { + return StackTraceStringResolverTest.exception2Regex(truncated); + } + + private String exception3Regex() { + return StackTraceStringResolverTest.exception3Regex(truncated); + } + + @Test + void exception_should_be_resolved() { + final Throwable exception = exception1(); + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + exception1Regex(); + assertSerializedException(exception, serializedExceptionRegex); + } + + @Test + void exception_with_cause_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable cause = exception2(); + exception.initCause(cause); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\nCaused by: " + exception2Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_causes_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable cause1 = exception2(); + final Throwable cause2 = exception3(); + exception.initCause(cause1); + cause1.initCause(cause2); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\nCaused by: " + exception2Regex() + + "\nCaused by: " + exception3Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_suppressed_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable suppressed = exception2(); + exception.addSuppressed(suppressed); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\n\tSuppressed: " + exception2Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_suppresseds_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable suppressed1 = exception2(); + final Throwable suppressed2 = exception3(); + exception.addSuppressed(suppressed1); + exception.addSuppressed(suppressed2); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\n\tSuppressed: " + exception2Regex() + + "\n\tSuppressed: " + exception3Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_cause_and_suppressed_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable suppressed = exception2(); + final Throwable cause = exception3(); + exception.addSuppressed(suppressed); + exception.initCause(cause); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\n\tSuppressed: " + exception2Regex() + + "\nCaused by: " + exception3Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_cause_with_suppressed_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable cause = exception2(); + final Throwable suppressed = exception3(); + exception.initCause(cause); + cause.addSuppressed(suppressed); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\nCaused by: " + exception2Regex() + + "\n\tSuppressed: " + exception3Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + @Test + void exception_with_suppressed_with_cause_should_be_resolved() { + + // Create the exception. + final Throwable exception = exception1(); + final Throwable suppressed = exception2(); + final Throwable cause = exception3(); + exception.addSuppressed(suppressed); + suppressed.initCause(cause); + + // Check the serialized exception. + final String serializedExceptionRegex = EXCEPTION_REGEX_FLAGS + + exception1Regex() + + "\n\tSuppressed: " + exception2Regex() + + "\n\tCaused by: " + exception3Regex(); + assertSerializedException(exception, serializedExceptionRegex); + + } + + abstract void assertSerializedException( + final Throwable exception, + final String regex); + + private static void assertSerializedException( + final Map exceptionResolverTemplate, + final Throwable exception, + final Consumer> serializedExceptionAsserter) { + + // Create the event template. + final String eventTemplate = writeJson(asMap("output", exceptionResolverTemplate)); + + // Create the layout. + final JsonTemplateLayout layout = JsonTemplateLayout + .newBuilder() + .setConfiguration(CONFIGURATION) + .setEventTemplate(eventTemplate) + .build(); + + // Create the log event. + final LogEvent logEvent = Log4jLogEvent + .newBuilder() + .setThrown(exception) + .build(); + + // Check the serialized event. + usingSerializedLogEventAccessor(layout, logEvent, accessor -> { + AbstractStringAssert serializedExceptionAssert = assertThat(accessor.getString("output")); + serializedExceptionAsserter.accept(serializedExceptionAssert); + }); + + } + + } + + //////////////////////////////////////////////////////////////////////////// + // tests without truncation //////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + + @Nested + class WithoutTruncation extends AbstractTestCases { + + WithoutTruncation() { + super(false); + } + + @Override + void assertSerializedException(final Throwable exception, final String regex) { + assertSerializedExceptionWithoutTruncation(exception, regex); + } + + private void assertSerializedExceptionWithoutTruncation( + final Throwable exception, + final String regex) { + + // Create the event template. + final Map exceptionResolverTemplate = asMap( + "$resolver", "exception", + "field", "stackTrace", + "stackTrace", asMap("stringified", true)); + + // Check the serialized event. + AbstractTestCases.assertSerializedException( + exceptionResolverTemplate, + exception, + serializedExceptionAssert -> serializedExceptionAssert.matches(regex)); + + } + + @Test + void JsonWriter_maxStringLength_should_work() { + + // Create the event template. + final String eventTemplate = writeJson(asMap( + "ex", asMap( + "$resolver", "exception", + "field", "stackTrace", + "stringified", true))); + + // Create the layout. + final int maxStringLength = eventTemplate.length(); + final JsonTemplateLayout layout = JsonTemplateLayout + .newBuilder() + .setConfiguration(CONFIGURATION) + .setEventTemplate(eventTemplate) + .setMaxStringLength(maxStringLength) + .setStackTraceEnabled(true) + .build(); + + // Create the log event. + Throwable exception = exception1(); + final LogEvent logEvent = Log4jLogEvent + .newBuilder() + .setThrown(exception) + .build(); + + // Check the serialized event. + usingSerializedLogEventAccessor(layout, logEvent, accessor -> { + final int expectedLength = maxStringLength + + JsonTemplateLayoutDefaults.getTruncatedStringSuffix().length(); + assertThat(accessor.getString("ex").length()).isEqualTo(expectedLength); + }); + + } + + } + + //////////////////////////////////////////////////////////////////////////// + // tests with `truncationPointMatcherStrings` ////////////////////////////// + //////////////////////////////////////////////////////////////////////////// + + @Nested + class WithTruncation extends AbstractTestCases { + + WithTruncation() { + super(true); + } + + @Override + void assertSerializedException(final Throwable exception, final String regex) { + assertSerializedExceptionWithStringTruncation(exception, regex); + } + + private void assertSerializedExceptionWithStringTruncation( + final Throwable exception, + final String regex) { + + // Create the event template. + final List pointMatcherStrings = pointMatcherStrings(); + final Map exceptionResolverTemplate = asMap( + "$resolver", "exception", + "field", "stackTrace", + "stackTrace", asMap("stringified", asMap( + "truncation", asMap( + "suffix", TRUNCATION_SUFFIX, + "pointMatcherStrings", pointMatcherStrings)))); + + // Check the serialized event. + AbstractTestCases.assertSerializedException( + exceptionResolverTemplate, + exception, + serializedExceptionAssert -> serializedExceptionAssert.matches(regex)); + + } + + private List pointMatcherStrings() { + final Throwable exception1 = exception1(); + final Throwable exception2 = exception2(); + final Throwable exception3 = exception3(); + return Stream + .of(exception1, exception2, exception3) + .map(this::pointMatcherString) + .collect(Collectors.toList()); + } + + @Test + void point_matchers_should_work() { + + // Create the exception to be logged. + final Throwable parentError = exception1(); + final Throwable childError = exception3(); + parentError.initCause(childError); + + // Create the event template. + final String eventTemplate = writeJson(asMap( + + // Raw exception + "ex", asMap( + "$resolver", "exception", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", true)), + + // Exception matcher using strings + "stringMatchedEx", asMap( + "$resolver", "exception", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", asMap( + "truncation", asMap( + "suffix", TRUNCATION_SUFFIX, + "pointMatcherStrings", Arrays.asList( + "this string shouldn't match with anything", + pointMatcherString(parentError)))))), + + // Exception matcher using regexes + "regexMatchedEx", asMap( + "$resolver", "exception", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", asMap( + "truncation", asMap( + "suffix", TRUNCATION_SUFFIX, + "pointMatcherRegexes", Arrays.asList( + "this string shouldn't match with anything", + pointMatcherRegex(parentError)))))), + + // Raw exception root cause + "rootEx", asMap( + "$resolver", "exceptionRootCause", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", true)), + + // Exception root cause matcher using strings + "stringMatchedRootEx", asMap( + "$resolver", "exceptionRootCause", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", asMap( + "truncation", asMap( + "suffix", TRUNCATION_SUFFIX, + "pointMatcherStrings", Arrays.asList( + "this string shouldn't match with anything", + pointMatcherString(childError)))))), + + // Exception root cause matcher using regexes + "regexMatchedRootEx", asMap( + "$resolver", "exceptionRootCause", + "field", "stackTrace", + "stackTrace", asMap( + "stringified", asMap( + "truncation", asMap( + "suffix", TRUNCATION_SUFFIX, + "pointMatcherRegexes", Arrays.asList( + "this string shouldn't match with anything", + pointMatcherRegex(childError)))))))); + + // Create the layout. + final JsonTemplateLayout layout = JsonTemplateLayout + .newBuilder() + .setConfiguration(CONFIGURATION) + .setEventTemplate(eventTemplate) + .build(); + + // Create the log event. + final LogEvent logEvent = Log4jLogEvent + .newBuilder() + .setThrown(parentError) + .build(); + + // Check the serialized event. + usingSerializedLogEventAccessor(layout, logEvent, accessor -> { + + // Check the raw parent exception. + final String exPattern = EXCEPTION_REGEX_FLAGS + + exception1Regex(false) + + "\nCaused by: " + exception3Regex(false); + assertThat(accessor.getString("ex")).matches(exPattern); + + // Check the matcher usage on parent exception. + final String matchedExPattern = EXCEPTION_REGEX_FLAGS + + exception1Regex(true) + + "\nCaused by: " + exception3Regex(false); + assertThat(accessor.getString("stringMatchedEx")).matches(matchedExPattern); + assertThat(accessor.getString("regexMatchedEx")).matches(matchedExPattern); + + // Check the raw child exception. + final String rootExPattern = EXCEPTION_REGEX_FLAGS + + exception3Regex(false); + assertThat(accessor.getString("rootEx")).matches(rootExPattern); + + // Check the matcher usage on child exception. + final String matchedRootExPattern = EXCEPTION_REGEX_FLAGS + + exception3Regex(true); + assertThat(accessor.getString("stringMatchedRootEx")).matches(matchedRootExPattern); + assertThat(accessor.getString("regexMatchedRootEx")).matches(matchedRootExPattern); + + }); + + } + + private String pointMatcherString(Throwable exception) { + final StackTraceElement stackTraceElement = exception.getStackTrace()[0]; + final String className = stackTraceElement.getClassName(); + return "at " + className; + } + + private String pointMatcherRegex(Throwable exception) { + String string = pointMatcherString(exception); + return matchingRegex(string); + } + + /** + * @return a regex matching the given input + */ + private String matchingRegex(String string) { + return "[" + string.charAt(0) + "]" + Pattern.quote(string.substring(1)); + } + + } + + @Test + void nonAscii_utf8_method_name_should_get_serialized() { + + // Create the log event. + final LogEvent logEvent = Log4jLogEvent + .newBuilder() + .setThrown(NonAsciiUtf8MethodNameContainingException.INSTANCE) + .build(); + + // Create the event template. + final String eventTemplate = writeJson(asMap( + "ex_stacktrace", asMap( + "$resolver", "exception", + "field", "stackTrace", + "stringified", true))); + + // Create the layout. + final JsonTemplateLayout layout = JsonTemplateLayout + .newBuilder() + .setConfiguration(CONFIGURATION) + .setStackTraceEnabled(true) + .setEventTemplate(eventTemplate) + .build(); + + // Check the serialized event. + usingSerializedLogEventAccessor(layout, logEvent, accessor -> + assertThat(accessor.getString("ex_stacktrace")) + .contains(NonAsciiUtf8MethodNameContainingException.NON_ASCII_UTF8_TEXT)); + + } + + private static final class NonAsciiUtf8MethodNameContainingException extends RuntimeException { + + public static final long serialVersionUID = 0; + + private static final String NON_ASCII_UTF8_TEXT = "அஆஇฬ๘"; + + private static final NonAsciiUtf8MethodNameContainingException INSTANCE = + createInstance(); + + private static NonAsciiUtf8MethodNameContainingException createInstance() { + try { + throwException_அஆஇฬ๘(); + throw new IllegalStateException("should not have reached here"); + } catch (final NonAsciiUtf8MethodNameContainingException exception) { + return exception; + } + } + + @SuppressWarnings("NonAsciiCharacters") + private static void throwException_அஆஇฬ๘() { + throw new NonAsciiUtf8MethodNameContainingException( + "exception with non-ASCII UTF-8 method name"); + } + + private NonAsciiUtf8MethodNameContainingException(final String message) { + super(message); + } + + } + +} diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointerTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointerTest.java new file mode 100644 index 0000000..b00270a --- /dev/null +++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/CharSequencePointerTest.java @@ -0,0 +1,121 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.layout.template.json.util; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +class CharSequencePointerTest { + + private final CharSequencePointer pointer = new CharSequencePointer(); + + @Test + void length_should_fail_without_reset() { + // noinspection ResultOfMethodCallIgnored + assertMissingReset(pointer::length); + } + + @Test + void charAt_should_fail_without_reset() { + assertMissingReset(() -> pointer.charAt(0)); + } + + @Test + void toString_should_fail_without_reset() { + // noinspection ResultOfMethodCallIgnored + assertMissingReset(pointer::toString); + } + + private static void assertMissingReset(final Runnable runnable) { + Assertions + .assertThatThrownBy(runnable::run) + .isInstanceOf(IllegalStateException.class) + .hasMessage("pointer must be reset first"); + } + + @ParameterizedTest + @CsvSource({ + "'',0,0,''", + "foo,0,1,f", + "foo,1,1,''", + "foo,1,2,o", + "foo,3,3,''" + }) + void toString_should_subSequence( + final CharSequence delegate, + final int startIndex, + final int endIndex, + final String expectedOutput) { + pointer.reset(delegate, startIndex, endIndex); + Assertions.assertThat(pointer).hasToString(expectedOutput); + } + + @Test + void subSequence_should_not_be_supported() { + pointer.reset("", 0, 0); + assertUnsupportedOperation(() -> pointer.subSequence(0, 0)); + } + + @Test + void chars_should_not_be_supported() { + pointer.reset("", 0, 0); + assertUnsupportedOperation(() -> pointer.subSequence(0, 0)); + } + + @Test + void codePoints_should_not_be_supported() { + pointer.reset("", 0, 0); + assertUnsupportedOperation(() -> pointer.subSequence(0, 0)); + } + + private static void assertUnsupportedOperation(final Runnable runnable) { + Assertions + .assertThatThrownBy(runnable::run) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("operation requires allocation, contradicting with the purpose of the class"); + } + + @Test + void reset_should_fail_on_null_delegate() { + Assertions + .assertThatThrownBy(() -> pointer.reset(null, 0, 0)) + .isInstanceOf(NullPointerException.class) + .hasMessage("delegate"); + } + + @ParameterizedTest + @CsvSource({ + "foo,-1,3,invalid start: -1", + "foo,4,3,invalid length: -1", + "foo,0,-1,invalid length: -1", + "foo,1,0,invalid length: -1", + "foo,0,4,invalid end: 4" + }) + void reset_should_fail_on_invalid_indices( + final CharSequence delegate, + final int startIndex, + final int endIndex, + final String expectedErrorMessage) { + Assertions + .assertThatThrownBy(() -> pointer.reset(delegate, startIndex, endIndex)) + .isInstanceOf(IndexOutOfBoundsException.class) + .hasMessage(expectedErrorMessage); + } + +} diff --git a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java index b52d453..113e8dd 100644 --- a/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java +++ b/log4j-layout-template-json/src/test/java/org/apache/logging/log4j/layout/template/json/util/TruncatingBufferedWriterTest.java @@ -18,6 +18,8 @@ import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; + +import java.util.function.Consumer; class TruncatingBufferedWriterTest { @@ -225,7 +227,7 @@ verifyTruncation(writer, 'n'); } - private void verifyTruncation( + private static void verifyTruncation( final TruncatingBufferedWriter writer, final char c) { Assertions.assertThat(writer.buffer()).isEqualTo(new char[]{c}); @@ -235,10 +237,67 @@ verifyClose(writer); } - private void verifyClose(final TruncatingBufferedWriter writer) { + private static void verifyClose(final TruncatingBufferedWriter writer) { writer.close(); Assertions.assertThat(writer.position()).isEqualTo(0); Assertions.assertThat(writer.truncated()).isFalse(); } + @Test + void test_length_and_position() { + + // Create the writer and the verifier. + final TruncatingBufferedWriter writer = new TruncatingBufferedWriter(2); + final Consumer positionAndLengthVerifier = + (final Integer expected) -> Assertions + .assertThat(writer.position()) + .isEqualTo(writer.length()) + .isEqualTo(expected); + + // Check the initial condition. + positionAndLengthVerifier.accept(0); + + // Append the 1st character and verify. + writer.write("a"); + positionAndLengthVerifier.accept(1); + + // Append the 2nd character and verify. + writer.write("b"); + positionAndLengthVerifier.accept(2); + + // Append the 3rd to-be-truncated character and verify. + writer.write("c"); + positionAndLengthVerifier.accept(2); + + // Reposition the writer and verify. + writer.position(1); + positionAndLengthVerifier.accept(1); + + } + + @Test + void subSequence_should_not_be_supported() { + final TruncatingBufferedWriter writer = new TruncatingBufferedWriter(2); + assertUnsupportedOperation(() -> writer.subSequence(0, 0)); + } + + @Test + void chars_should_not_be_supported() { + final TruncatingBufferedWriter writer = new TruncatingBufferedWriter(2); + assertUnsupportedOperation(() -> writer.subSequence(0, 0)); + } + + @Test + void codePoints_should_not_be_supported() { + final TruncatingBufferedWriter writer = new TruncatingBufferedWriter(2); + assertUnsupportedOperation(() -> writer.subSequence(0, 0)); + } + + private static void assertUnsupportedOperation(final Runnable runnable) { + Assertions + .assertThatThrownBy(runnable::run) + .isInstanceOf(UnsupportedOperationException.class) + .hasMessage("operation requires allocation, contradicting with the purpose of the class"); + } + } diff --git a/log4j-liquibase/pom.xml b/log4j-liquibase/pom.xml index 0d57dc2..f6f26eb 100644 --- a/log4j-liquibase/pom.xml +++ b/log4j-liquibase/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-liquibase jar @@ -66,10 +66,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test diff --git a/log4j-mongodb3/pom.xml b/log4j-mongodb3/pom.xml index 0439e99..94668ad 100644 --- a/log4j-mongodb3/pom.xml +++ b/log4j-mongodb3/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -56,10 +56,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.mockito diff --git a/log4j-mongodb4/pom.xml b/log4j-mongodb4/pom.xml index ce638c9..251135f 100644 --- a/log4j-mongodb4/pom.xml +++ b/log4j-mongodb4/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 4.0.0 @@ -56,10 +56,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.mockito diff --git a/log4j-osgi/pom.xml b/log4j-osgi/pom.xml index 121626b..dc35ae6 100644 --- a/log4j-osgi/pom.xml +++ b/log4j-osgi/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-osgi jar @@ -32,8 +32,57 @@ /osgi org.apache.logging.log4j.osgi true + 4.13.5 + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-api + ${project.version} + tests + test + + + org.apache.logging.log4j + log4j-1.2-api + test + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-to-jul + test + + + org.apache.logging.log4j + log4j-to-slf4j + test + + + org.apache.logging.log4j.samples + log4j-samples-configuration + ${project.version} + test + + + ch.qos.logback + logback-classic + test + + + ch.qos.logback + logback-core + test + org.apache.felix @@ -43,15 +92,16 @@ org.osgi org.osgi.core - test org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.eclipse.tycho @@ -59,14 +109,38 @@ test - org.apache.maven - maven-core - test - - - org.apache.commons - commons-lang3 - test + javax.inject + javax.inject + + + org.ops4j.pax.exam + pax-exam + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-spi + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-container-native + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-junit4 + ${pax.exam.version} + test + + + org.ops4j.pax.exam + pax-exam-link-assembly + ${pax.exam.version} + test @@ -108,6 +182,30 @@ + + + org.apache.maven.plugins + maven-surefire-plugin + + + + false + org.ops4j.pax.url + + + + + org.ops4j.pax.exam + exam-maven-plugin + ${pax.exam.version} + + + generate-test-resources + + generate-link-files + + + diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java index 41dc963..acc1232 100644 --- a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java +++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/AbstractLoadBundleTest.java @@ -23,14 +23,12 @@ import java.io.PrintStream; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.nio.file.Path; -import java.nio.file.Paths; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.logging.log4j.osgi.tests.junit.BundleTestInfo; import org.apache.logging.log4j.osgi.tests.junit.OsgiRule; +import org.apache.logging.log4j.util.ServiceLoaderUtil; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -47,18 +45,8 @@ private BundleContext bundleContext; - private final BundleTestInfo bundleTestInfo; - - private Path here; - @Rule public OsgiRule osgi = new OsgiRule(getFactory()); - /** - * Constructs a test for a given bundle. - */ - public AbstractLoadBundleTest() { - this.bundleTestInfo = new BundleTestInfo(); - } /** * Called before each @Test. @@ -66,36 +54,33 @@ @Before public void before() throws BundleException { bundleContext = osgi.getFramework().getBundleContext(); - - here = Paths.get(".").toAbsolutePath().normalize(); + } + + private Bundle installBundle(String symbolicName) throws BundleException { + // The links are generated by 'exam-maven-plugin' + final String url = String.format("link:classpath:%s.link", symbolicName); + return bundleContext.installBundle(url); } private Bundle getApiBundle() throws BundleException { - final Path apiPath = here.resolveSibling("log4j-api").resolve("target").resolve("log4j-api-" + bundleTestInfo.getVersion() + ".jar"); - return bundleContext.installBundle(apiPath.toUri().toString()); + return installBundle("org.apache.logging.log4j.api"); } private Bundle getCoreBundle() throws BundleException { - final Path corePath = here.resolveSibling("log4j-core").resolve("target").resolve("log4j-core-" + bundleTestInfo.getVersion() + ".jar"); - return bundleContext.installBundle(corePath.toUri().toString()); + return installBundle("org.apache.logging.log4j.core"); } private Bundle getDummyBundle() throws BundleException { - final Path dumyPath = here.resolveSibling("log4j-samples").resolve("log4j-samples-configuration").resolve("target").resolve("log4j-samples-configuration-" + bundleTestInfo.getVersion() + ".jar"); - return bundleContext.installBundle(dumyPath.toUri().toString()); + return installBundle("org.apache.logging.log4j.samples.log4j-samples-configuration"); } private Bundle get12ApiBundle() throws BundleException { - final Path apiPath = here.resolveSibling("log4j-1.2-api").resolve("target").resolve("log4j-1.2-api-" + bundleTestInfo.getVersion() + ".jar"); - return bundleContext.installBundle(apiPath.toUri().toString()); + return installBundle("org.apache.logging.log4j.1.2-api"); } private Bundle getApiTestsBundle() throws BundleException { - final Path apiTestsPath = here.resolveSibling("log4j-api") - .resolve("target") - .resolve("log4j-api-" + bundleTestInfo.getVersion() + "-tests.jar"); - return bundleContext.installBundle(apiTestsPath.toUri().toString()); + return installBundle("org.apache.logging.log4j.api.tests"); } protected abstract FrameworkFactory getFactory(); diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/JULProviderTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/JULProviderTest.java new file mode 100644 index 0000000..5e918bf --- /dev/null +++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/JULProviderTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.osgi.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.linkBundle; +import static org.ops4j.pax.exam.CoreOptions.options; + +import java.util.Optional; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.log4j.tojul.JULLoggerContextFactory; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class JULProviderTest { + + @Inject + private BundleContext context; + + @Configuration + public Option[] config() { + return options( + linkBundle("org.apache.logging.log4j.api"), + linkBundle("org.apache.logging.log4j.to-jul"), + // required by Pax Exam's logging + linkBundle("slf4j.api"), + linkBundle("ch.qos.logback.classic"), + linkBundle("ch.qos.logback.core"), + junitBundles()); + } + + @Test + public void testJulFactoryResolves() { + final Optional julBundle = Stream.of(context.getBundles()) + .filter(b -> "org.apache.logging.log4j.to-jul".equals(b.getSymbolicName())) + .findAny(); + assertTrue(julBundle.isPresent()); + final LoggerContextFactory factory = LogManager.getFactory(); + assertEquals(JULLoggerContextFactory.class, factory.getClass()); + } +} diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/SLF4JProviderTest.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/SLF4JProviderTest.java new file mode 100644 index 0000000..4546b7b --- /dev/null +++ b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/SLF4JProviderTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.log4j.osgi.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.linkBundle; +import static org.ops4j.pax.exam.CoreOptions.options; + +import java.util.Optional; +import java.util.stream.Stream; + +import javax.inject.Inject; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.spi.LoggerContextFactory; +import org.apache.logging.slf4j.SLF4JLoggerContextFactory; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.ops4j.pax.exam.Configuration; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; +import org.ops4j.pax.exam.spi.reactors.PerClass; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +@RunWith(PaxExam.class) +@ExamReactorStrategy(PerClass.class) +public class SLF4JProviderTest { + + @Inject + private BundleContext context; + + @Configuration + public Option[] config() { + return options( + linkBundle("org.apache.logging.log4j.api"), + linkBundle("org.apache.logging.log4j.to-slf4j"), + linkBundle("slf4j.api"), + linkBundle("ch.qos.logback.classic"), + linkBundle("ch.qos.logback.core"), + junitBundles()); + } + + @Test + public void testSlf4jFactoryResolves() { + final Optional slf4jBundle = Stream.of(context.getBundles()) + .filter(b -> "org.apache.logging.log4j.to-slf4j".equals(b.getSymbolicName())) + .findAny(); + assertTrue(slf4jBundle.isPresent()); + final LoggerContextFactory factory = LogManager.getFactory(); + assertEquals(SLF4JLoggerContextFactory.class, factory.getClass()); + } +} diff --git a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java b/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java deleted file mode 100644 index 9a4f8a8..0000000 --- a/log4j-osgi/src/test/java/org/apache/logging/log4j/osgi/tests/junit/BundleTestInfo.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.log4j.osgi.tests.junit; - -import java.io.FileReader; -import java.io.IOException; - -import org.apache.maven.model.Model; -import org.apache.maven.model.io.xpp3.MavenXpp3Reader; -import org.apache.maven.project.MavenProject; -import org.codehaus.plexus.util.xml.pull.XmlPullParserException; - -/** - * Provides tests with bundle information. Reads the {@code pom.xml} in the current directory to get project settings. - */ -public class BundleTestInfo { - - private final MavenProject project; - - /** - * Constructs a new helper objects and initializes itself. - */ - public BundleTestInfo() { - try (final FileReader reader = new FileReader("pom.xml")) { - // get a raw POM view, not a fully realized POM object. - final Model model = new MavenXpp3Reader().read(reader); - this.project = new MavenProject(model); - } catch (final IOException | XmlPullParserException e) { - throw new IllegalStateException("Could not read pom.xml", e); - } - } - - /** - * Gets the Maven artifact ID. - * - * @return the Maven artifact ID. - */ - public String getArtifactId() { - return project.getArtifactId(); - } - - /** - * Gets the Maven version String. - * - * @return the Maven version String. - */ - public String getVersion() { - return project.getVersion(); - } - - @Override - public String toString() { - return "BundleTestInfo [project=" + project + "]"; - } - -} diff --git a/log4j-perf/pom.xml b/log4j-perf/pom.xml index 103925a..38fcfea 100644 --- a/log4j-perf/pom.xml +++ b/log4j-perf/pom.xml @@ -20,7 +20,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 log4j-perf diff --git a/log4j-samples/log4j-samples-configuration/pom.xml b/log4j-samples/log4j-samples-configuration/pom.xml index 4f9e27d..7bc33ce 100644 --- a/log4j-samples/log4j-samples-configuration/pom.xml +++ b/log4j-samples/log4j-samples-configuration/pom.xml @@ -20,7 +20,7 @@ log4j-samples org.apache.logging.log4j.samples - 2.18.0 + 2.19.0 log4j-samples-configuration jar diff --git a/log4j-samples/log4j-samples-flume-common/pom.xml b/log4j-samples/log4j-samples-flume-common/pom.xml index 677d6ca..7e858cc 100644 --- a/log4j-samples/log4j-samples-flume-common/pom.xml +++ b/log4j-samples/log4j-samples-flume-common/pom.xml @@ -20,7 +20,7 @@ log4j-samples org.apache.logging.log4j.samples - 2.18.0 + 2.19.0 log4j-samples-flume-common jar diff --git a/log4j-samples/log4j-samples-flume-embedded/pom.xml b/log4j-samples/log4j-samples-flume-embedded/pom.xml index 129c7db..af3ad5a 100644 --- a/log4j-samples/log4j-samples-flume-embedded/pom.xml +++ b/log4j-samples/log4j-samples-flume-embedded/pom.xml @@ -20,7 +20,7 @@ log4j-samples org.apache.logging.log4j.samples - 2.18.0 + 2.19.0 log4j-samples-flume-embedded war diff --git a/log4j-samples/log4j-samples-flume-remote/pom.xml b/log4j-samples/log4j-samples-flume-remote/pom.xml index a85aebd..56a8a6f 100644 --- a/log4j-samples/log4j-samples-flume-remote/pom.xml +++ b/log4j-samples/log4j-samples-flume-remote/pom.xml @@ -20,7 +20,7 @@ log4j-samples org.apache.logging.log4j.samples - 2.18.0 + 2.19.0 log4j-samples-flume-remote war diff --git a/log4j-samples/log4j-samples-loggerProperties/pom.xml b/log4j-samples/log4j-samples-loggerProperties/pom.xml index 74d0e0d..2b3b62a 100644 --- a/log4j-samples/log4j-samples-loggerProperties/pom.xml +++ b/log4j-samples/log4j-samples-loggerProperties/pom.xml @@ -20,7 +20,7 @@ log4j-samples org.apache.logging.log4j.samples - 2.18.0 + 2.19.0 log4j-samples-loggerProperties jar diff --git a/log4j-samples/pom.xml b/log4j-samples/pom.xml index 5d85f0f..416135a 100644 --- a/log4j-samples/pom.xml +++ b/log4j-samples/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 org.apache.logging.log4j.samples log4j-samples diff --git a/log4j-slf4j-impl/pom.xml b/log4j-slf4j-impl/pom.xml index 4a5683c..fbdb705 100644 --- a/log4j-slf4j-impl/pom.xml +++ b/log4j-slf4j-impl/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-slf4j-impl jar @@ -88,10 +88,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java index 1fa8080..39cb2e7 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java @@ -30,7 +30,6 @@ import org.apache.logging.log4j.util.LoaderUtil; import org.slf4j.Marker; import org.slf4j.MarkerFactory; -import org.slf4j.impl.StaticMarkerBinder; import org.slf4j.spi.LocationAwareLogger; /** @@ -47,8 +46,10 @@ private final boolean eventLogger; private transient ExtendedLogger logger; private final String name; - - public Log4jLogger(final ExtendedLogger logger, final String name) { + private transient Log4jMarkerFactory markerFactory; + + public Log4jLogger(final Log4jMarkerFactory markerFactory, final ExtendedLogger logger, final String name) { + this.markerFactory = markerFactory; this.logger = logger; this.eventLogger = "EventLogger".equals(name); this.name = name; @@ -86,32 +87,32 @@ @Override public boolean isTraceEnabled(final Marker marker) { - return logger.isEnabled(Level.TRACE, getMarker(marker), null); + return logger.isEnabled(Level.TRACE, markerFactory.getLog4jMarker(marker), null); } @Override public void trace(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s); + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s); } @Override public void trace(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, o); + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, o); } @Override public void trace(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, o, o1); + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, o, o1); } @Override public void trace(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, objects); + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, objects); } @Override public void trace(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, throwable); + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, throwable); } @Override @@ -146,32 +147,32 @@ @Override public boolean isDebugEnabled(final Marker marker) { - return logger.isEnabled(Level.DEBUG, getMarker(marker), null); + return logger.isEnabled(Level.DEBUG, markerFactory.getLog4jMarker(marker), null); } @Override public void debug(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s); + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s); } @Override public void debug(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, o); + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, o); } @Override public void debug(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, o, o1); + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, o, o1); } @Override public void debug(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, objects); + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, objects); } @Override public void debug(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, throwable); + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, throwable); } @Override @@ -206,32 +207,32 @@ @Override public boolean isInfoEnabled(final Marker marker) { - return logger.isEnabled(Level.INFO, getMarker(marker), null); + return logger.isEnabled(Level.INFO, markerFactory.getLog4jMarker(marker), null); } @Override public void info(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s); + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s); } @Override public void info(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, o); + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, o); } @Override public void info(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, o, o1); + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, o, o1); } @Override public void info(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, objects); + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, objects); } @Override public void info(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, throwable); + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, throwable); } @Override @@ -266,32 +267,32 @@ @Override public boolean isWarnEnabled(final Marker marker) { - return logger.isEnabled(Level.WARN, getMarker(marker), null); + return logger.isEnabled(Level.WARN, markerFactory.getLog4jMarker(marker), null); } @Override public void warn(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s); + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s); } @Override public void warn(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, o); + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, o); } @Override public void warn(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, o, o1); + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, o, o1); } @Override public void warn(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, objects); + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, objects); } @Override public void warn(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, throwable); + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, throwable); } @Override @@ -326,65 +327,55 @@ @Override public boolean isErrorEnabled(final Marker marker) { - return logger.isEnabled(Level.ERROR, getMarker(marker), null); + return logger.isEnabled(Level.ERROR, markerFactory.getLog4jMarker(marker), null); } @Override public void error(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s); + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s); } @Override public void error(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, o); + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, o); } @Override public void error(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, o, o1); + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, o, o1); } @Override public void error(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, objects); + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, objects); } @Override public void error(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, throwable); - } - - @Override - public void log(final Marker marker, final String fqcn, final int level, final String message, final Object[] params, Throwable throwable) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void log(final Marker marker, final String fqcn, final int level, final String message, final Object[] params, final Throwable throwable) { final Level log4jLevel = getLevel(level); - final org.apache.logging.log4j.Marker log4jMarker = getMarker(marker); + final org.apache.logging.log4j.Marker log4jMarker = markerFactory.getLog4jMarker(marker); if (!logger.isEnabled(log4jLevel, log4jMarker, message, params)) { return; } final Message msg; + final Throwable actualThrowable; if (CONVERTER != null && eventLogger && marker != null && marker.contains(EVENT_MARKER)) { msg = CONVERTER.convertEvent(message, params, throwable); + actualThrowable = throwable != null ? throwable : msg.getThrowable(); } else if (params == null) { msg = new SimpleMessage(message); + actualThrowable = throwable; } else { msg = new ParameterizedMessage(message, params, throwable); - if (throwable != null) { - throwable = msg.getThrowable(); - } + actualThrowable = throwable != null ? throwable : msg.getThrowable(); } - logger.logMessage(fqcn, log4jLevel, log4jMarker, msg, throwable); - } - - private static org.apache.logging.log4j.Marker getMarker(final Marker marker) { - if (marker == null) { - return null; - } else if (marker instanceof Log4jMarker) { - return ((Log4jMarker) marker).getLog4jMarker(); - } else { - final Log4jMarkerFactory factory = (Log4jMarkerFactory) StaticMarkerBinder.SINGLETON.getMarkerFactory(); - return ((Log4jMarker) factory.getMarker(marker)).getLog4jMarker(); - } + logger.logMessage(fqcn, log4jLevel, log4jMarker, msg, actualThrowable); } @Override @@ -400,6 +391,7 @@ // always perform the default de-serialization first aInputStream.defaultReadObject(); logger = LogManager.getContext().getLogger(name); + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); } /** diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java index d884e6f..f173301 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java @@ -34,14 +34,20 @@ private static final StatusLogger LOGGER = StatusLogger.getLogger(); private static final String SLF4J_PACKAGE = "org.slf4j"; - private static final String TO_SLF4J_CONTEXT = "org.apache.logging.slf4j.SLF4JLoggerContext"; private static final Predicate> CALLER_PREDICATE = clazz -> !AbstractLoggerAdapter.class.equals(clazz) && !clazz.getName().startsWith(SLF4J_PACKAGE); + private static final String TO_SLF4J_CONTEXT = "org.apache.logging.slf4j.SLF4JLoggerContext"; + + private final Log4jMarkerFactory markerFactory; + + public Log4jLoggerFactory(final Log4jMarkerFactory markerFactory) { + this.markerFactory = markerFactory; + } @Override protected Logger newLogger(final String name, final LoggerContext context) { final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; - return new Log4jLogger(validateContext(context).getLogger(key), name); + return new Log4jLogger(markerFactory, validateContext(context).getLogger(key), name); } @Override @@ -55,6 +61,10 @@ : getContext(anchor); } + Log4jMarkerFactory getMarkerFactory() { + return markerFactory; + } + private LoggerContext validateContext(final LoggerContext context) { if (TO_SLF4J_CONTEXT.equals(context.getClass().getName())) { throw new LoggingException("log4j-slf4j-impl cannot be present with log4j-to-slf4j"); diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java index b12c297..9293c5e 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java @@ -52,8 +52,7 @@ } @Override - @SuppressWarnings("unchecked") // nothing we can do about this, restricted by SLF4J API - public void setContextMap(@SuppressWarnings("rawtypes") final Map map) { + public void setContextMap(final Map map) { ThreadContext.clearMap(); ThreadContext.putAll(map); } diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java index db54b5c..fb21659 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java @@ -24,16 +24,15 @@ import org.apache.logging.log4j.MarkerManager; import org.slf4j.IMarkerFactory; import org.slf4j.Marker; -import org.slf4j.impl.StaticMarkerBinder; /** - * Log4j/SLF4J {@link org.slf4j.Marker} type bridge. + * Log4j/SLF4J {@link Marker} type bridge. */ -public class Log4jMarker implements Marker { +class Log4jMarker implements Marker { public static final long serialVersionUID = 1590472L; - private final IMarkerFactory factory = StaticMarkerBinder.SINGLETON.getMarkerFactory(); + private final IMarkerFactory factory; private final org.apache.logging.log4j.Marker marker; @@ -41,7 +40,8 @@ * Constructs a Log4jMarker using an existing Log4j {@link org.apache.logging.log4j.Marker}. * @param marker The Log4j Marker upon which to base this Marker. */ - public Log4jMarker(final org.apache.logging.log4j.Marker marker) { + public Log4jMarker(final IMarkerFactory markerFactory, final org.apache.logging.log4j.Marker marker) { + this.factory = markerFactory; this.marker = marker; } @@ -55,7 +55,7 @@ } @Override - public boolean contains(final org.slf4j.Marker marker) { + public boolean contains(final Marker marker) { if (marker == null) { throw new IllegalArgumentException(); } diff --git a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java index 4cfec6e..9073fa9 100644 --- a/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java +++ b/log4j-slf4j-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java @@ -56,7 +56,7 @@ } private Marker addMarkerIfAbsent(final String name, final org.apache.logging.log4j.Marker log4jMarker) { - final Marker marker = new Log4jMarker(log4jMarker); + final Marker marker = new Log4jMarker(this, log4jMarker); final Marker existing = markerMap.putIfAbsent(name, marker); return existing == null ? marker : existing; } @@ -78,7 +78,23 @@ return addMarkerIfAbsent(marker.getName(), convertMarker(marker)); } - private static org.apache.logging.log4j.Marker convertMarker(final Marker original) { + /** + * Gets the Log4j2 marker associated to this SLF4J marker or creates a new one. + * + * @param marker a SLF4J marker + * @return a Log4j2 marker + */ + org.apache.logging.log4j.Marker getLog4jMarker(final Marker marker) { + if (marker == null) { + return null; + } else if (marker instanceof Log4jMarker) { + return ((Log4jMarker) marker).getLog4jMarker(); + } else { + return ((Log4jMarker) getMarker(marker)).getLog4jMarker(); + } + } + + static org.apache.logging.log4j.Marker convertMarker(final Marker original) { if (original == null) { throw new IllegalArgumentException("Marker must not be null"); } diff --git a/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticLoggerBinder.java b/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticLoggerBinder.java index 85b7016..f735f1e 100644 --- a/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticLoggerBinder.java +++ b/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticLoggerBinder.java @@ -17,6 +17,7 @@ package org.slf4j.impl; import org.apache.logging.slf4j.Log4jLoggerFactory; +import org.apache.logging.slf4j.Log4jMarkerFactory; import org.slf4j.ILoggerFactory; import org.slf4j.spi.LoggerFactoryBinder; @@ -50,7 +51,7 @@ * Private constructor to prevent instantiation */ private StaticLoggerBinder() { - loggerFactory = new Log4jLoggerFactory(); + loggerFactory = new Log4jLoggerFactory((Log4jMarkerFactory) StaticMarkerBinder.getSingleton().getMarkerFactory()); } /** diff --git a/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticMDCBinder.java b/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticMDCBinder.java index eb46620..584c8d1 100644 --- a/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticMDCBinder.java +++ b/log4j-slf4j-impl/src/main/java/org/slf4j/impl/StaticMDCBinder.java @@ -29,6 +29,8 @@ */ public static final StaticMDCBinder SINGLETON = new StaticMDCBinder(); + private final MDCAdapter mdcAdapter = new Log4jMDCAdapter(); + private StaticMDCBinder() { } @@ -46,7 +48,7 @@ * @return an MDC adapter */ public MDCAdapter getMDCA() { - return new Log4jMDCAdapter(); + return mdcAdapter; } /** diff --git a/log4j-slf4j-impl/src/site/markdown/index.md b/log4j-slf4j-impl/src/site/markdown/index.md index b81787f..da16735 100644 --- a/log4j-slf4j-impl/src/site/markdown/index.md +++ b/log4j-slf4j-impl/src/site/markdown/index.md @@ -21,12 +21,14 @@ The Log4j 2 SLF4J Binding allows applications coded to the SLF4J API to use Log4j 2 as the implementation. -Due to a break in compatibility in the SLF4J binding, as of release 2.11.1 two SLF4J to Log4j Adapters are provided. +Due to a break in compatibility in the SLF4J binding, as of release 2.19.0 two SLF4J to Log4j Adapters are provided. -1. log4j-slf4j-impl should be used with SLF4J 1.7.x releases or older. -1. log4j-slf4j18-impl should be used with SLF4J 1.8.x releases or newer. +1. `log4j-slf4j-impl` should be used with SLF4J 1.7.x releases or older. +1. `log4j-slf4j2-impl` should be used with SLF4J 2.0.x releases or newer. -Applications that take advantage of the Java Module System should use SLF4J 1.8.x and log4j-slf4j18-impl. +Applications that take advantage of the Java Module System should use SLF4J 2.0.x and log4j-slf4j2-impl. + +As of release 2.19.0 the `log4j-slf4j18-impl` module targetting the unreleased SLF4J 1.8.x series has been removed. ## Requirements diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java index c982440..7efc0e5 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java @@ -1,67 +1,67 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import static org.junit.Assert.assertEquals; - -import java.util.List; - -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.apache.logging.log4j.test.appender.ListAppender; -import org.junit.ClassRule; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CallerInformationTest { - - // config from log4j-core test-jar - private static final String CONFIG = "log4j2-calling-class.xml"; - - @ClassRule - public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG); - - @Test - public void testClassLogger() throws Exception { - final ListAppender app = ctx.getListAppender("Class").clear(); - final Logger logger = LoggerFactory.getLogger("ClassLogger"); - logger.info("Ignored message contents."); - logger.warn("Verifying the caller class is still correct."); - logger.error("Hopefully nobody breaks me!"); - final List messages = app.getMessages(); - assertEquals("Incorrect number of messages.", 3, messages.size()); - for (final String message : messages) { - assertEquals("Incorrect caller class name.", this.getClass().getName(), message); - } - } - - @Test - public void testMethodLogger() throws Exception { - final ListAppender app = ctx.getListAppender("Method").clear(); - final Logger logger = LoggerFactory.getLogger("MethodLogger"); - logger.info("More messages."); - logger.warn("CATASTROPHE INCOMING!"); - logger.error("ZOMBIES!!!"); - logger.warn("brains~~~"); - logger.info("Itchy. Tasty."); - final List messages = app.getMessages(); - assertEquals("Incorrect number of messages.", 5, messages.size()); - for (final String message : messages) { - assertEquals("Incorrect caller method name.", "testMethodLogger", message); - } - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerInformationTest { + + // config from log4j-core test-jar + private static final String CONFIG = "log4j2-calling-class.xml"; + + @ClassRule + public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG); + + @Test + public void testClassLogger() throws Exception { + final ListAppender app = ctx.getListAppender("Class").clear(); + final Logger logger = LoggerFactory.getLogger("ClassLogger"); + logger.info("Ignored message contents."); + logger.warn("Verifying the caller class is still correct."); + logger.error("Hopefully nobody breaks me!"); + final List messages = app.getMessages(); + assertEquals("Incorrect number of messages.", 3, messages.size()); + for (final String message : messages) { + assertEquals("Incorrect caller class name.", this.getClass().getName(), message); + } + } + + @Test + public void testMethodLogger() throws Exception { + final ListAppender app = ctx.getListAppender("Method").clear(); + final Logger logger = LoggerFactory.getLogger("MethodLogger"); + logger.info("More messages."); + logger.warn("CATASTROPHE INCOMING!"); + logger.error("ZOMBIES!!!"); + logger.warn("brains~~~"); + logger.info("Itchy. Tasty."); + final List messages = app.getMessages(); + assertEquals("Incorrect number of messages.", 5, messages.size()); + for (final String message : messages) { + assertEquals("Incorrect caller method name.", "testMethodLogger", message); + } + } +} diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java index af7c394..ac6ad22 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java @@ -19,17 +19,26 @@ import org.apache.logging.log4j.Marker; import org.apache.logging.log4j.MarkerManager; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; public class Log4jMarkerTest { + + private static Log4jMarkerFactory markerFactory; + + @BeforeClass + public static void startup() { + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); + + } @Test public void testEquals() { final Marker markerA = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-A"); final Marker markerB = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-B"); - final Log4jMarker marker1 = new Log4jMarker(markerA); - final Log4jMarker marker2 = new Log4jMarker(markerA); - final Log4jMarker marker3 = new Log4jMarker(markerB); + final Log4jMarker marker1 = new Log4jMarker(markerFactory, markerA); + final Log4jMarker marker2 = new Log4jMarker(markerFactory, markerA); + final Log4jMarker marker3 = new Log4jMarker(markerFactory, markerB); Assert.assertEquals(marker1, marker2); Assert.assertNotEquals(marker1, null); Assert.assertNotEquals(null, marker1); diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java index 103e5f8..de37a36 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java @@ -19,11 +19,11 @@ import org.apache.logging.log4j.core.LifeCycle; import org.apache.logging.log4j.spi.LoggerContext; import org.junit.Test; -import org.slf4j.impl.StaticLoggerBinder; +import org.slf4j.LoggerFactory; import java.util.Set; -import static org.junit.Assert.*; +import static org.junit.Assert.assertTrue; /** * Tests cleanup of the LoggerContexts. @@ -32,7 +32,7 @@ @Test public void testCleanup() throws Exception { - Log4jLoggerFactory factory = (Log4jLoggerFactory) StaticLoggerBinder.getSingleton().getLoggerFactory(); + Log4jLoggerFactory factory = (Log4jLoggerFactory) LoggerFactory.getILoggerFactory(); factory.getLogger("test"); Set set = factory.getLoggerContexts(); LoggerContext ctx1 = set.toArray(LoggerContext.EMPTY_ARRAY)[0]; diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java index 0524074..5a9f837 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Locale; +import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.junit.LoggerContextRule; import org.apache.logging.log4j.test.appender.ListAppender; import org.apache.logging.log4j.util.Strings; @@ -162,9 +163,38 @@ verify("EventLogger", "o.a.l.s.LoggerTest Transfer [Audit@18060 Amount=\"200.00\" FromAccount=\"123457\" ToAccount=\"123456\"] Transfer Complete" + Strings.LINE_SEPARATOR); } - private void verify(final String name, final String expected) { + @Test + public void testThrowable() { + final Throwable expected = new RuntimeException(); + logger.debug("Hello {}", expected); + verifyThrowable(expected); + logger.debug("Hello {}", (Object) expected); + verifyThrowable(null); + logger.debug("Hello", expected); + verifyThrowable(expected); + logger.debug("Hello {}! {}", "World!", expected); + verifyThrowable(null); + logger.debug("Hello {}!", "World!", expected); + verifyThrowable(expected); + final LocationAwareLogger lal = (LocationAwareLogger) logger; + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", null, expected); + verifyThrowable(expected); + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", new Object[] { expected }, + null); + verifyThrowable(null); + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", + new Object[] { "World!", expected }, null); + verifyThrowable(expected); + } + + private ListAppender getAppenderByName(final String name) { final ListAppender listApp = ctx.getListAppender(name); assertNotNull("Missing Appender", listApp); + return listApp; + } + + private void verify(final String name, final String expected) { + final ListAppender listApp = getAppenderByName(name); final List events = listApp.getMessages(); assertTrue("Incorrect number of messages. Expected 1 Actual " + events.size(), events.size()== 1); final String actual = events.get(0); @@ -172,11 +202,21 @@ listApp.clear(); } + private void verifyThrowable(final Throwable expected) { + final ListAppender listApp = getAppenderByName("UnformattedList"); + final List events = listApp.getEvents(); + assertEquals("Incorrect number of messages", 1, events.size()); + final LogEvent actual = events.get(0); + assertEquals("Incorrect throwable.", expected, actual.getThrown()); + listApp.clear(); + } + @Before @After public void cleanup() { MDC.clear(); ctx.getListAppender("List").clear(); + ctx.getListAppender("UnformattedList").clear(); ctx.getListAppender("EventLogger").clear(); } } diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java index 6ef3846..c078fe0 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java @@ -21,6 +21,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; import static org.junit.Assert.*; @@ -32,8 +33,15 @@ private static final String CHILD_MAKER_NAME = MarkerTest.class.getSimpleName() + "-TEST"; private static final String PARENT_MARKER_NAME = MarkerTest.class.getSimpleName() + "-PARENT"; + private static Log4jMarkerFactory markerFactory; - @Before + @BeforeClass + public static void startup() { + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); + + } + + @Before @After public void clearMarkers() { MarkerManager.clear(); @@ -65,8 +73,8 @@ slf4jMarker.add(slf4jParent); final Marker log4jParent = MarkerManager.getMarker(parentMakerName); final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(log4jMarker); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); final org.slf4j.Marker nullMarker = null; try { log4jSlf4jParent.add(nullMarker); @@ -126,8 +134,8 @@ slf4jMarker.add(slf4jParent); final Marker log4jParent = MarkerManager.getMarker(parentMakerName); final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(log4jMarker); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); final org.slf4j.Marker nullMarker = null; try { Assert.assertFalse(log4jSlf4jParent.contains(nullMarker)); @@ -152,8 +160,8 @@ slf4jMarker.add(slf4jParent); final Marker log4jParent = MarkerManager.getMarker(parentMakerName); final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(log4jMarker); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); final String nullStr = null; Assert.assertFalse(log4jSlf4jParent.contains(nullStr)); Assert.assertFalse(log4jSlf4jMarker.contains(nullStr)); @@ -168,8 +176,8 @@ slf4jMarker.add(slf4jParent); final Marker log4jParent = MarkerManager.getMarker(parentMakerName); final Marker log4jMarker = MarkerManager.getMarker(childMakerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(log4jMarker); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); final org.slf4j.Marker nullMarker = null; Assert.assertFalse(log4jSlf4jParent.remove(nullMarker)); Assert.assertFalse(log4jSlf4jMarker.remove(nullMarker)); diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java index bcb9d0b..4593919 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java @@ -16,12 +16,11 @@ */ package org.apache.logging.slf4j; +import static org.junit.Assert.fail; + import org.apache.logging.log4j.LoggingException; import org.junit.Test; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; - -import static org.junit.Assert.fail; /** * Tests StackOverflow when slf4j-impl and to-slf4j are both present. @@ -31,7 +30,7 @@ @Test public void log() { try { - final Logger logger = LoggerFactory.getLogger(OverflowTest.class); + LoggerFactory.getLogger(OverflowTest.class); fail("Failed to detect inclusion of log4j-to-slf4j"); } catch (LoggingException ex) { // Expected exception. diff --git a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java index 746731b..6b9d223 100644 --- a/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java +++ b/log4j-slf4j-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java @@ -1,46 +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 org.apache.logging.slf4j; - -import java.io.Serializable; - -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.junit.ClassRule; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.logging.log4j.SerializableMatchers.serializesRoundTrip; -import static org.junit.Assert.*; - -/** - * - */ -public class SerializeTest { - - private static final String CONFIG = "log4j-test1.xml"; - - @ClassRule - public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG); - - Logger logger = LoggerFactory.getLogger("LoggerTest"); - - @Test - public void testLogger() throws Exception { - assertThat((Serializable) logger, serializesRoundTrip()); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.apache.logging.log4j.SerializableMatchers.serializesRoundTrip; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.Serializable; + +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class SerializeTest { + + private static final String CONFIG = "log4j-test1.xml"; + + @ClassRule + public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG); + + Logger logger = LoggerFactory.getLogger("LoggerTest"); + + @Test + public void testLogger() throws Exception { + assertThat((Serializable) logger, serializesRoundTrip()); + } +} diff --git a/log4j-slf4j-impl/src/test/resources/log4j-test1.xml b/log4j-slf4j-impl/src/test/resources/log4j-test1.xml index a64bdfa..1ba09ca 100644 --- a/log4j-slf4j-impl/src/test/resources/log4j-test1.xml +++ b/log4j-slf4j-impl/src/test/resources/log4j-test1.xml @@ -20,6 +20,7 @@ + @@ -34,6 +35,7 @@ + diff --git a/log4j-slf4j18-impl/pom.xml b/log4j-slf4j18-impl/pom.xml deleted file mode 100644 index 689312f..0000000 --- a/log4j-slf4j18-impl/pom.xml +++ /dev/null @@ -1,255 +0,0 @@ - - - - 4.0.0 - - org.apache.logging.log4j - log4j - 2.18.0 - - log4j-slf4j18-impl - jar - Apache Log4j SLF4J 1.8+ Binding - The Apache Log4j SLF4J 1.8 API binding to Log4j 2 Core - - ${basedir}/.. - SLF4J Documentation - /slf4j18 - 1.8.0-beta4 - org.apache.logging.log4j.slf4j - true - - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - slf4j-ext - ${slf4j.version} - true - - - org.apache.logging.log4j - log4j-api - - - org.apache.logging.log4j - log4j-core - runtime - - - org.apache.logging.log4j - log4j-api - test-jar - test - - - org.apache.commons - commons-lang3 - test - - - org.apache.commons - commons-csv - test - - - org.apache.logging.log4j - log4j-core - test-jar - test - - - org.apache.logging.log4j - log4j-to-slf4j - test - ${project.version} - - - org.junit.vintage - junit-vintage-engine - - - org.junit.jupiter - junit-jupiter-engine - - - - - - - org.apache.maven.plugins - maven-remote-resources-plugin - - - - process - - - false - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - loop-test - test - - test - - - - **/OverflowTest.java - - - - - default-test - test - - test - - - - **/*Test.java - - - **/OverflowTest.java - - - org.apache.logging.log4j:log4j-to-slf4j - - - - - - - org.apache.felix - maven-bundle-plugin - - - - org.apache.logging.slf4j, - org.slf4j.impl - - - osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)" - - - osgi.serviceloader;osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider - - - - - - - - - - org.apache.maven.plugins - maven-changes-plugin - ${changes.plugin.version} - - - - changes-report - - - - - %URL%/show_bug.cgi?id=%ISSUE% - true - - - - org.apache.maven.plugins - maven-checkstyle-plugin - ${checkstyle.plugin.version} - - - ${log4jParentDir}/checkstyle.xml - ${log4jParentDir}/checkstyle-suppressions.xml - false - basedir=${basedir} - licensedir=${log4jParentDir}/checkstyle-header.txt - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${javadoc.plugin.version} - - Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.
    - Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo, - and the Apache Log4j logo are trademarks of The Apache Software Foundation.

    ]]> - - false - true - - - - non-aggregate - - javadoc - - - - - - com.github.spotbugs - spotbugs-maven-plugin - - - org.apache.maven.plugins - maven-jxr-plugin - ${jxr.plugin.version} - - - non-aggregate - - jxr - - - - aggregate - - aggregate - - - - - - org.apache.maven.plugins - maven-pmd-plugin - ${pmd.plugin.version} - - ${maven.compiler.target} - - - - - - diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java deleted file mode 100644 index 10ad49c..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.message.Message; -import org.apache.logging.log4j.message.ParameterizedMessage; -import org.apache.logging.log4j.message.SimpleMessage; -import org.apache.logging.log4j.spi.ExtendedLogger; -import org.slf4j.Marker; -import org.slf4j.spi.LocationAwareLogger; - -/** - * SLF4J logger implementation that uses Log4j. - */ -public class Log4jLogger implements LocationAwareLogger, Serializable { - - public static final String FQCN = Log4jLogger.class.getName(); - - private static final long serialVersionUID = 7869000638091304316L; - private transient ExtendedLogger logger; - private final String name; - private transient Log4jMarkerFactory markerFactory; - - public Log4jLogger(final Log4jMarkerFactory markerFactory, final ExtendedLogger logger, final String name) { - this.markerFactory = markerFactory; - this.logger = logger; - this.name = name; - } - - @Override - public void trace(final String format) { - logger.logIfEnabled(FQCN, Level.TRACE, null, format); - } - - @Override - public void trace(final String format, final Object o) { - logger.logIfEnabled(FQCN, Level.TRACE, null, format, o); - } - - @Override - public void trace(final String format, final Object arg1, final Object arg2) { - logger.logIfEnabled(FQCN, Level.TRACE, null, format, arg1, arg2); - } - - @Override - public void trace(final String format, final Object... args) { - logger.logIfEnabled(FQCN, Level.TRACE, null, format, args); - } - - @Override - public void trace(final String format, final Throwable t) { - logger.logIfEnabled(FQCN, Level.TRACE, null, format, t); - } - - @Override - public boolean isTraceEnabled() { - return logger.isEnabled(Level.TRACE, null, null); - } - - @Override - public boolean isTraceEnabled(final Marker marker) { - return logger.isEnabled(Level.TRACE, getMarker(marker), null); - } - - @Override - public void trace(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s); - } - - @Override - public void trace(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, o); - } - - @Override - public void trace(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, o, o1); - } - - @Override - public void trace(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, objects); - } - - @Override - public void trace(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.TRACE, getMarker(marker), s, throwable); - } - - @Override - public void debug(final String format) { - logger.logIfEnabled(FQCN, Level.DEBUG, null, format); - } - - @Override - public void debug(final String format, final Object o) { - logger.logIfEnabled(FQCN, Level.DEBUG, null, format, o); - } - - @Override - public void debug(final String format, final Object arg1, final Object arg2) { - logger.logIfEnabled(FQCN, Level.DEBUG, null, format, arg1, arg2); - } - - @Override - public void debug(final String format, final Object... args) { - logger.logIfEnabled(FQCN, Level.DEBUG, null, format, args); - } - - @Override - public void debug(final String format, final Throwable t) { - logger.logIfEnabled(FQCN, Level.DEBUG, null, format, t); - } - - @Override - public boolean isDebugEnabled() { - return logger.isEnabled(Level.DEBUG, null, null); - } - - @Override - public boolean isDebugEnabled(final Marker marker) { - return logger.isEnabled(Level.DEBUG, getMarker(marker), null); - } - - @Override - public void debug(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s); - } - - @Override - public void debug(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, o); - } - - @Override - public void debug(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, o, o1); - } - - @Override - public void debug(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, objects); - } - - @Override - public void debug(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.DEBUG, getMarker(marker), s, throwable); - } - - @Override - public void info(final String format) { - logger.logIfEnabled(FQCN, Level.INFO, null, format); - } - - @Override - public void info(final String format, final Object o) { - logger.logIfEnabled(FQCN, Level.INFO, null, format, o); - } - - @Override - public void info(final String format, final Object arg1, final Object arg2) { - logger.logIfEnabled(FQCN, Level.INFO, null, format, arg1, arg2); - } - - @Override - public void info(final String format, final Object... args) { - logger.logIfEnabled(FQCN, Level.INFO, null, format, args); - } - - @Override - public void info(final String format, final Throwable t) { - logger.logIfEnabled(FQCN, Level.INFO, null, format, t); - } - - @Override - public boolean isInfoEnabled() { - return logger.isEnabled(Level.INFO, null, null); - } - - @Override - public boolean isInfoEnabled(final Marker marker) { - return logger.isEnabled(Level.INFO, getMarker(marker), null); - } - - @Override - public void info(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s); - } - - @Override - public void info(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, o); - } - - @Override - public void info(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, o, o1); - } - - @Override - public void info(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, objects); - } - - @Override - public void info(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.INFO, getMarker(marker), s, throwable); - } - - @Override - public void warn(final String format) { - logger.logIfEnabled(FQCN, Level.WARN, null, format); - } - - @Override - public void warn(final String format, final Object o) { - logger.logIfEnabled(FQCN, Level.WARN, null, format, o); - } - - @Override - public void warn(final String format, final Object arg1, final Object arg2) { - logger.logIfEnabled(FQCN, Level.WARN, null, format, arg1, arg2); - } - - @Override - public void warn(final String format, final Object... args) { - logger.logIfEnabled(FQCN, Level.WARN, null, format, args); - } - - @Override - public void warn(final String format, final Throwable t) { - logger.logIfEnabled(FQCN, Level.WARN, null, format, t); - } - - @Override - public boolean isWarnEnabled() { - return logger.isEnabled(Level.WARN, null, null); - } - - @Override - public boolean isWarnEnabled(final Marker marker) { - return logger.isEnabled(Level.WARN, getMarker(marker), null); - } - - @Override - public void warn(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s); - } - - @Override - public void warn(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, o); - } - - @Override - public void warn(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, o, o1); - } - - @Override - public void warn(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, objects); - } - - @Override - public void warn(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.WARN, getMarker(marker), s, throwable); - } - - @Override - public void error(final String format) { - logger.logIfEnabled(FQCN, Level.ERROR, null, format); - } - - @Override - public void error(final String format, final Object o) { - logger.logIfEnabled(FQCN, Level.ERROR, null, format, o); - } - - @Override - public void error(final String format, final Object arg1, final Object arg2) { - logger.logIfEnabled(FQCN, Level.ERROR, null, format, arg1, arg2); - } - - @Override - public void error(final String format, final Object... args) { - logger.logIfEnabled(FQCN, Level.ERROR, null, format, args); - } - - @Override - public void error(final String format, final Throwable t) { - logger.logIfEnabled(FQCN, Level.ERROR, null, format, t); - } - - @Override - public boolean isErrorEnabled() { - return logger.isEnabled(Level.ERROR, null, null); - } - - @Override - public boolean isErrorEnabled(final Marker marker) { - return logger.isEnabled(Level.ERROR, getMarker(marker), null); - } - - @Override - public void error(final Marker marker, final String s) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s); - } - - @Override - public void error(final Marker marker, final String s, final Object o) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, o); - } - - @Override - public void error(final Marker marker, final String s, final Object o, final Object o1) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, o, o1); - } - - @Override - public void error(final Marker marker, final String s, final Object... objects) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, objects); - } - - @Override - public void error(final Marker marker, final String s, final Throwable throwable) { - logger.logIfEnabled(FQCN, Level.ERROR, getMarker(marker), s, throwable); - } - - @Override - public void log(final Marker marker, final String fqcn, final int level, final String message, final Object[] params, Throwable throwable) { - final Level log4jLevel = getLevel(level); - final org.apache.logging.log4j.Marker log4jMarker = getMarker(marker); - - if (!logger.isEnabled(log4jLevel, log4jMarker, message, params)) { - return; - } - final Message msg; - if (params == null) { - msg = new SimpleMessage(message); - } else { - msg = new ParameterizedMessage(message, params, throwable); - if (throwable != null) { - throwable = msg.getThrowable(); - } - } - logger.logMessage(fqcn, log4jLevel, log4jMarker, msg, throwable); - } - - private org.apache.logging.log4j.Marker getMarker(final Marker marker) { - if (marker == null) { - return null; - } else if (marker instanceof Log4jMarker) { - return ((Log4jMarker) marker).getLog4jMarker(); - } else { - return ((Log4jMarker) markerFactory.getMarker(marker)).getLog4jMarker(); - } - } - - @Override - public String getName() { - return name; - } - - /** - * Always treat de-serialization as a full-blown constructor, by validating the final state of - * the de-serialized object. - */ - private void readObject(final ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { - // always perform the default de-serialization first - aInputStream.defaultReadObject(); - logger = LogManager.getContext().getLogger(name); - markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); - } - - /** - * This is the default implementation of writeObject. Customise if necessary. - */ - private void writeObject(final ObjectOutputStream aOutputStream) throws IOException { - // perform the default serialization for all non-transient, non-static fields - aOutputStream.defaultWriteObject(); - } - - private static Level getLevel(final int i) { - switch (i) { - case TRACE_INT: - return Level.TRACE; - case DEBUG_INT: - return Level.DEBUG; - case INFO_INT: - return Level.INFO; - case WARN_INT: - return Level.WARN; - case ERROR_INT: - return Level.ERROR; - } - return Level.ERROR; - } -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java deleted file mode 100644 index 99e6817..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.LoggingException; -import org.apache.logging.log4j.spi.AbstractLoggerAdapter; -import org.apache.logging.log4j.spi.LoggerContext; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.StackLocatorUtil; -import org.slf4j.ILoggerFactory; -import org.slf4j.Logger; - -import java.util.function.Predicate; - -/** - * Log4j implementation of SLF4J ILoggerFactory interface. - */ -public class Log4jLoggerFactory extends AbstractLoggerAdapter implements ILoggerFactory { - - private static final StatusLogger LOGGER = StatusLogger.getLogger(); - private static final String SLF4J_PACKAGE = "org.slf4j"; - private static final Predicate> CALLER_PREDICATE = clazz -> - !AbstractLoggerAdapter.class.equals(clazz) && !clazz.getName().startsWith(SLF4J_PACKAGE); - private static final String TO_SLF4J_CONTEXT = "org.apache.logging.slf4j.SLF4JLoggerContext"; - - private final Log4jMarkerFactory markerFactory; - - public Log4jLoggerFactory(final Log4jMarkerFactory markerFactory) { - this.markerFactory = markerFactory; - } - - - @Override - protected Logger newLogger(final String name, final LoggerContext context) { - final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; - return new Log4jLogger(markerFactory, validateContext(context).getLogger(key), name); - } - - @Override - protected LoggerContext getContext() { - final Class anchor = LogManager.getFactory().isClassLoaderDependent() - ? StackLocatorUtil.getCallerClass(Log4jLoggerFactory.class, CALLER_PREDICATE) - : null; - LOGGER.trace("Log4jLoggerFactory.getContext() found anchor {}", anchor); - return anchor == null - ? LogManager.getContext(false) - : getContext(anchor); - } - - Log4jMarkerFactory getMarkerFactory() { - return markerFactory; - } - - private LoggerContext validateContext(final LoggerContext context) { - if (TO_SLF4J_CONTEXT.equals(context.getClass().getName())) { - throw new LoggingException("log4j-slf4j-impl cannot be present with log4j-to-slf4j"); - } - return context; - } -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java deleted file mode 100644 index b12c297..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import java.util.Map; - -import org.apache.logging.log4j.ThreadContext; -import org.slf4j.spi.MDCAdapter; - -/** - * - */ -public class Log4jMDCAdapter implements MDCAdapter { - - @Override - public void put(final String key, final String val) { - ThreadContext.put(key, val); - } - - @Override - public String get(final String key) { - return ThreadContext.get(key); - } - - @Override - public void remove(final String key) { - ThreadContext.remove(key); - } - - @Override - public void clear() { - ThreadContext.clearMap(); - } - - @Override - public Map getCopyOfContextMap() { - return ThreadContext.getContext(); - } - - @Override - @SuppressWarnings("unchecked") // nothing we can do about this, restricted by SLF4J API - public void setContextMap(@SuppressWarnings("rawtypes") final Map map) { - ThreadContext.clearMap(); - ThreadContext.putAll(map); - } -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java deleted file mode 100644 index fb21659..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; - -import org.apache.logging.log4j.MarkerManager; -import org.slf4j.IMarkerFactory; -import org.slf4j.Marker; - -/** - * Log4j/SLF4J {@link Marker} type bridge. - */ -class Log4jMarker implements Marker { - - public static final long serialVersionUID = 1590472L; - - private final IMarkerFactory factory; - - private final org.apache.logging.log4j.Marker marker; - - /** - * Constructs a Log4jMarker using an existing Log4j {@link org.apache.logging.log4j.Marker}. - * @param marker The Log4j Marker upon which to base this Marker. - */ - public Log4jMarker(final IMarkerFactory markerFactory, final org.apache.logging.log4j.Marker marker) { - this.factory = markerFactory; - this.marker = marker; - } - - @Override - public void add(final Marker marker) { - if (marker == null) { - throw new IllegalArgumentException(); - } - final Marker m = factory.getMarker(marker.getName()); - this.marker.addParents(((Log4jMarker)m).getLog4jMarker()); - } - - @Override - public boolean contains(final Marker marker) { - if (marker == null) { - throw new IllegalArgumentException(); - } - return this.marker.isInstanceOf(marker.getName()); - } - - @Override - public boolean contains(final String s) { - return s != null ? this.marker.isInstanceOf(s) : false; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof Log4jMarker)) { - return false; - } - final Log4jMarker other = (Log4jMarker) obj; - if (!Objects.equals(marker, other.marker)) { - return false; - } - return true; - } - - public org.apache.logging.log4j.Marker getLog4jMarker() { - return marker; - } - - @Override - public String getName() { - return marker.getName(); - } - - @Override - public boolean hasChildren() { - return marker.hasParents(); - } - - @Override - public int hashCode() { - return 31 + Objects.hashCode(marker); - } - - @Override - public boolean hasReferences() { - return marker.hasParents(); - } - - @Override - public Iterator iterator() { - final org.apache.logging.log4j.Marker[] log4jParents = this.marker.getParents(); - final List parents = new ArrayList<>(log4jParents.length); - for (final org.apache.logging.log4j.Marker m : log4jParents) { - parents.add(factory.getMarker(m.getName())); - } - return parents.iterator(); - } - - @Override - public boolean remove(final Marker marker) { - return marker != null ? this.marker.remove(MarkerManager.getMarker(marker.getName())) : false; - } -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java deleted file mode 100644 index 69ea94b..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -import org.apache.logging.log4j.Logger; -import org.apache.logging.log4j.MarkerManager; -import org.apache.logging.log4j.status.StatusLogger; -import org.slf4j.IMarkerFactory; -import org.slf4j.Marker; - -/** - * Log4j/SLF4J bridge to create SLF4J Markers based on name or based on existing SLF4J Markers. - */ -public class Log4jMarkerFactory implements IMarkerFactory { - - private static final Logger LOGGER = StatusLogger.getLogger(); - - private final ConcurrentMap markerMap = new ConcurrentHashMap<>(); - - /** - * Returns a Log4j Marker that is compatible with SLF4J. - * @param name The name of the Marker. - * @return A Marker. - */ - @Override - public Marker getMarker(final String name) { - if (name == null) { - throw new IllegalArgumentException("Marker name must not be null"); - } - final Marker marker = markerMap.get(name); - if (marker != null) { - return marker; - } - final org.apache.logging.log4j.Marker log4jMarker = MarkerManager.getMarker(name); - return addMarkerIfAbsent(name, log4jMarker); - } - - private Marker addMarkerIfAbsent(final String name, final org.apache.logging.log4j.Marker log4jMarker) { - final Marker marker = new Log4jMarker(this, log4jMarker); - final Marker existing = markerMap.putIfAbsent(name, marker); - return existing == null ? marker : existing; - } - - /** - * Returns a Log4j Marker converted from an existing custom SLF4J Marker. - * @param marker The SLF4J Marker to convert. - * @return A converted Log4j/SLF4J Marker. - * @since 2.1 - */ - public Marker getMarker(final Marker marker) { - if (marker == null) { - throw new IllegalArgumentException("Marker must not be null"); - } - final Marker m = markerMap.get(marker.getName()); - if (m != null) { - return m; - } - return addMarkerIfAbsent(marker.getName(), convertMarker(marker)); - } - - private static org.apache.logging.log4j.Marker convertMarker(final Marker original) { - if (original == null) { - throw new IllegalArgumentException("Marker must not be null"); - } - return convertMarker(original, new ArrayList()); - } - - private static org.apache.logging.log4j.Marker convertMarker(final Marker original, - final Collection visited) { - final org.apache.logging.log4j.Marker marker = MarkerManager.getMarker(original.getName()); - if (original.hasReferences()) { - final Iterator it = original.iterator(); - while (it.hasNext()) { - final Marker next = it.next(); - if (visited.contains(next)) { - LOGGER.warn("Found a cycle in Marker [{}]. Cycle will be broken.", next.getName()); - } else { - visited.add(next); - marker.addParents(convertMarker(next, visited)); - } - } - } - return marker; - } - - /** - * Returns true if the Marker exists. - * @param name The Marker name. - * @return {@code true} if the Marker exists, {@code false} otherwise. - */ - @Override - public boolean exists(final String name) { - return markerMap.containsKey(name); - } - - /** - * Log4j does not support detached Markers. This method always returns false. - * @param name The Marker name. - * @return {@code false} - */ - @Override - public boolean detachMarker(final String name) { - return false; - } - - /** - * Log4j does not support detached Markers for performance reasons. The returned Marker is attached. - * @param name The Marker name. - * @return The named Marker (unmodified). - */ - @Override - public Marker getDetachedMarker(final String name) { - LOGGER.warn("Log4j does not support detached Markers. Returned Marker [{}] will be unchanged.", name); - return getMarker(name); - } - - -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java deleted file mode 100644 index 0a41215..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -/** - * Exception thrown when the SLF4J adapter encounters a problem. - * - */ -public class SLF4JLoggingException extends RuntimeException { - - /** - * Generated serial version ID. - */ - private static final long serialVersionUID = -1618650972455089998L; - - public SLF4JLoggingException(final String msg) { - super(msg); - } - - public SLF4JLoggingException(final String msg, final Exception ex) { - super(msg, ex); - } - - public SLF4JLoggingException(final Exception ex) { - super(ex); - } -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java deleted file mode 100644 index 561132f..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.slf4j.ILoggerFactory; -import org.slf4j.IMarkerFactory; -import org.slf4j.spi.MDCAdapter; - -public class SLF4JServiceProvider implements org.slf4j.spi.SLF4JServiceProvider { - - public static final String REQUESTED_API_VERSION = "1.8.99"; - - private ILoggerFactory loggerFactory; - private Log4jMarkerFactory markerFactory; - private MDCAdapter mdcAdapter; - - @Override - public ILoggerFactory getLoggerFactory() { - return loggerFactory; - } - - @Override - public IMarkerFactory getMarkerFactory() { - return markerFactory; - } - - @Override - public MDCAdapter getMDCAdapter() { - return mdcAdapter; - } - - @Override - public String getRequesteApiVersion() { - return REQUESTED_API_VERSION; - } - - @Override - public void initialize() { - markerFactory = new Log4jMarkerFactory(); - loggerFactory = new Log4jLoggerFactory(markerFactory); - mdcAdapter = new Log4jMDCAdapter(); - } - - -} diff --git a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/package-info.java b/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/package-info.java deleted file mode 100644 index ec0f031..0000000 --- a/log4j-slf4j18-impl/src/main/java/org/apache/logging/slf4j/package-info.java +++ /dev/null @@ -1,22 +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. - */ -/** - * SLF4J support. Note that this does indeed share the same package namespace as the one found in log4j-to-slf4j; - * this is intentional. The two JARs should not be used at the same time! Thus, in an OSGi environment - * where split packages are not allowed, this error is prevented due to both JARs sharing an exported package name. - */ -package org.apache.logging.slf4j; diff --git a/log4j-slf4j18-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/log4j-slf4j18-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider deleted file mode 100644 index 1577f12..0000000 --- a/log4j-slf4j18-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider +++ /dev/null @@ -1 +0,0 @@ -org.apache.logging.slf4j.SLF4JServiceProvider \ No newline at end of file diff --git a/log4j-slf4j18-impl/src/site/markdown/index.md b/log4j-slf4j18-impl/src/site/markdown/index.md deleted file mode 100644 index 6e3f3f6..0000000 --- a/log4j-slf4j18-impl/src/site/markdown/index.md +++ /dev/null @@ -1,40 +0,0 @@ - - - -# Log4j 2 SLF4J Binding - -The Log4j 2 SLF4J Binding allows applications coded to the SLF4J API to use -Log4j 2 as the implementation. - -## Requirements - -The Log4j 2 SLF4J Binding has a dependency on the Log4j 2 API as well as the SLF4J API. -For more information, see [Runtime Dependencies](../runtime-dependencies.html). - -## Usage - -The SLF4J binding provided in this component cause all the SLF4J APIs to be routed to Log4j 2. Simply -include the Log4j 2 SLF4J Binding jar along with the Log4j 2 jars and SLF4J API jar to cause all SLF4J -logging to be handled by Log4j 2. - -
    -Use of the Log4j 2 SLF4J Binding (log4j-slf4j-impl-2.0.jar) together with -the SLF4J adapter (log4j-to-slf4j-2.0.jar) should -never be attempted, as it will cause events to endlessly be routed between -SLF4J and Log4j 2. -
    diff --git a/log4j-slf4j18-impl/src/site/site.xml b/log4j-slf4j18-impl/src/site/site.xml deleted file mode 100644 index a1f9106..0000000 --- a/log4j-slf4j18-impl/src/site/site.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java deleted file mode 100644 index 2b0fe91..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.other.pkg; - -import org.apache.logging.log4j.Level; -import org.apache.logging.log4j.status.StatusData; -import org.apache.logging.log4j.status.StatusListener; -import org.apache.logging.log4j.status.StatusLogger; -import org.junit.Test; -import org.slf4j.LoggerFactory; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -import static org.junit.Assert.assertEquals; - -/** - * Test LoggerContext lookups by verifying the anchor class representing calling code. - */ -public class LoggerContextAnchorTest { - private static final String PREFIX = "Log4jLoggerFactory.getContext() found anchor class "; - - @Test - public void testLoggerFactoryLookupClass() { - String fqcn = getAnchorFqcn(() -> LoggerFactory.getLogger(LoggerContextAnchorTest.class)); - assertEquals(getClass().getName(), fqcn); - } - - @Test - public void testLoggerFactoryLookupString() { - String fqcn = getAnchorFqcn(() -> LoggerFactory.getLogger("custom.logger")); - assertEquals(getClass().getName(), fqcn); - } - - @Test - public void testLoggerFactoryGetILoggerFactoryLookup() { - String fqcn = getAnchorFqcn(() -> LoggerFactory.getILoggerFactory().getLogger("custom.logger")); - assertEquals(getClass().getName(), fqcn); - } - - private static String getAnchorFqcn(Runnable runnable) { - List results = new CopyOnWriteArrayList<>(); - StatusListener listener = new StatusListener() { - @Override - public void log(StatusData data) { - String formattedMessage = data.getMessage().getFormattedMessage(); - if (formattedMessage.startsWith(PREFIX)) { - results.add(formattedMessage.substring(PREFIX.length())); - } - } - - @Override - public Level getStatusLevel() { - return Level.TRACE; - } - - @Override - public void close() { - // nop - } - }; - StatusLogger statusLogger = StatusLogger.getLogger(); - statusLogger.registerListener(listener); - try { - runnable.run(); - if (results.isEmpty()) { - throw new AssertionError("Failed to locate an anchor lookup status message"); - } - if (results.size() > 1) { - throw new AssertionError("Found multiple anchor lines: " + results); - } - return results.get(0); - } finally { - statusLogger.removeListener(listener); - } - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java deleted file mode 100644 index 7efc0e5..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import static org.junit.Assert.assertEquals; - -import java.util.List; - -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.apache.logging.log4j.test.appender.ListAppender; -import org.junit.ClassRule; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class CallerInformationTest { - - // config from log4j-core test-jar - private static final String CONFIG = "log4j2-calling-class.xml"; - - @ClassRule - public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG); - - @Test - public void testClassLogger() throws Exception { - final ListAppender app = ctx.getListAppender("Class").clear(); - final Logger logger = LoggerFactory.getLogger("ClassLogger"); - logger.info("Ignored message contents."); - logger.warn("Verifying the caller class is still correct."); - logger.error("Hopefully nobody breaks me!"); - final List messages = app.getMessages(); - assertEquals("Incorrect number of messages.", 3, messages.size()); - for (final String message : messages) { - assertEquals("Incorrect caller class name.", this.getClass().getName(), message); - } - } - - @Test - public void testMethodLogger() throws Exception { - final ListAppender app = ctx.getListAppender("Method").clear(); - final Logger logger = LoggerFactory.getLogger("MethodLogger"); - logger.info("More messages."); - logger.warn("CATASTROPHE INCOMING!"); - logger.error("ZOMBIES!!!"); - logger.warn("brains~~~"); - logger.info("Itchy. Tasty."); - final List messages = app.getMessages(); - assertEquals("Incorrect number of messages.", 5, messages.size()); - for (final String message : messages) { - assertEquals("Incorrect caller method name.", "testMethodLogger", message); - } - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java deleted file mode 100644 index 0138cea..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.slf4j; - -import java.util.Iterator; - -import org.slf4j.Marker; - -/** - * Test Marker that may contain no reference/parent Markers. - * @see LOG4J2-793 - */ -public class CustomFlatMarker implements Marker { - private static final long serialVersionUID = -4115520883240247266L; - - private final String name; - - public CustomFlatMarker(final String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public void add(final Marker reference) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(final Marker reference) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean hasChildren() { - return hasReferences(); - } - - @Override - public boolean hasReferences() { - return false; - } - - @Override - public Iterator iterator() { - throw new UnsupportedOperationException(); - } - - @Override - public boolean contains(final Marker other) { - return false; - } - - @Override - public boolean contains(final String name) { - return false; - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java deleted file mode 100644 index 59f69e0..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.Assert.*; - -/** - * Tests logging during shutdown. - */ -public class Log4j1222Test -{ - - @Test - public void homepageRendersSuccessfully() - { - System.setProperty("log4j.configurationFile", "log4j2-console.xml"); - Runtime.getRuntime().addShutdownHook(new ShutdownHook()); - } - - private static class ShutdownHook extends Thread { - - private static class Holder { - private static final Logger LOGGER = LoggerFactory.getLogger(Log4j1222Test.class); - } - - @Override - public void run() - { - super.run(); - trigger(); - } - - private void trigger() { - Holder.LOGGER.info("Attempt to trigger"); - assertTrue("Logger is of type " + Holder.LOGGER.getClass().getName(), Holder.LOGGER instanceof Log4jLogger); - - } - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java deleted file mode 100644 index 8934f29..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ - -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.core.layout.Log4j2_1482_Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Tests https://issues.apache.org/jira/browse/LOG4J2-1482 - */ -public class Log4j2_1482_Slf4jTest extends Log4j2_1482_Test { - - @Override - protected void log(final int runNumber) { - if (runNumber == 2) { - // System.out.println("Set a breakpoint here."); - } - final Logger logger = LoggerFactory.getLogger("auditcsvfile"); - final int val1 = 9, val2 = 11, val3 = 12; - logger.info("Info Message!", val1, val2, val3); - logger.info("Info Message!", val1, val2, val3); - logger.info("Info Message!", val1, val2, val3); - } - -} \ No newline at end of file diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java deleted file mode 100644 index ac6ad22..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.MarkerManager; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class Log4jMarkerTest { - - private static Log4jMarkerFactory markerFactory; - - @BeforeClass - public static void startup() { - markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); - - } - - @Test - public void testEquals() { - final Marker markerA = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-A"); - final Marker markerB = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-B"); - final Log4jMarker marker1 = new Log4jMarker(markerFactory, markerA); - final Log4jMarker marker2 = new Log4jMarker(markerFactory, markerA); - final Log4jMarker marker3 = new Log4jMarker(markerFactory, markerB); - Assert.assertEquals(marker1, marker2); - Assert.assertNotEquals(marker1, null); - Assert.assertNotEquals(null, marker1); - Assert.assertNotEquals(marker1, marker3); - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java deleted file mode 100644 index de37a36..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.core.LifeCycle; -import org.apache.logging.log4j.spi.LoggerContext; -import org.junit.Test; -import org.slf4j.LoggerFactory; - -import java.util.Set; - -import static org.junit.Assert.assertTrue; - -/** - * Tests cleanup of the LoggerContexts. - */ -public class LoggerContextTest { - - @Test - public void testCleanup() throws Exception { - Log4jLoggerFactory factory = (Log4jLoggerFactory) LoggerFactory.getILoggerFactory(); - factory.getLogger("test"); - Set set = factory.getLoggerContexts(); - LoggerContext ctx1 = set.toArray(LoggerContext.EMPTY_ARRAY)[0]; - assertTrue("LoggerContext is not enabled for shutdown", ctx1 instanceof LifeCycle); - ((LifeCycle) ctx1).stop(); - set = factory.getLoggerContexts(); - assertTrue("Expected no LoggerContexts", set.isEmpty()); - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java deleted file mode 100644 index c60f1ad..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.apache.logging.log4j.test.appender.ListAppender; -import org.apache.logging.log4j.util.Strings; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.MDC; -import org.slf4j.Marker; -import org.slf4j.ext.XLogger; -import org.slf4j.ext.XLoggerFactory; -import org.slf4j.spi.LocationAwareLogger; - -/** - * - */ -public class LoggerTest { - - private static final String CONFIG = "log4j-test1.xml"; - - @ClassRule - public static LoggerContextRule ctx = new LoggerContextRule(CONFIG); - - Logger logger = LoggerFactory.getLogger("LoggerTest"); - XLogger xlogger = XLoggerFactory.getXLogger("LoggerTest"); - - @Test - public void basicFlow() { - xlogger.entry(); - verify("List", "o.a.l.s.LoggerTest entry MDC{}" + Strings.LINE_SEPARATOR); - xlogger.exit(); - verify("List", "o.a.l.s.LoggerTest exit MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void simpleFlow() { - xlogger.entry(CONFIG); - verify("List", "o.a.l.s.LoggerTest entry with (log4j-test1.xml) MDC{}" + Strings.LINE_SEPARATOR); - xlogger.exit(0); - verify("List", "o.a.l.s.LoggerTest exit with (0) MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void throwing() { - xlogger.throwing(new IllegalArgumentException("Test Exception")); - verify("List", "o.a.l.s.LoggerTest throwing MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void catching() { - try { - throw new NullPointerException(); - } catch (final Exception e) { - xlogger.catching(e); - verify("List", "o.a.l.s.LoggerTest catching MDC{}" + Strings.LINE_SEPARATOR); - } - } - - @Test - public void debug() { - logger.debug("Debug message"); - verify("List", "o.a.l.s.LoggerTest Debug message MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void debugNoParms() { - logger.debug("Debug message {}"); - verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); - logger.debug("Debug message {}", (Object[]) null); - verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); - ((LocationAwareLogger)logger).log(null, Log4jLogger.class.getName(), LocationAwareLogger.DEBUG_INT, - "Debug message {}", null, null); - verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); - } - - - @Test - public void debugWithParms() { - logger.debug("Hello, {}", "World"); - verify("List", "o.a.l.s.LoggerTest Hello, World MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void mdc() { - - MDC.put("TestYear", "2010"); - logger.debug("Debug message"); - verify("List", "o.a.l.s.LoggerTest Debug message MDC{TestYear=2010}" + Strings.LINE_SEPARATOR); - MDC.clear(); - logger.debug("Debug message"); - verify("List", "o.a.l.s.LoggerTest Debug message MDC{}" + Strings.LINE_SEPARATOR); - } - - /** - * @see LOG4J2-793 - */ - @Test - public void supportsCustomSLF4JMarkers() { - final Marker marker = new CustomFlatMarker("TEST"); - logger.debug(marker, "Test"); - verify("List", "o.a.l.s.LoggerTest Test MDC{}" + Strings.LINE_SEPARATOR); - } - - @Test - public void testRootLogger() { - final Logger l = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); - assertNotNull("No Root Logger", l); - assertEquals(Logger.ROOT_LOGGER_NAME, l.getName()); - } - - @Test - public void doubleSubst() { - logger.debug("Hello, {}", "Log4j {}"); - verify("List", "o.a.l.s.LoggerTest Hello, Log4j {} MDC{}" + Strings.LINE_SEPARATOR); - xlogger.debug("Hello, {}", "Log4j {}"); - verify("List", "o.a.l.s.LoggerTest Hello, Log4j Log4j {} MDC{}" + Strings.LINE_SEPARATOR); - } - - private void verify(final String name, final String expected) { - final ListAppender listApp = ctx.getListAppender(name); - assertNotNull("Missing Appender", listApp); - final List events = listApp.getMessages(); - assertTrue("Incorrect number of messages. Expected 1 Actual " + events.size(), events.size()== 1); - final String actual = events.get(0); - assertEquals("Incorrect message. Expected " + expected + ". Actual " + actual, expected, actual); - listApp.clear(); - } - - @Before - @After - public void cleanup() { - MDC.clear(); - ctx.getListAppender("List").clear(); - } -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java deleted file mode 100644 index c078fe0..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.Marker; -import org.apache.logging.log4j.MarkerManager; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * - */ -public class MarkerTest { - - private static final String CHILD_MAKER_NAME = MarkerTest.class.getSimpleName() + "-TEST"; - private static final String PARENT_MARKER_NAME = MarkerTest.class.getSimpleName() + "-PARENT"; - private static Log4jMarkerFactory markerFactory; - - @BeforeClass - public static void startup() { - markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); - - } - - @Before - @After - public void clearMarkers() { - MarkerManager.clear(); - } - - @Test - public void testAddMarker() { - final String childMakerName = CHILD_MAKER_NAME + "-AM"; - final String parentMarkerName = PARENT_MARKER_NAME + "-AM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMakerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMarkerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMarkerName); - final Marker log4jMarker = MarkerManager.getMarker(childMakerName); - - assertTrue("Incorrect Marker class", slf4jMarker instanceof Log4jMarker); - assertTrue(String.format("%s (log4jMarker=%s) is not an instance of %s (log4jParent=%s) in Log4j", - childMakerName, parentMarkerName, log4jMarker, log4jParent), log4jMarker.isInstanceOf(log4jParent)); - assertTrue(String.format("%s (slf4jMarker=%s) is not an instance of %s (log4jParent=%s) in SLF4J", - childMakerName, parentMarkerName, slf4jMarker, slf4jParent), slf4jMarker.contains(slf4jParent)); - } - - @Test - public void testAddNullMarker() { - final String childMarkerName = CHILD_MAKER_NAME + "-ANM"; - final String parentMakerName = PARENT_MARKER_NAME + "-ANM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); - final org.slf4j.Marker nullMarker = null; - try { - log4jSlf4jParent.add(nullMarker); - fail("Expected " + IllegalArgumentException.class.getName()); - } catch (final IllegalArgumentException e) { - // expected - } - try { - log4jSlf4jMarker.add(nullMarker); - fail("Expected " + IllegalArgumentException.class.getName()); - } catch (final IllegalArgumentException e) { - // expected - } - } - - @Test - public void testAddSameMarker() { - final String childMarkerName = CHILD_MAKER_NAME + "-ASM"; - final String parentMakerName = PARENT_MARKER_NAME + "-ASM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - assertTrue(String.format("%s (log4jMarker=%s) is not an instance of %s (log4jParent=%s) in Log4j", - childMarkerName, parentMakerName, log4jMarker, log4jParent), log4jMarker.isInstanceOf(log4jParent)); - assertTrue(String.format("%s (slf4jMarker=%s) is not an instance of %s (log4jParent=%s) in SLF4J", - childMarkerName, parentMakerName, slf4jMarker, slf4jParent), slf4jMarker.contains(slf4jParent)); - } - - @Test - public void testEquals() { - final String childMarkerName = CHILD_MAKER_NAME + "-ASM"; - final String parentMakerName = PARENT_MARKER_NAME + "-ASM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jMarker2 = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Marker log4jMarker2 = MarkerManager.getMarker(childMarkerName); - assertEquals(log4jParent, log4jParent); - assertEquals(log4jMarker, log4jMarker); - assertEquals(log4jMarker, log4jMarker2); - assertEquals(slf4jMarker, slf4jMarker2); - assertNotEquals(log4jParent, log4jMarker); - assertNotEquals(log4jMarker, log4jParent); - } - - @Test - public void testContainsNullMarker() { - final String childMarkerName = CHILD_MAKER_NAME + "-CM"; - final String parentMakerName = PARENT_MARKER_NAME + "-CM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); - final org.slf4j.Marker nullMarker = null; - try { - Assert.assertFalse(log4jSlf4jParent.contains(nullMarker)); - fail("Expected " + IllegalArgumentException.class.getName()); - } catch (final IllegalArgumentException e) { - // expected - } - try { - Assert.assertFalse(log4jSlf4jMarker.contains(nullMarker)); - fail("Expected " + IllegalArgumentException.class.getName()); - } catch (final IllegalArgumentException e) { - // expected - } - } - - @Test - public void testContainsNullString() { - final String childMarkerName = CHILD_MAKER_NAME + "-CS"; - final String parentMakerName = PARENT_MARKER_NAME + "-CS"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); - final String nullStr = null; - Assert.assertFalse(log4jSlf4jParent.contains(nullStr)); - Assert.assertFalse(log4jSlf4jMarker.contains(nullStr)); - } - - @Test - public void testRemoveNullMarker() { - final String childMakerName = CHILD_MAKER_NAME + "-CM"; - final String parentMakerName = PARENT_MARKER_NAME + "-CM"; - final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMakerName); - final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); - slf4jMarker.add(slf4jParent); - final Marker log4jParent = MarkerManager.getMarker(parentMakerName); - final Marker log4jMarker = MarkerManager.getMarker(childMakerName); - final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); - final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); - final org.slf4j.Marker nullMarker = null; - Assert.assertFalse(log4jSlf4jParent.remove(nullMarker)); - Assert.assertFalse(log4jSlf4jMarker.remove(nullMarker)); - } - -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java deleted file mode 100644 index bcb9d0b..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import org.apache.logging.log4j.LoggingException; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.junit.Assert.fail; - -/** - * Tests StackOverflow when slf4j-impl and to-slf4j are both present. - */ -public class OverflowTest { - - @Test - public void log() { - try { - final Logger logger = LoggerFactory.getLogger(OverflowTest.class); - fail("Failed to detect inclusion of log4j-to-slf4j"); - } catch (LoggingException ex) { - // Expected exception. - } catch (StackOverflowError error) { - fail("Failed to detect inclusion of log4j-to-slf4j, caught StackOverflowError"); - } - } - -} diff --git a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java b/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java deleted file mode 100644 index d3d3db6..0000000 --- a/log4j-slf4j18-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.slf4j; - -import java.io.Serializable; - -import org.apache.logging.log4j.junit.LoggerContextRule; -import org.junit.ClassRule; -import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import static org.apache.logging.log4j.SerializableMatchers.serializesRoundTrip; -import static org.junit.Assert.*; - -/** - * - */ -public class SerializeTest { - - private static final String CONFIG = "log4j-test1.xml"; - - @ClassRule - public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG); - - Logger logger = LoggerFactory.getLogger("LoggerTest"); - - @Test - public void testLogger() throws Exception { - assertThat((Serializable) logger, serializesRoundTrip()); - } -} diff --git a/log4j-slf4j18-impl/src/test/resources/log4j-test1.xml b/log4j-slf4j18-impl/src/test/resources/log4j-test1.xml deleted file mode 100644 index 07a2be6..0000000 --- a/log4j-slf4j18-impl/src/test/resources/log4j-test1.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - target/test.log - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - - - diff --git a/log4j-slf4j18-impl/src/test/resources/log4j2-1482.xml b/log4j-slf4j18-impl/src/test/resources/log4j2-1482.xml deleted file mode 100644 index e17953c..0000000 --- a/log4j-slf4j18-impl/src/test/resources/log4j2-1482.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - target/log4j2-1482 - audit - param1,param2,param3${sys:line.separator} - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/log4j-slf4j2-impl/pom.xml b/log4j-slf4j2-impl/pom.xml new file mode 100644 index 0000000..de6ce52 --- /dev/null +++ b/log4j-slf4j2-impl/pom.xml @@ -0,0 +1,268 @@ + + + + 4.0.0 + + org.apache.logging.log4j + log4j + 2.19.0 + + log4j-slf4j2-impl + jar + Apache Log4j SLF4J 2.0 Binding + The Apache Log4j SLF4J 2.0 API binding to Log4j 2 Core + + ${basedir}/.. + SLF4J Documentation + /slf4j2-impl + 2.0.0 + org.apache.logging.log4j.slf4j + true + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-ext + ${slf4j.version} + true + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + test + + + org.apache.logging.log4j + log4j-api + test-jar + test + + + org.apache.commons + commons-lang3 + test + + + org.apache.commons + commons-csv + test + + + org.apache.logging.log4j + log4j-core + test-jar + test + + + org.apache.logging.log4j + log4j-to-slf4j + ${project.version} + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.assertj + assertj-core + test + + + + + + + org.apache.maven.plugins + maven-remote-resources-plugin + + + + process + + + false + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + loop-test + test + + test + + + + **/OverflowTest.java + + junit-vintage + + + + default-test + test + + test + + + + **/*Test.java + + + **/OverflowTest.java + + + org.apache.logging.log4j:log4j-to-slf4j + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.apache.logging.slf4j, + org.slf4j.impl + + + osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)" + + + osgi.serviceloader;osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider + + + + + + + + + + org.apache.maven.plugins + maven-changes-plugin + ${changes.plugin.version} + + + + changes-report + + + + + %URL%/show_bug.cgi?id=%ISSUE% + true + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.plugin.version} + + + ${log4jParentDir}/checkstyle.xml + ${log4jParentDir}/checkstyle-suppressions.xml + false + basedir=${basedir} + licensedir=${log4jParentDir}/checkstyle-header.txt + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${javadoc.plugin.version} + + Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.
    + Apache Logging, Apache Log4j, Log4j, Apache, the Apache feather logo, the Apache Logging project logo, + and the Apache Log4j logo are trademarks of The Apache Software Foundation.

    ]]>
    + + false + true +
    + + + non-aggregate + + javadoc + + + +
    + + com.github.spotbugs + spotbugs-maven-plugin + + + org.apache.maven.plugins + maven-jxr-plugin + ${jxr.plugin.version} + + + non-aggregate + + jxr + + + + aggregate + + aggregate + + + + + + org.apache.maven.plugins + maven-pmd-plugin + ${pmd.plugin.version} + + ${maven.compiler.target} + + +
    +
    +
    + diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jEventBuilder.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jEventBuilder.java new file mode 100644 index 0000000..2d50d10 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jEventBuilder.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import org.apache.logging.log4j.BridgeAware; +import org.apache.logging.log4j.CloseableThreadContext; +import org.apache.logging.log4j.CloseableThreadContext.Instance; +import org.apache.logging.log4j.LogBuilder; +import org.slf4j.Marker; +import org.slf4j.spi.LoggingEventBuilder; + +public class Log4jEventBuilder implements LoggingEventBuilder { + + private static final String FQCN = Log4jEventBuilder.class.getName(); + + private final Log4jMarkerFactory markerFactory; + private final LogBuilder logBuilder; + private final List arguments = new ArrayList<>(); + private String message = null; + private Map keyValuePairs = null; + + public Log4jEventBuilder(final Log4jMarkerFactory markerFactory, final LogBuilder logBuilder) { + this.markerFactory = markerFactory; + this.logBuilder = logBuilder; + if (logBuilder instanceof BridgeAware) { + ((BridgeAware) logBuilder).setEntryPoint(FQCN); + } + } + + @Override + public LoggingEventBuilder setCause(Throwable cause) { + logBuilder.withThrowable(cause); + return this; + } + + @Override + public LoggingEventBuilder addMarker(Marker marker) { + logBuilder.withMarker(markerFactory.getLog4jMarker(marker)); + return this; + } + + @Override + public LoggingEventBuilder addArgument(Object p) { + arguments.add(p); + return this; + } + + @Override + public LoggingEventBuilder addArgument(Supplier objectSupplier) { + arguments.add(objectSupplier.get()); + return this; + } + + @Override + public LoggingEventBuilder addKeyValue(String key, Object value) { + if (keyValuePairs == null) { + keyValuePairs = new HashMap<>(); + } + keyValuePairs.put(key, String.valueOf(value)); + return this; + } + + @Override + public LoggingEventBuilder addKeyValue(String key, Supplier valueSupplier) { + if (keyValuePairs == null) { + keyValuePairs = new HashMap<>(); + } + keyValuePairs.put(key, String.valueOf(valueSupplier.get())); + return this; + } + + @Override + public LoggingEventBuilder setMessage(String message) { + this.message = message; + return this; + } + + @Override + public LoggingEventBuilder setMessage(Supplier messageSupplier) { + this.message = messageSupplier.get(); + return this; + } + + @Override + public void log() { + if (keyValuePairs == null || keyValuePairs.isEmpty()) { + logBuilder.log(message, arguments.toArray()); + } else { + try (Instance c = CloseableThreadContext.putAll(keyValuePairs)) { + logBuilder.log(message, arguments.toArray()); + } + } + } + + @Override + public void log(String message) { + setMessage(message); + log(); + } + + @Override + public void log(String message, Object arg) { + setMessage(message); + addArgument(arg); + log(); + } + + @Override + public void log(String message, Object arg0, Object arg1) { + setMessage(message); + addArgument(arg0); + addArgument(arg1); + log(); + } + + @Override + public void log(String message, Object... args) { + setMessage(message); + for (final Object arg : args) { + addArgument(arg); + } + log(); + } + + @Override + public void log(Supplier messageSupplier) { + setMessage(messageSupplier); + log(); + } + +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java new file mode 100644 index 0000000..2b601be --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java @@ -0,0 +1,468 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.ParameterizedMessage; +import org.apache.logging.log4j.message.SimpleMessage; +import org.apache.logging.log4j.spi.ExtendedLogger; +import org.slf4j.Marker; +import org.slf4j.spi.LocationAwareLogger; +import org.slf4j.spi.LoggingEventBuilder; +import org.slf4j.spi.NOPLoggingEventBuilder; + +/** + * SLF4J logger implementation that uses Log4j. + */ +public class Log4jLogger implements LocationAwareLogger, Serializable { + + public static final String FQCN = Log4jLogger.class.getName(); + + private static final long serialVersionUID = 7869000638091304316L; + private transient ExtendedLogger logger; + private final String name; + private transient Log4jMarkerFactory markerFactory; + + public Log4jLogger(final Log4jMarkerFactory markerFactory, final ExtendedLogger logger, final String name) { + this.markerFactory = markerFactory; + this.logger = logger; + this.name = name; + } + + @Override + public void trace(final String format) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format); + } + + @Override + public void trace(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, o); + } + + @Override + public void trace(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, arg1, arg2); + } + + @Override + public void trace(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, args); + } + + @Override + public void trace(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.TRACE, null, format, t); + } + + @Override + public boolean isTraceEnabled() { + return logger.isEnabled(Level.TRACE, null, null); + } + + @Override + public boolean isTraceEnabled(final Marker marker) { + return logger.isEnabled(Level.TRACE, markerFactory.getLog4jMarker(marker), null); + } + + @Override + public void trace(final Marker marker, final String s) { + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s); + } + + @Override + public void trace(final Marker marker, final String s, final Object o) { + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, o); + } + + @Override + public void trace(final Marker marker, final String s, final Object o, final Object o1) { + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, o, o1); + } + + @Override + public void trace(final Marker marker, final String s, final Object... objects) { + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, objects); + } + + @Override + public void trace(final Marker marker, final String s, final Throwable throwable) { + logger.logIfEnabled(FQCN, Level.TRACE, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void debug(final String format) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format); + } + + @Override + public void debug(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, o); + } + + @Override + public void debug(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, arg1, arg2); + } + + @Override + public void debug(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, args); + } + + @Override + public void debug(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.DEBUG, null, format, t); + } + + @Override + public boolean isDebugEnabled() { + return logger.isEnabled(Level.DEBUG, null, null); + } + + @Override + public boolean isDebugEnabled(final Marker marker) { + return logger.isEnabled(Level.DEBUG, markerFactory.getLog4jMarker(marker), null); + } + + @Override + public void debug(final Marker marker, final String s) { + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s); + } + + @Override + public void debug(final Marker marker, final String s, final Object o) { + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, o); + } + + @Override + public void debug(final Marker marker, final String s, final Object o, final Object o1) { + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, o, o1); + } + + @Override + public void debug(final Marker marker, final String s, final Object... objects) { + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, objects); + } + + @Override + public void debug(final Marker marker, final String s, final Throwable throwable) { + logger.logIfEnabled(FQCN, Level.DEBUG, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void info(final String format) { + logger.logIfEnabled(FQCN, Level.INFO, null, format); + } + + @Override + public void info(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, o); + } + + @Override + public void info(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, arg1, arg2); + } + + @Override + public void info(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, args); + } + + @Override + public void info(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.INFO, null, format, t); + } + + @Override + public boolean isInfoEnabled() { + return logger.isEnabled(Level.INFO, null, null); + } + + @Override + public boolean isInfoEnabled(final Marker marker) { + return logger.isEnabled(Level.INFO, markerFactory.getLog4jMarker(marker), null); + } + + @Override + public void info(final Marker marker, final String s) { + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s); + } + + @Override + public void info(final Marker marker, final String s, final Object o) { + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, o); + } + + @Override + public void info(final Marker marker, final String s, final Object o, final Object o1) { + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, o, o1); + } + + @Override + public void info(final Marker marker, final String s, final Object... objects) { + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, objects); + } + + @Override + public void info(final Marker marker, final String s, final Throwable throwable) { + logger.logIfEnabled(FQCN, Level.INFO, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void warn(final String format) { + logger.logIfEnabled(FQCN, Level.WARN, null, format); + } + + @Override + public void warn(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, o); + } + + @Override + public void warn(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, arg1, arg2); + } + + @Override + public void warn(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, args); + } + + @Override + public void warn(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.WARN, null, format, t); + } + + @Override + public boolean isWarnEnabled() { + return logger.isEnabled(Level.WARN, null, null); + } + + @Override + public boolean isWarnEnabled(final Marker marker) { + return logger.isEnabled(Level.WARN, markerFactory.getLog4jMarker(marker), null); + } + + @Override + public void warn(final Marker marker, final String s) { + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s); + } + + @Override + public void warn(final Marker marker, final String s, final Object o) { + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, o); + } + + @Override + public void warn(final Marker marker, final String s, final Object o, final Object o1) { + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, o, o1); + } + + @Override + public void warn(final Marker marker, final String s, final Object... objects) { + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, objects); + } + + @Override + public void warn(final Marker marker, final String s, final Throwable throwable) { + logger.logIfEnabled(FQCN, Level.WARN, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void error(final String format) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format); + } + + @Override + public void error(final String format, final Object o) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, o); + } + + @Override + public void error(final String format, final Object arg1, final Object arg2) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, arg1, arg2); + } + + @Override + public void error(final String format, final Object... args) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, args); + } + + @Override + public void error(final String format, final Throwable t) { + logger.logIfEnabled(FQCN, Level.ERROR, null, format, t); + } + + @Override + public boolean isErrorEnabled() { + return logger.isEnabled(Level.ERROR, null, null); + } + + @Override + public boolean isErrorEnabled(final Marker marker) { + return logger.isEnabled(Level.ERROR, markerFactory.getLog4jMarker(marker), null); + } + + @Override + public void error(final Marker marker, final String s) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s); + } + + @Override + public void error(final Marker marker, final String s, final Object o) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, o); + } + + @Override + public void error(final Marker marker, final String s, final Object o, final Object o1) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, o, o1); + } + + @Override + public void error(final Marker marker, final String s, final Object... objects) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, objects); + } + + @Override + public void error(final Marker marker, final String s, final Throwable throwable) { + logger.logIfEnabled(FQCN, Level.ERROR, markerFactory.getLog4jMarker(marker), s, throwable); + } + + @Override + public void log(final Marker marker, final String fqcn, final int level, final String message, final Object[] params, final Throwable throwable) { + final Level log4jLevel = getLevel(level); + final org.apache.logging.log4j.Marker log4jMarker = markerFactory.getLog4jMarker(marker); + + if (!logger.isEnabled(log4jLevel, log4jMarker, message, params)) { + return; + } + final Message msg; + final Throwable actualThrowable; + if (params == null) { + msg = new SimpleMessage(message); + actualThrowable = throwable; + } else { + msg = new ParameterizedMessage(message, params, throwable); + actualThrowable = throwable != null ? throwable : msg.getThrowable(); + } + logger.logMessage(fqcn, log4jLevel, log4jMarker, msg, actualThrowable); + } + + @Override + public String getName() { + return name; + } + + /** + * Always treat de-serialization as a full-blown constructor, by validating the final state of + * the de-serialized object. + */ + private void readObject(final ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { + // always perform the default de-serialization first + aInputStream.defaultReadObject(); + logger = LogManager.getContext().getLogger(name); + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); + } + + /** + * This is the default implementation of writeObject. Customise if necessary. + */ + private void writeObject(final ObjectOutputStream aOutputStream) throws IOException { + // perform the default serialization for all non-transient, non-static fields + aOutputStream.defaultWriteObject(); + } + + private static Level getLevel(final int i) { + switch (i) { + case TRACE_INT: + return Level.TRACE; + case DEBUG_INT: + return Level.DEBUG; + case INFO_INT: + return Level.INFO; + case WARN_INT: + return Level.WARN; + case ERROR_INT: + return Level.ERROR; + } + return Level.ERROR; + } + + @Override + public LoggingEventBuilder makeLoggingEventBuilder(org.slf4j.event.Level level) { + final Level log4jLevel = getLevel(level.toInt()); + if (logger.isEnabled(log4jLevel)) { + return new Log4jEventBuilder(markerFactory, logger.atLevel(log4jLevel)); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public LoggingEventBuilder atTrace() { + if (logger.isTraceEnabled()) { + return new Log4jEventBuilder(markerFactory, logger.atTrace()); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public LoggingEventBuilder atDebug() { + if (logger.isDebugEnabled()) { + return new Log4jEventBuilder(markerFactory, logger.atDebug()); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public LoggingEventBuilder atInfo() { + if (logger.isInfoEnabled()) { + return new Log4jEventBuilder(markerFactory, logger.atInfo()); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public LoggingEventBuilder atWarn() { + if (logger.isWarnEnabled()) { + return new Log4jEventBuilder(markerFactory, logger.atWarn()); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public LoggingEventBuilder atError() { + if (logger.isErrorEnabled()) { + return new Log4jEventBuilder(markerFactory, logger.atError()); + } + return NOPLoggingEventBuilder.singleton(); + } + + @Override + public boolean isEnabledForLevel(org.slf4j.event.Level level) { + return logger.isEnabled(getLevel(level.toInt())); + } + + +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java new file mode 100644 index 0000000..f173301 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.LoggingException; +import org.apache.logging.log4j.spi.AbstractLoggerAdapter; +import org.apache.logging.log4j.spi.LoggerContext; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.StackLocatorUtil; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; + +import java.util.function.Predicate; + +/** + * Log4j implementation of SLF4J ILoggerFactory interface. + */ +public class Log4jLoggerFactory extends AbstractLoggerAdapter implements ILoggerFactory { + + private static final StatusLogger LOGGER = StatusLogger.getLogger(); + private static final String SLF4J_PACKAGE = "org.slf4j"; + private static final Predicate> CALLER_PREDICATE = clazz -> + !AbstractLoggerAdapter.class.equals(clazz) && !clazz.getName().startsWith(SLF4J_PACKAGE); + private static final String TO_SLF4J_CONTEXT = "org.apache.logging.slf4j.SLF4JLoggerContext"; + + private final Log4jMarkerFactory markerFactory; + + public Log4jLoggerFactory(final Log4jMarkerFactory markerFactory) { + this.markerFactory = markerFactory; + } + + @Override + protected Logger newLogger(final String name, final LoggerContext context) { + final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name; + return new Log4jLogger(markerFactory, validateContext(context).getLogger(key), name); + } + + @Override + protected LoggerContext getContext() { + final Class anchor = LogManager.getFactory().isClassLoaderDependent() + ? StackLocatorUtil.getCallerClass(Log4jLoggerFactory.class, CALLER_PREDICATE) + : null; + LOGGER.trace("Log4jLoggerFactory.getContext() found anchor {}", anchor); + return anchor == null + ? LogManager.getContext(false) + : getContext(anchor); + } + + Log4jMarkerFactory getMarkerFactory() { + return markerFactory; + } + + private LoggerContext validateContext(final LoggerContext context) { + if (TO_SLF4J_CONTEXT.equals(context.getClass().getName())) { + throw new LoggingException("log4j-slf4j-impl cannot be present with log4j-to-slf4j"); + } + return context; + } +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java new file mode 100644 index 0000000..71e5a04 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.ThreadContext; +import org.apache.logging.log4j.ThreadContext.ContextStack; +import org.apache.logging.log4j.status.StatusLogger; +import org.slf4j.spi.MDCAdapter; + +/** + * + */ +public class Log4jMDCAdapter implements MDCAdapter { + + private static Logger LOGGER = StatusLogger.getLogger(); + + private final ThreadLocalMapOfStacks mapOfStacks = new ThreadLocalMapOfStacks(); + + @Override + public void put(final String key, final String val) { + ThreadContext.put(key, val); + } + + @Override + public String get(final String key) { + return ThreadContext.get(key); + } + + @Override + public void remove(final String key) { + ThreadContext.remove(key); + } + + @Override + public void clear() { + ThreadContext.clearMap(); + } + + @Override + public Map getCopyOfContextMap() { + return ThreadContext.getContext(); + } + + @Override + public void setContextMap(final Map map) { + ThreadContext.clearMap(); + ThreadContext.putAll(map); + } + + @Override + public void pushByKey(String key, String value) { + if (key == null) { + ThreadContext.push(value); + } else { + final String oldValue = mapOfStacks.peekByKey(key); + if (!Objects.equals(ThreadContext.get(key), oldValue)) { + LOGGER.warn("The key {} was used in both the string and stack-valued MDC.", key); + } + mapOfStacks.pushByKey(key, value); + ThreadContext.put(key, value); + } + } + + @Override + public String popByKey(String key) { + if (key == null) { + return ThreadContext.getDepth() > 0 ? ThreadContext.pop() : null; + } + final String value = mapOfStacks.popByKey(key); + if (!Objects.equals(ThreadContext.get(key), value)) { + LOGGER.warn("The key {} was used in both the string and stack-valued MDC.", key); + } + ThreadContext.put(key, mapOfStacks.peekByKey(key)); + return value; + } + + @Override + public Deque getCopyOfDequeByKey(String key) { + if (key == null) { + final ContextStack stack = ThreadContext.getImmutableStack(); + final Deque copy = new ArrayDeque<>(stack.size()); + stack.forEach(copy::push); + return copy; + } + return mapOfStacks.getCopyOfDequeByKey(key); + } + + @Override + public void clearDequeByKey(String key) { + if (key == null) { + ThreadContext.clearStack(); + } else { + mapOfStacks.clearByKey(key); + ThreadContext.put(key, null); + } + } + + private static class ThreadLocalMapOfStacks { + + private final ThreadLocal>> tlMapOfStacks = ThreadLocal.withInitial(HashMap::new); + + public void pushByKey(String key, String value) { + tlMapOfStacks.get() + .computeIfAbsent(key, ignored -> new ArrayDeque<>()) + .push(value); + } + + public String popByKey(String key) { + final Deque deque = tlMapOfStacks.get().get(key); + return deque != null ? deque.poll() : null; + } + + public Deque getCopyOfDequeByKey(String key) { + final Deque deque = tlMapOfStacks.get().get(key); + return deque != null ? new ArrayDeque<>(deque) : null; + } + + public void clearByKey(String key) { + final Deque deque = tlMapOfStacks.get().get(key); + if (deque != null) { + deque.clear(); + } + } + + public String peekByKey(String key) { + final Deque deque = tlMapOfStacks.get().get(key); + return deque != null ? deque.peek() : null; + } + } +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java new file mode 100644 index 0000000..fb21659 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.apache.logging.log4j.MarkerManager; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; + +/** + * Log4j/SLF4J {@link Marker} type bridge. + */ +class Log4jMarker implements Marker { + + public static final long serialVersionUID = 1590472L; + + private final IMarkerFactory factory; + + private final org.apache.logging.log4j.Marker marker; + + /** + * Constructs a Log4jMarker using an existing Log4j {@link org.apache.logging.log4j.Marker}. + * @param marker The Log4j Marker upon which to base this Marker. + */ + public Log4jMarker(final IMarkerFactory markerFactory, final org.apache.logging.log4j.Marker marker) { + this.factory = markerFactory; + this.marker = marker; + } + + @Override + public void add(final Marker marker) { + if (marker == null) { + throw new IllegalArgumentException(); + } + final Marker m = factory.getMarker(marker.getName()); + this.marker.addParents(((Log4jMarker)m).getLog4jMarker()); + } + + @Override + public boolean contains(final Marker marker) { + if (marker == null) { + throw new IllegalArgumentException(); + } + return this.marker.isInstanceOf(marker.getName()); + } + + @Override + public boolean contains(final String s) { + return s != null ? this.marker.isInstanceOf(s) : false; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof Log4jMarker)) { + return false; + } + final Log4jMarker other = (Log4jMarker) obj; + if (!Objects.equals(marker, other.marker)) { + return false; + } + return true; + } + + public org.apache.logging.log4j.Marker getLog4jMarker() { + return marker; + } + + @Override + public String getName() { + return marker.getName(); + } + + @Override + public boolean hasChildren() { + return marker.hasParents(); + } + + @Override + public int hashCode() { + return 31 + Objects.hashCode(marker); + } + + @Override + public boolean hasReferences() { + return marker.hasParents(); + } + + @Override + public Iterator iterator() { + final org.apache.logging.log4j.Marker[] log4jParents = this.marker.getParents(); + final List parents = new ArrayList<>(log4jParents.length); + for (final org.apache.logging.log4j.Marker m : log4jParents) { + parents.add(factory.getMarker(m.getName())); + } + return parents.iterator(); + } + + @Override + public boolean remove(final Marker marker) { + return marker != null ? this.marker.remove(MarkerManager.getMarker(marker.getName())) : false; + } +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java new file mode 100644 index 0000000..9073fa9 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.status.StatusLogger; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; + +/** + * Log4j/SLF4J bridge to create SLF4J Markers based on name or based on existing SLF4J Markers. + */ +public class Log4jMarkerFactory implements IMarkerFactory { + + private static final Logger LOGGER = StatusLogger.getLogger(); + + private final ConcurrentMap markerMap = new ConcurrentHashMap<>(); + + /** + * Returns a Log4j Marker that is compatible with SLF4J. + * @param name The name of the Marker. + * @return A Marker. + */ + @Override + public Marker getMarker(final String name) { + if (name == null) { + throw new IllegalArgumentException("Marker name must not be null"); + } + final Marker marker = markerMap.get(name); + if (marker != null) { + return marker; + } + final org.apache.logging.log4j.Marker log4jMarker = MarkerManager.getMarker(name); + return addMarkerIfAbsent(name, log4jMarker); + } + + private Marker addMarkerIfAbsent(final String name, final org.apache.logging.log4j.Marker log4jMarker) { + final Marker marker = new Log4jMarker(this, log4jMarker); + final Marker existing = markerMap.putIfAbsent(name, marker); + return existing == null ? marker : existing; + } + + /** + * Returns a Log4j Marker converted from an existing custom SLF4J Marker. + * @param marker The SLF4J Marker to convert. + * @return A converted Log4j/SLF4J Marker. + * @since 2.1 + */ + public Marker getMarker(final Marker marker) { + if (marker == null) { + throw new IllegalArgumentException("Marker must not be null"); + } + final Marker m = markerMap.get(marker.getName()); + if (m != null) { + return m; + } + return addMarkerIfAbsent(marker.getName(), convertMarker(marker)); + } + + /** + * Gets the Log4j2 marker associated to this SLF4J marker or creates a new one. + * + * @param marker a SLF4J marker + * @return a Log4j2 marker + */ + org.apache.logging.log4j.Marker getLog4jMarker(final Marker marker) { + if (marker == null) { + return null; + } else if (marker instanceof Log4jMarker) { + return ((Log4jMarker) marker).getLog4jMarker(); + } else { + return ((Log4jMarker) getMarker(marker)).getLog4jMarker(); + } + } + + static org.apache.logging.log4j.Marker convertMarker(final Marker original) { + if (original == null) { + throw new IllegalArgumentException("Marker must not be null"); + } + return convertMarker(original, new ArrayList()); + } + + private static org.apache.logging.log4j.Marker convertMarker(final Marker original, + final Collection visited) { + final org.apache.logging.log4j.Marker marker = MarkerManager.getMarker(original.getName()); + if (original.hasReferences()) { + final Iterator it = original.iterator(); + while (it.hasNext()) { + final Marker next = it.next(); + if (visited.contains(next)) { + LOGGER.warn("Found a cycle in Marker [{}]. Cycle will be broken.", next.getName()); + } else { + visited.add(next); + marker.addParents(convertMarker(next, visited)); + } + } + } + return marker; + } + + /** + * Returns true if the Marker exists. + * @param name The Marker name. + * @return {@code true} if the Marker exists, {@code false} otherwise. + */ + @Override + public boolean exists(final String name) { + return markerMap.containsKey(name); + } + + /** + * Log4j does not support detached Markers. This method always returns false. + * @param name The Marker name. + * @return {@code false} + */ + @Override + public boolean detachMarker(final String name) { + return false; + } + + /** + * Log4j does not support detached Markers for performance reasons. The returned Marker is attached. + * @param name The Marker name. + * @return The named Marker (unmodified). + */ + @Override + public Marker getDetachedMarker(final String name) { + LOGGER.warn("Log4j does not support detached Markers. Returned Marker [{}] will be unchanged.", name); + return getMarker(name); + } + + +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java new file mode 100644 index 0000000..0a41215 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +/** + * Exception thrown when the SLF4J adapter encounters a problem. + * + */ +public class SLF4JLoggingException extends RuntimeException { + + /** + * Generated serial version ID. + */ + private static final long serialVersionUID = -1618650972455089998L; + + public SLF4JLoggingException(final String msg) { + super(msg); + } + + public SLF4JLoggingException(final String msg, final Exception ex) { + super(msg, ex); + } + + public SLF4JLoggingException(final Exception ex) { + super(ex); + } +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java new file mode 100644 index 0000000..b711941 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import org.slf4j.ILoggerFactory; +import org.slf4j.IMarkerFactory; +import org.slf4j.spi.MDCAdapter; + +public class SLF4JServiceProvider implements org.slf4j.spi.SLF4JServiceProvider { + + public static final String REQUESTED_API_VERSION = "2.0.99"; + + private ILoggerFactory loggerFactory; + private Log4jMarkerFactory markerFactory; + private MDCAdapter mdcAdapter; + + @Override + public ILoggerFactory getLoggerFactory() { + return loggerFactory; + } + + @Override + public IMarkerFactory getMarkerFactory() { + return markerFactory; + } + + @Override + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + @Override + public String getRequestedApiVersion() { + return REQUESTED_API_VERSION; + } + + @Override + public void initialize() { + markerFactory = new Log4jMarkerFactory(); + loggerFactory = new Log4jLoggerFactory(markerFactory); + mdcAdapter = new Log4jMDCAdapter(); + } + + +} diff --git a/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java new file mode 100644 index 0000000..ec0f031 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/java/org/apache/logging/slf4j/package-info.java @@ -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. + */ +/** + * SLF4J support. Note that this does indeed share the same package namespace as the one found in log4j-to-slf4j; + * this is intentional. The two JARs should not be used at the same time! Thus, in an OSGi environment + * where split packages are not allowed, this error is prevented due to both JARs sharing an exported package name. + */ +package org.apache.logging.slf4j; diff --git a/log4j-slf4j2-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/log4j-slf4j2-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 0000000..1577f12 --- /dev/null +++ b/log4j-slf4j2-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider @@ -0,0 +1 @@ +org.apache.logging.slf4j.SLF4JServiceProvider \ No newline at end of file diff --git a/log4j-slf4j2-impl/src/site/markdown/index.md b/log4j-slf4j2-impl/src/site/markdown/index.md new file mode 100644 index 0000000..da16735 --- /dev/null +++ b/log4j-slf4j2-impl/src/site/markdown/index.md @@ -0,0 +1,49 @@ + + + +# Log4j 2 SLF4J Binding + +The Log4j 2 SLF4J Binding allows applications coded to the SLF4J API to use +Log4j 2 as the implementation. + +Due to a break in compatibility in the SLF4J binding, as of release 2.19.0 two SLF4J to Log4j Adapters are provided. + +1. `log4j-slf4j-impl` should be used with SLF4J 1.7.x releases or older. +1. `log4j-slf4j2-impl` should be used with SLF4J 2.0.x releases or newer. + +Applications that take advantage of the Java Module System should use SLF4J 2.0.x and log4j-slf4j2-impl. + +As of release 2.19.0 the `log4j-slf4j18-impl` module targetting the unreleased SLF4J 1.8.x series has been removed. + +## Requirements + +The Log4j 2 SLF4J Binding has a dependency on the Log4j 2 API as well as the SLF4J API. +For more information, see [Runtime Dependencies](../runtime-dependencies.html). + +## Usage + +The SLF4J binding provided in this component cause all the SLF4J APIs to be routed to Log4j 2. Simply +include the Log4j 2 SLF4J Binding jar along with the Log4j 2 jars and SLF4J API jar to cause all SLF4J +logging to be handled by Log4j 2. + +
    +Use of the Log4j 2 SLF4J Binding (log4j-slf4j-impl-2.0.jar) together with +the SLF4J adapter (log4j-to-slf4j-2.0.jar) should +never be attempted, as it will cause events to endlessly be routed between +SLF4J and Log4j 2. +
    diff --git a/log4j-slf4j2-impl/src/site/site.xml b/log4j-slf4j2-impl/src/site/site.xml new file mode 100644 index 0000000..a1f9106 --- /dev/null +++ b/log4j-slf4j2-impl/src/site/site.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java new file mode 100644 index 0000000..2b0fe91 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.other.pkg; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.status.StatusData; +import org.apache.logging.log4j.status.StatusListener; +import org.apache.logging.log4j.status.StatusLogger; +import org.junit.Test; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.junit.Assert.assertEquals; + +/** + * Test LoggerContext lookups by verifying the anchor class representing calling code. + */ +public class LoggerContextAnchorTest { + private static final String PREFIX = "Log4jLoggerFactory.getContext() found anchor class "; + + @Test + public void testLoggerFactoryLookupClass() { + String fqcn = getAnchorFqcn(() -> LoggerFactory.getLogger(LoggerContextAnchorTest.class)); + assertEquals(getClass().getName(), fqcn); + } + + @Test + public void testLoggerFactoryLookupString() { + String fqcn = getAnchorFqcn(() -> LoggerFactory.getLogger("custom.logger")); + assertEquals(getClass().getName(), fqcn); + } + + @Test + public void testLoggerFactoryGetILoggerFactoryLookup() { + String fqcn = getAnchorFqcn(() -> LoggerFactory.getILoggerFactory().getLogger("custom.logger")); + assertEquals(getClass().getName(), fqcn); + } + + private static String getAnchorFqcn(Runnable runnable) { + List results = new CopyOnWriteArrayList<>(); + StatusListener listener = new StatusListener() { + @Override + public void log(StatusData data) { + String formattedMessage = data.getMessage().getFormattedMessage(); + if (formattedMessage.startsWith(PREFIX)) { + results.add(formattedMessage.substring(PREFIX.length())); + } + } + + @Override + public Level getStatusLevel() { + return Level.TRACE; + } + + @Override + public void close() { + // nop + } + }; + StatusLogger statusLogger = StatusLogger.getLogger(); + statusLogger.registerListener(listener); + try { + runnable.run(); + if (results.isEmpty()) { + throw new AssertionError("Failed to locate an anchor lookup status message"); + } + if (results.size() > 1) { + throw new AssertionError("Found multiple anchor lines: " + results); + } + return results.get(0); + } finally { + statusLogger.removeListener(listener); + } + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java new file mode 100644 index 0000000..ea399b2 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class CallerInformationTest { + + // config from log4j-core test-jar + private static final String CONFIG = "log4j2-calling-class.xml"; + + @ClassRule + public static final LoggerContextRule ctx = new LoggerContextRule(CONFIG); + + @Test + public void testClassLogger() throws Exception { + final ListAppender app = ctx.getListAppender("Class").clear(); + final Logger logger = LoggerFactory.getLogger("ClassLogger"); + logger.info("Ignored message contents."); + logger.warn("Verifying the caller class is still correct."); + logger.error("Hopefully nobody breaks me!"); + logger.atInfo().log("Ignored message contents."); + logger.atWarn().log("Verifying the caller class is still correct."); + logger.atError().log("Hopefully nobody breaks me!"); + final List messages = app.getMessages(); + assertEquals("Incorrect number of messages.", 6, messages.size()); + for (final String message : messages) { + assertEquals("Incorrect caller class name.", this.getClass().getName(), message); + } + } + + @Test + public void testMethodLogger() throws Exception { + final ListAppender app = ctx.getListAppender("Method").clear(); + final Logger logger = LoggerFactory.getLogger("MethodLogger"); + logger.info("More messages."); + logger.warn("CATASTROPHE INCOMING!"); + logger.error("ZOMBIES!!!"); + logger.warn("brains~~~"); + logger.info("Itchy. Tasty."); + logger.atInfo().log("More messages."); + logger.atWarn().log("CATASTROPHE INCOMING!"); + logger.atError().log("ZOMBIES!!!"); + logger.atWarn().log("brains~~~"); + logger.atInfo().log("Itchy. Tasty."); + final List messages = app.getMessages(); + assertEquals("Incorrect number of messages.", 10, messages.size()); + for (final String message : messages) { + assertEquals("Incorrect caller method name.", "testMethodLogger", message); + } + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java new file mode 100644 index 0000000..0138cea --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.slf4j; + +import java.util.Iterator; + +import org.slf4j.Marker; + +/** + * Test Marker that may contain no reference/parent Markers. + * @see LOG4J2-793 + */ +public class CustomFlatMarker implements Marker { + private static final long serialVersionUID = -4115520883240247266L; + + private final String name; + + public CustomFlatMarker(final String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void add(final Marker reference) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Marker reference) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean hasChildren() { + return hasReferences(); + } + + @Override + public boolean hasReferences() { + return false; + } + + @Override + public Iterator iterator() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean contains(final Marker other) { + return false; + } + + @Override + public boolean contains(final String name) { + return false; + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java new file mode 100644 index 0000000..59f69e0 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.*; + +/** + * Tests logging during shutdown. + */ +public class Log4j1222Test +{ + + @Test + public void homepageRendersSuccessfully() + { + System.setProperty("log4j.configurationFile", "log4j2-console.xml"); + Runtime.getRuntime().addShutdownHook(new ShutdownHook()); + } + + private static class ShutdownHook extends Thread { + + private static class Holder { + private static final Logger LOGGER = LoggerFactory.getLogger(Log4j1222Test.class); + } + + @Override + public void run() + { + super.run(); + trigger(); + } + + private void trigger() { + Holder.LOGGER.info("Attempt to trigger"); + assertTrue("Logger is of type " + Holder.LOGGER.getClass().getName(), Holder.LOGGER instanceof Log4jLogger); + + } + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java new file mode 100644 index 0000000..8934f29 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ + +package org.apache.logging.slf4j; + +import org.apache.logging.log4j.core.layout.Log4j2_1482_Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tests https://issues.apache.org/jira/browse/LOG4J2-1482 + */ +public class Log4j2_1482_Slf4jTest extends Log4j2_1482_Test { + + @Override + protected void log(final int runNumber) { + if (runNumber == 2) { + // System.out.println("Set a breakpoint here."); + } + final Logger logger = LoggerFactory.getLogger("auditcsvfile"); + final int val1 = 9, val2 = 11, val3 = 12; + logger.info("Info Message!", val1, val2, val3); + logger.info("Info Message!", val1, val2, val3); + logger.info("Info Message!", val1, val2, val3); + } + +} \ No newline at end of file diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jEventBuilderTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jEventBuilderTest.java new file mode 100644 index 0000000..7534618 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jEventBuilderTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.junit.LoggerContextSource; +import org.apache.logging.log4j.junit.Named; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// Config from `log4j-core` test jar. +@LoggerContextSource("log4j2-config.xml") +public class Log4jEventBuilderTest { + + private final Logger logger; + private final ListAppender appender; + + public Log4jEventBuilderTest(@Named("List") Appender appender) { + logger = LoggerFactory.getLogger("org.apache.test.Log4jEventBuilderTest"); + this.appender = (ListAppender) appender; + } + + @BeforeEach + public void setUp() { + appender.clear(); + } + + @Test + public void testKeyValuePairs() { + logger.atDebug().addKeyValue("testKeyValuePairs", "ok").log(); + final List events = appender.getEvents(); + assertThat(events).hasSize(1); + assertThat(events.get(0).getContextData().toMap()).containsEntry("testKeyValuePairs", "ok"); + } + + @Test + public void testArguments() { + logger.atDebug().setMessage("{}-{}").addArgument("a").addArgument("b").log(); + logger.atDebug().log("{}-{}", "a", "b"); + logger.atDebug().addArgument("a").log("{}-{}", "b"); + logger.atDebug().log("{}-{}", new Object[] {"a", "b"}); + assertThat(appender.getEvents()).hasSize(4) + .allMatch(event -> "a-b".equals(event.getMessage().getFormattedMessage())); + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMDCAdapterTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMDCAdapterTest.java new file mode 100644 index 0000000..4fa9038 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMDCAdapterTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +public class Log4jMDCAdapterTest { + + private static final Log4jMDCAdapter MDC_ADAPTER = new Log4jMDCAdapter(); + private static final String KEY = "Log4j2"; + + private static Deque createDeque(int size) { + final Deque result = new ArrayDeque<>(size); + IntStream.range(0, size).mapToObj(Integer::toString).forEach(result::addLast); + return result; + } + + private static Deque popDeque(String key) { + final Deque result = new ArrayDeque<>(); + String value; + while ((value = MDC_ADAPTER.popByKey(key)) != null) { + result.addLast(value); + } + return result; + } + + static Stream keys() { + return Stream.of(KEY, "", null); + } + + @ParameterizedTest + @MethodSource("keys") + public void testPushPopByKey(final String key) { + MDC_ADAPTER.clearDequeByKey(key); + final Deque expectedValues = createDeque(100); + expectedValues.descendingIterator().forEachRemaining(v -> MDC_ADAPTER.pushByKey(key, v)); + assertThat(MDC_ADAPTER.getCopyOfDequeByKey(key)).containsExactlyElementsOf(expectedValues); + assertThat(popDeque(key)).containsExactlyElementsOf(expectedValues); + } + +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java new file mode 100644 index 0000000..ac6ad22 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.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 org.apache.logging.slf4j; + +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class Log4jMarkerTest { + + private static Log4jMarkerFactory markerFactory; + + @BeforeClass + public static void startup() { + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); + + } + + @Test + public void testEquals() { + final Marker markerA = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-A"); + final Marker markerB = MarkerManager.getMarker(Log4jMarkerTest.class.getName() + "-B"); + final Log4jMarker marker1 = new Log4jMarker(markerFactory, markerA); + final Log4jMarker marker2 = new Log4jMarker(markerFactory, markerA); + final Log4jMarker marker3 = new Log4jMarker(markerFactory, markerB); + Assert.assertEquals(marker1, marker2); + Assert.assertNotEquals(marker1, null); + Assert.assertNotEquals(null, marker1); + Assert.assertNotEquals(marker1, marker3); + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java new file mode 100644 index 0000000..de37a36 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import org.apache.logging.log4j.core.LifeCycle; +import org.apache.logging.log4j.spi.LoggerContext; +import org.junit.Test; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +import static org.junit.Assert.assertTrue; + +/** + * Tests cleanup of the LoggerContexts. + */ +public class LoggerContextTest { + + @Test + public void testCleanup() throws Exception { + Log4jLoggerFactory factory = (Log4jLoggerFactory) LoggerFactory.getILoggerFactory(); + factory.getLogger("test"); + Set set = factory.getLoggerContexts(); + LoggerContext ctx1 = set.toArray(LoggerContext.EMPTY_ARRAY)[0]; + assertTrue("LoggerContext is not enabled for shutdown", ctx1 instanceof LifeCycle); + ((LifeCycle) ctx1).stop(); + set = factory.getLoggerContexts(); + assertTrue("Expected no LoggerContexts", set.isEmpty()); + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java new file mode 100644 index 0000000..c8fe9d3 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.apache.logging.log4j.test.appender.ListAppender; +import org.apache.logging.log4j.util.Strings; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; +import org.slf4j.spi.LocationAwareLogger; + +/** + * + */ +public class LoggerTest { + + private static final String CONFIG = "log4j-test1.xml"; + + @ClassRule + public static LoggerContextRule ctx = new LoggerContextRule(CONFIG); + + Logger logger = LoggerFactory.getLogger("LoggerTest"); + XLogger xlogger = XLoggerFactory.getXLogger("LoggerTest"); + + @Test + public void basicFlow() { + xlogger.entry(); + verify("List", "o.a.l.s.LoggerTest entry MDC{}" + Strings.LINE_SEPARATOR); + xlogger.exit(); + verify("List", "o.a.l.s.LoggerTest exit MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void simpleFlow() { + xlogger.entry(CONFIG); + verify("List", "o.a.l.s.LoggerTest entry with (log4j-test1.xml) MDC{}" + Strings.LINE_SEPARATOR); + xlogger.exit(0); + verify("List", "o.a.l.s.LoggerTest exit with (0) MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void throwing() { + xlogger.throwing(new IllegalArgumentException("Test Exception")); + verify("List", "o.a.l.s.LoggerTest throwing MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void catching() { + try { + throw new NullPointerException(); + } catch (final Exception e) { + xlogger.catching(e); + verify("List", "o.a.l.s.LoggerTest catching MDC{}" + Strings.LINE_SEPARATOR); + } + } + + @Test + public void debug() { + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void debugNoParms() { + logger.debug("Debug message {}"); + verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); + logger.debug("Debug message {}", (Object[]) null); + verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); + ((LocationAwareLogger)logger).log(null, Log4jLogger.class.getName(), LocationAwareLogger.DEBUG_INT, + "Debug message {}", null, null); + verify("List", "o.a.l.s.LoggerTest Debug message {} MDC{}" + Strings.LINE_SEPARATOR); + } + + + @Test + public void debugWithParms() { + logger.debug("Hello, {}", "World"); + verify("List", "o.a.l.s.LoggerTest Hello, World MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void mdc() { + + MDC.put("TestYear", "2010"); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{TestYear=2010}" + Strings.LINE_SEPARATOR); + MDC.clear(); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void mdcStack() { + MDC.pushByKey("TestYear", "2010"); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{TestYear=2010}" + Strings.LINE_SEPARATOR); + MDC.pushByKey("TestYear", "2011"); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{TestYear=2011}" + Strings.LINE_SEPARATOR); + MDC.popByKey("TestYear"); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{TestYear=2010}" + Strings.LINE_SEPARATOR); + MDC.clear(); + logger.debug("Debug message"); + verify("List", "o.a.l.s.LoggerTest Debug message MDC{}" + Strings.LINE_SEPARATOR); + } + + /** + * @see LOG4J2-793 + */ + @Test + public void supportsCustomSLF4JMarkers() { + final Marker marker = new CustomFlatMarker("TEST"); + logger.debug(marker, "Test"); + verify("List", "o.a.l.s.LoggerTest Test MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void testRootLogger() { + final Logger l = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + assertNotNull("No Root Logger", l); + assertEquals(Logger.ROOT_LOGGER_NAME, l.getName()); + } + + @Test + public void doubleSubst() { + logger.debug("Hello, {}", "Log4j {}"); + verify("List", "o.a.l.s.LoggerTest Hello, Log4j {} MDC{}" + Strings.LINE_SEPARATOR); + xlogger.debug("Hello, {}", "Log4j {}"); + verify("List", "o.a.l.s.LoggerTest Hello, Log4j {} MDC{}" + Strings.LINE_SEPARATOR); + } + + @Test + public void testThrowable() { + final Throwable expected = new RuntimeException(); + logger.debug("Hello {}", expected); + verifyThrowable(expected); + logger.debug("Hello {}", (Object) expected); + verifyThrowable(null); + logger.debug("Hello", expected); + verifyThrowable(expected); + logger.debug("Hello {}! {}", "World!", expected); + verifyThrowable(null); + logger.debug("Hello {}!", "World!", expected); + verifyThrowable(expected); + final LocationAwareLogger lal = (LocationAwareLogger) logger; + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", null, expected); + verifyThrowable(expected); + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", new Object[] { expected }, + null); + verifyThrowable(null); + lal.log(null, LoggerTest.class.getName(), LocationAwareLogger.DEBUG_INT, "Hello {}", + new Object[] { "World!", expected }, null); + verifyThrowable(expected); + } + + private ListAppender getAppenderByName(final String name) { + final ListAppender listApp = ctx.getListAppender(name); + assertNotNull("Missing Appender", listApp); + return listApp; + } + + private void verify(final String name, final String expected) { + final ListAppender listApp = getAppenderByName(name); + final List events = listApp.getMessages(); + assertTrue("Incorrect number of messages. Expected 1 Actual " + events.size(), events.size()== 1); + final String actual = events.get(0); + assertEquals("Incorrect message. Expected " + expected + ". Actual " + actual, expected, actual); + listApp.clear(); + } + + private void verifyThrowable(final Throwable expected) { + final ListAppender listApp = getAppenderByName("UnformattedList"); + final List events = listApp.getEvents(); + assertEquals("Incorrect number of messages", 1, events.size()); + final LogEvent actual = events.get(0); + assertEquals("Incorrect throwable.", expected, actual.getThrown()); + listApp.clear(); + } + + @Before + @After + public void cleanup() { + MDC.clear(); + ctx.getListAppender("List").clear(); + ctx.getListAppender("UnformattedList").clear(); + } +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java new file mode 100644 index 0000000..c078fe0 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + */ +public class MarkerTest { + + private static final String CHILD_MAKER_NAME = MarkerTest.class.getSimpleName() + "-TEST"; + private static final String PARENT_MARKER_NAME = MarkerTest.class.getSimpleName() + "-PARENT"; + private static Log4jMarkerFactory markerFactory; + + @BeforeClass + public static void startup() { + markerFactory = ((Log4jLoggerFactory) org.slf4j.LoggerFactory.getILoggerFactory()).getMarkerFactory(); + + } + + @Before + @After + public void clearMarkers() { + MarkerManager.clear(); + } + + @Test + public void testAddMarker() { + final String childMakerName = CHILD_MAKER_NAME + "-AM"; + final String parentMarkerName = PARENT_MARKER_NAME + "-AM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMakerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMarkerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMarkerName); + final Marker log4jMarker = MarkerManager.getMarker(childMakerName); + + assertTrue("Incorrect Marker class", slf4jMarker instanceof Log4jMarker); + assertTrue(String.format("%s (log4jMarker=%s) is not an instance of %s (log4jParent=%s) in Log4j", + childMakerName, parentMarkerName, log4jMarker, log4jParent), log4jMarker.isInstanceOf(log4jParent)); + assertTrue(String.format("%s (slf4jMarker=%s) is not an instance of %s (log4jParent=%s) in SLF4J", + childMakerName, parentMarkerName, slf4jMarker, slf4jParent), slf4jMarker.contains(slf4jParent)); + } + + @Test + public void testAddNullMarker() { + final String childMarkerName = CHILD_MAKER_NAME + "-ANM"; + final String parentMakerName = PARENT_MARKER_NAME + "-ANM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); + final org.slf4j.Marker nullMarker = null; + try { + log4jSlf4jParent.add(nullMarker); + fail("Expected " + IllegalArgumentException.class.getName()); + } catch (final IllegalArgumentException e) { + // expected + } + try { + log4jSlf4jMarker.add(nullMarker); + fail("Expected " + IllegalArgumentException.class.getName()); + } catch (final IllegalArgumentException e) { + // expected + } + } + + @Test + public void testAddSameMarker() { + final String childMarkerName = CHILD_MAKER_NAME + "-ASM"; + final String parentMakerName = PARENT_MARKER_NAME + "-ASM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); + assertTrue(String.format("%s (log4jMarker=%s) is not an instance of %s (log4jParent=%s) in Log4j", + childMarkerName, parentMakerName, log4jMarker, log4jParent), log4jMarker.isInstanceOf(log4jParent)); + assertTrue(String.format("%s (slf4jMarker=%s) is not an instance of %s (log4jParent=%s) in SLF4J", + childMarkerName, parentMakerName, slf4jMarker, slf4jParent), slf4jMarker.contains(slf4jParent)); + } + + @Test + public void testEquals() { + final String childMarkerName = CHILD_MAKER_NAME + "-ASM"; + final String parentMakerName = PARENT_MARKER_NAME + "-ASM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jMarker2 = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); + final Marker log4jMarker2 = MarkerManager.getMarker(childMarkerName); + assertEquals(log4jParent, log4jParent); + assertEquals(log4jMarker, log4jMarker); + assertEquals(log4jMarker, log4jMarker2); + assertEquals(slf4jMarker, slf4jMarker2); + assertNotEquals(log4jParent, log4jMarker); + assertNotEquals(log4jMarker, log4jParent); + } + + @Test + public void testContainsNullMarker() { + final String childMarkerName = CHILD_MAKER_NAME + "-CM"; + final String parentMakerName = PARENT_MARKER_NAME + "-CM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); + final org.slf4j.Marker nullMarker = null; + try { + Assert.assertFalse(log4jSlf4jParent.contains(nullMarker)); + fail("Expected " + IllegalArgumentException.class.getName()); + } catch (final IllegalArgumentException e) { + // expected + } + try { + Assert.assertFalse(log4jSlf4jMarker.contains(nullMarker)); + fail("Expected " + IllegalArgumentException.class.getName()); + } catch (final IllegalArgumentException e) { + // expected + } + } + + @Test + public void testContainsNullString() { + final String childMarkerName = CHILD_MAKER_NAME + "-CS"; + final String parentMakerName = PARENT_MARKER_NAME + "-CS"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMarkerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMarkerName); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); + final String nullStr = null; + Assert.assertFalse(log4jSlf4jParent.contains(nullStr)); + Assert.assertFalse(log4jSlf4jMarker.contains(nullStr)); + } + + @Test + public void testRemoveNullMarker() { + final String childMakerName = CHILD_MAKER_NAME + "-CM"; + final String parentMakerName = PARENT_MARKER_NAME + "-CM"; + final org.slf4j.Marker slf4jMarker = org.slf4j.MarkerFactory.getMarker(childMakerName); + final org.slf4j.Marker slf4jParent = org.slf4j.MarkerFactory.getMarker(parentMakerName); + slf4jMarker.add(slf4jParent); + final Marker log4jParent = MarkerManager.getMarker(parentMakerName); + final Marker log4jMarker = MarkerManager.getMarker(childMakerName); + final Log4jMarker log4jSlf4jParent = new Log4jMarker(markerFactory, log4jParent); + final Log4jMarker log4jSlf4jMarker = new Log4jMarker(markerFactory, log4jMarker); + final org.slf4j.Marker nullMarker = null; + Assert.assertFalse(log4jSlf4jParent.remove(nullMarker)); + Assert.assertFalse(log4jSlf4jMarker.remove(nullMarker)); + } + +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java new file mode 100644 index 0000000..4593919 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.slf4j; + +import static org.junit.Assert.fail; + +import org.apache.logging.log4j.LoggingException; +import org.junit.Test; +import org.slf4j.LoggerFactory; + +/** + * Tests StackOverflow when slf4j-impl and to-slf4j are both present. + */ +public class OverflowTest { + + @Test + public void log() { + try { + LoggerFactory.getLogger(OverflowTest.class); + fail("Failed to detect inclusion of log4j-to-slf4j"); + } catch (LoggingException ex) { + // Expected exception. + } catch (StackOverflowError error) { + fail("Failed to detect inclusion of log4j-to-slf4j, caught StackOverflowError"); + } + } + +} diff --git a/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java new file mode 100644 index 0000000..6b9d223 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.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 org.apache.logging.slf4j; + +import static org.apache.logging.log4j.SerializableMatchers.serializesRoundTrip; +import static org.hamcrest.MatcherAssert.assertThat; + +import java.io.Serializable; + +import org.apache.logging.log4j.junit.LoggerContextRule; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * + */ +public class SerializeTest { + + private static final String CONFIG = "log4j-test1.xml"; + + @ClassRule + public static final LoggerContextRule CTX = new LoggerContextRule(CONFIG); + + Logger logger = LoggerFactory.getLogger("LoggerTest"); + + @Test + public void testLogger() throws Exception { + assertThat((Serializable) logger, serializesRoundTrip()); + } +} diff --git a/log4j-slf4j2-impl/src/test/resources/log4j-test1.xml b/log4j-slf4j2-impl/src/test/resources/log4j-test1.xml new file mode 100644 index 0000000..2dee2c1 --- /dev/null +++ b/log4j-slf4j2-impl/src/test/resources/log4j-test1.xml @@ -0,0 +1,35 @@ + + + + target/test.log + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + diff --git a/log4j-slf4j2-impl/src/test/resources/log4j2-1482.xml b/log4j-slf4j2-impl/src/test/resources/log4j2-1482.xml new file mode 100644 index 0000000..e17953c --- /dev/null +++ b/log4j-slf4j2-impl/src/test/resources/log4j2-1482.xml @@ -0,0 +1,27 @@ + + + + target/log4j2-1482 + audit + param1,param2,param3${sys:line.separator} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/log4j-spring-boot/pom.xml b/log4j-spring-boot/pom.xml index 8979491..7083cde 100644 --- a/log4j-spring-boot/pom.xml +++ b/log4j-spring-boot/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-spring-boot jar @@ -56,14 +56,17 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.junit.jupiter junit-jupiter-api + test org.apache.logging.log4j diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystem.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystem.java deleted file mode 100644 index 5186579..0000000 --- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystem.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spring.boot; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLConnection; -import java.net.URLDecoder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Properties; -import javax.net.ssl.HttpsURLConnection; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -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.ConfigurationSource; -import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; -import org.apache.logging.log4j.core.net.UrlConnectionFactory; -import org.apache.logging.log4j.core.net.ssl.SslConfiguration; -import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory; -import org.apache.logging.log4j.core.util.AuthorizationProvider; -import org.apache.logging.log4j.core.util.FileUtils; -import org.apache.logging.log4j.internal.LogManagerStatus; -import org.apache.logging.log4j.status.StatusLogger; -import org.apache.logging.log4j.util.PropertiesUtil; -import org.apache.logging.log4j.util.Strings; -import org.springframework.boot.context.properties.bind.BindResult; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.logging.LogFile; -import org.springframework.boot.logging.LoggingInitializationContext; -import org.springframework.boot.logging.LoggingSystem; -import org.springframework.boot.logging.LoggingSystemFactory; -import org.springframework.boot.logging.log4j2.Log4J2LoggingSystem; -import org.springframework.core.annotation.Order; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ResourceUtils; - -/** - * Override Spring's implementation of the Log4j 2 Logging System to properly support Spring Cloud Config. - */ -public class Log4j2CloudConfigLoggingSystem extends Log4J2LoggingSystem { - - /** - * Property that disables the usage of this {@link LoggingSystem}. - */ - public static final String LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM = "log4j2.disableCloudConfigLoggingSystem"; - - public static final String ENVIRONMENT_KEY = "SpringEnvironment"; - private static final String HTTPS = "https"; - private static final String OVERRIDE_PARAM = "override"; - private static Logger LOGGER = StatusLogger.getLogger(); - private static final int PRECEDENCE = 0; - - public Log4j2CloudConfigLoggingSystem(ClassLoader loader) { - super(loader); - } - - /** - * Set the environment into the ExternalContext field so that it can be obtained by SpringLookup when it - * is constructed. Spring will replace the ExternalContext field with a String once initialization is - * complete. - * @param initializationContext The initialization context. - * @param configLocation The configuration location. - * @param logFile the log file. - */ - @Override - public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) { - getLoggerContext().putObjectIfAbsent(ENVIRONMENT_KEY, initializationContext.getEnvironment()); - super.initialize(initializationContext, configLocation, logFile); - } - - @Override - protected String[] getStandardConfigLocations() { - String[] locations = super.getStandardConfigLocations(); - PropertiesUtil props = new PropertiesUtil(new Properties()); - String location = props.getStringProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY); - if (location != null) { - List list = new ArrayList<>(Arrays.asList(super.getStandardConfigLocations())); - list.add(location); - locations = list.toArray(Strings.EMPTY_ARRAY); - } - return locations; - } - - @Override - protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) { - if (logFile != null) { - this.loadConfiguration(this.getBootPackagedConfigFile("log4j2-file.xml"), logFile); - } else { - this.loadConfiguration(this.getBootPackagedConfigFile("log4j2.xml"), logFile); - } - } - - private String getBootPackagedConfigFile(String fileName) { - String defaultPath = ClassUtils.getPackageName(Log4J2LoggingSystem.class); - defaultPath = defaultPath.replace('.', '/'); - defaultPath = defaultPath + "/" + fileName; - defaultPath = "classpath:" + defaultPath; - return defaultPath; - } - - /** - * This method is removed from Spring in 3.x and is scheduled to be removed in 2.8.x. It must be left in - * to support older Spring releases. - * @param location the location - * @param logFile log file configuration - */ - @Override - protected void loadConfiguration(String location, LogFile logFile) { - loadConfiguration(location, logFile, Collections.emptyList()); - } - - /** - * Added in Spring 2.6.0, the overrides parameter allows override files to be specified in the - * "logging.log4j2.config.override" property. However, spring does not support passing credentials - * when accessing the location. We do. - * @param location The location of the primary configuration. - * @param logFile log file configuration. - * @param overrides Any override files. - */ - @Override - protected void loadConfiguration(String location, LogFile logFile, List overrides) { - Assert.notNull(location, "Location must not be null"); - try { - LoggerContext ctx = getLoggerContext(); - List locations = parseConfigLocations(location); - if (overrides != null) { - locations.addAll(overrides); - } - if (locations.size() == 1) { - final URL url = ResourceUtils.getURL(location); - final ConfigurationSource source = getConfigurationSource(url); - if (source != null) { - ctx.start(ConfigurationFactory.getInstance().getConfiguration(ctx, source)); - } - } else { - final List configs = new ArrayList<>(); - boolean first = true; - for (final String sourceLocation : locations) { - final ConfigurationSource source = getConfigurationSource(ResourceUtils.getURL(sourceLocation)); - if (source != null) { - try { - final Configuration config = ConfigurationFactory.getInstance().getConfiguration(ctx, source); - if (config instanceof AbstractConfiguration) { - configs.add((AbstractConfiguration) config); - } else { - LOGGER.warn("Configuration at {} cannot be combined in a CompositeConfiguration", sourceLocation); - return; - } - } catch (Exception ex) { - if (!first) { - LOGGER.warn("Error accessing {}: {}. Ignoring override", sourceLocation, ex.getMessage()); - } else { - throw ex; - } - } - } - first = false; - } - if (configs.size() > 1) { - ctx.start(new CompositeConfiguration(configs)); - } else { - ctx.start(configs.get(0)); - } - } - } - catch (Exception ex) { - throw new IllegalStateException( - "Could not initialize Log4J2 logging from " + location, ex); - } - } - - @Override - public void cleanUp() { - getLoggerContext().removeObject(ENVIRONMENT_KEY); - super.cleanUp(); - } - - private List parseConfigLocations(String configLocations) { - final String[] uris = configLocations.split("\\?"); - final List locations = new ArrayList<>(); - locations.add(uris[0]); - if (uris.length > 1) { - try { - final URL url = new URL(configLocations); - final String[] pairs = url.getQuery().split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - try { - final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; - if (key.equalsIgnoreCase(OVERRIDE_PARAM)) { - locations.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); - } - } catch (UnsupportedEncodingException ex) { - LOGGER.warn("Bad data in configuration string: {}", pair); - } - } - return locations; - } catch (MalformedURLException ex) { - LOGGER.warn("Unable to parse configuration URL {}", configLocations); - } - } - return locations; - } - - private ConfigurationSource getConfigurationSource(URL url) throws IOException, URISyntaxException { - AuthorizationProvider provider = ConfigurationFactory.authorizationProvider(PropertiesUtil.getProperties()); - SslConfiguration sslConfiguration = url.getProtocol().equals(HTTPS) - ? SslConfigurationFactory.getSslConfiguration() : null; - URLConnection urlConnection = UrlConnectionFactory.createConnection(url, 0, sslConfiguration, - provider); - - File file = FileUtils.fileFromUri(url.toURI()); - try { - if (file != null) { - return new ConfigurationSource(urlConnection.getInputStream(), FileUtils.fileFromUri(url.toURI())); - } - return new ConfigurationSource(urlConnection.getInputStream(), url, urlConnection.getLastModified()); - } catch (FileNotFoundException ex) { - LOGGER.info("Unable to locate file {}, ignoring.", url.toString()); - return null; - } - } - - private LoggerContext getLoggerContext() { - return (LoggerContext) LogManager.getContext(false); - } - - @Order(PRECEDENCE) - public static class Factory implements LoggingSystemFactory { - - @Override - public LoggingSystem getLoggingSystem(ClassLoader classLoader) { - if (PropertiesUtil.getProperties().getBooleanProperty(LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM)) { - return null; - } - return new Log4j2CloudConfigLoggingSystem(classLoader); - } - - } - -} diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystem.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystem.java new file mode 100644 index 0000000..809c38a --- /dev/null +++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystem.java @@ -0,0 +1,267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spring.boot; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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.ConfigurationSource; +import org.apache.logging.log4j.core.config.composite.CompositeConfiguration; +import org.apache.logging.log4j.core.net.UrlConnectionFactory; +import org.apache.logging.log4j.core.net.ssl.SslConfiguration; +import org.apache.logging.log4j.core.net.ssl.SslConfigurationFactory; +import org.apache.logging.log4j.core.util.AuthorizationProvider; +import org.apache.logging.log4j.core.util.FileUtils; +import org.apache.logging.log4j.status.StatusLogger; +import org.apache.logging.log4j.util.PropertiesUtil; +import org.apache.logging.log4j.util.Strings; +import org.springframework.boot.logging.LogFile; +import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.boot.logging.LoggingSystem; +import org.springframework.boot.logging.LoggingSystemFactory; +import org.springframework.boot.logging.log4j2.Log4J2LoggingSystem; +import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ResourceUtils; + +/** + * Override Spring's implementation of the Log4j 2 Logging System to properly support Spring Cloud Config. + */ +public class Log4j2SpringBootLoggingSystem extends Log4J2LoggingSystem { + + /** + * Property that disables the usage of this {@link LoggingSystem}. + */ + public static final String LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM = "log4j2.disableCloudConfigLoggingSystem"; + + public static final String ENVIRONMENT_KEY = "SpringEnvironment"; + private static final String HTTPS = "https"; + private static final String OVERRIDE_PARAM = "override"; + private static Logger LOGGER = StatusLogger.getLogger(); + private static final int PRECEDENCE = 0; + + public Log4j2SpringBootLoggingSystem(ClassLoader loader) { + super(loader); + } + + /** + * Set the environment into the ExternalContext field so that it can be obtained by SpringLookup when it + * is constructed. Spring will replace the ExternalContext field with a String once initialization is + * complete. + * @param initializationContext The initialization context. + * @param configLocation The configuration location. + * @param logFile the log file. + */ + @Override + public void initialize(LoggingInitializationContext initializationContext, String configLocation, LogFile logFile) { + Environment environment = initializationContext.getEnvironment(); + PropertiesUtil.getProperties().addPropertySource(new SpringPropertySource(environment)); + getLoggerContext().putObjectIfAbsent(ENVIRONMENT_KEY, environment); + super.initialize(initializationContext, configLocation, logFile); + } + + @Override + protected String[] getStandardConfigLocations() { + String[] locations = super.getStandardConfigLocations(); + PropertiesUtil props = new PropertiesUtil(new Properties()); + String location = props.getStringProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY); + if (location != null) { + List list = new ArrayList<>(Arrays.asList(super.getStandardConfigLocations())); + list.add(location); + locations = list.toArray(Strings.EMPTY_ARRAY); + } + return locations; + } + + @Override + protected void loadDefaults(LoggingInitializationContext initializationContext, LogFile logFile) { + if (logFile != null) { + this.loadConfiguration(this.getBootPackagedConfigFile("log4j2-file.xml"), logFile); + } else { + this.loadConfiguration(this.getBootPackagedConfigFile("log4j2.xml"), logFile); + } + } + + private String getBootPackagedConfigFile(String fileName) { + String defaultPath = ClassUtils.getPackageName(Log4J2LoggingSystem.class); + defaultPath = defaultPath.replace('.', '/'); + defaultPath = defaultPath + "/" + fileName; + defaultPath = "classpath:" + defaultPath; + return defaultPath; + } + + /** + * This method is removed from Spring in 3.x and is scheduled to be removed in 2.8.x. It must be left in + * to support older Spring releases. + * @param location the location + * @param logFile log file configuration + */ + @Override + protected void loadConfiguration(String location, LogFile logFile) { + loadConfiguration(location, logFile, Collections.emptyList()); + } + + /** + * Added in Spring 2.6.0, the overrides parameter allows override files to be specified in the + * "logging.log4j2.config.override" property. However, spring does not support passing credentials + * when accessing the location. We do. + * @param location The location of the primary configuration. + * @param logFile log file configuration. + * @param overrides Any override files. + */ + @Override + protected void loadConfiguration(String location, LogFile logFile, List overrides) { + Assert.notNull(location, "Location must not be null"); + try { + LoggerContext ctx = getLoggerContext(); + List locations = parseConfigLocations(location); + if (overrides != null) { + locations.addAll(overrides); + } + if (locations.size() == 1) { + final URL url = ResourceUtils.getURL(location); + final ConfigurationSource source = getConfigurationSource(url); + if (source != null) { + ctx.start(ConfigurationFactory.getInstance().getConfiguration(ctx, source)); + } + } else { + final List configs = new ArrayList<>(); + boolean first = true; + for (final String sourceLocation : locations) { + final ConfigurationSource source = getConfigurationSource(ResourceUtils.getURL(sourceLocation)); + if (source != null) { + try { + final Configuration config = ConfigurationFactory.getInstance().getConfiguration(ctx, source); + if (config instanceof AbstractConfiguration) { + configs.add((AbstractConfiguration) config); + } else { + LOGGER.warn("Configuration at {} cannot be combined in a CompositeConfiguration", sourceLocation); + return; + } + } catch (Exception ex) { + if (!first) { + LOGGER.warn("Error accessing {}: {}. Ignoring override", sourceLocation, ex.getMessage()); + } else { + throw ex; + } + } + } + first = false; + } + if (configs.size() > 1) { + ctx.start(new CompositeConfiguration(configs)); + } else { + ctx.start(configs.get(0)); + } + } + } + catch (Exception ex) { + throw new IllegalStateException( + "Could not initialize Log4J2 logging from " + location, ex); + } + } + + @Override + public void cleanUp() { + getLoggerContext().removeObject(ENVIRONMENT_KEY); + super.cleanUp(); + } + + private List parseConfigLocations(String configLocations) { + final String[] uris = configLocations.split("\\?"); + final List locations = new ArrayList<>(); + locations.add(uris[0]); + if (uris.length > 1) { + try { + final URL url = new URL(configLocations); + final String[] pairs = url.getQuery().split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + try { + final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair; + if (key.equalsIgnoreCase(OVERRIDE_PARAM)) { + locations.add(URLDecoder.decode(pair.substring(idx + 1), "UTF-8")); + } + } catch (UnsupportedEncodingException ex) { + LOGGER.warn("Bad data in configuration string: {}", pair); + } + } + return locations; + } catch (MalformedURLException ex) { + LOGGER.warn("Unable to parse configuration URL {}", configLocations); + } + } + return locations; + } + + private ConfigurationSource getConfigurationSource(URL url) throws IOException, URISyntaxException { + AuthorizationProvider provider = ConfigurationFactory.authorizationProvider(PropertiesUtil.getProperties()); + SslConfiguration sslConfiguration = url.getProtocol().equals(HTTPS) + ? SslConfigurationFactory.getSslConfiguration() : null; + URLConnection urlConnection = UrlConnectionFactory.createConnection(url, 0, sslConfiguration, + provider); + + File file = FileUtils.fileFromUri(url.toURI()); + try { + if (file != null) { + return new ConfigurationSource(urlConnection.getInputStream(), FileUtils.fileFromUri(url.toURI())); + } + return new ConfigurationSource(urlConnection.getInputStream(), url, urlConnection.getLastModified()); + } catch (FileNotFoundException ex) { + LOGGER.info("Unable to locate file {}, ignoring.", url.toString()); + return null; + } + } + + private LoggerContext getLoggerContext() { + return (LoggerContext) LogManager.getContext(false); + } + + @Order(PRECEDENCE) + public static class Factory implements LoggingSystemFactory { + + @Override + public LoggingSystem getLoggingSystem(ClassLoader classLoader) { + if (PropertiesUtil.getProperties().getBooleanProperty(LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM)) { + return null; + } + return new Log4j2SpringBootLoggingSystem(classLoader); + } + + } + +} diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringEnvironmentHolder.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringEnvironmentHolder.java deleted file mode 100644 index 5c4403d..0000000 --- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringEnvironmentHolder.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spring.boot; - -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.internal.LogManagerStatus; -import org.springframework.core.env.Environment; - -/** - * Provides access to the Spring Environment. - */ -public class SpringEnvironmentHolder { - - private volatile Environment environment; - private Lock lock = new ReentrantLock(); - - - protected Environment getEnvironment() { - if (environment == null && LogManagerStatus.isInitialized() && LogManager.getFactory() != null && - LogManager.getFactory().hasContext(SpringEnvironmentHolder.class.getName(), null, false)) { - lock.lock(); - try { - if (environment == null) { - Object obj = LogManager.getContext(false).getObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY); - environment = obj instanceof Environment ? (Environment) obj : null; - } - } finally { - lock.unlock(); - } - } - return environment; - } -} diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java index 401cbde..3d38210 100644 --- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java +++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringLookup.java @@ -21,6 +21,8 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.LogEvent; +import org.apache.logging.log4j.core.LoggerContext; +import org.apache.logging.log4j.core.config.LoggerContextAware; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.lookup.StrLookup; import org.apache.logging.log4j.core.util.Integers; @@ -31,7 +33,7 @@ * Lookup for Spring properties. */ @Plugin(name = "spring", category = StrLookup.CATEGORY) -public class SpringLookup extends SpringEnvironmentHolder implements StrLookup { +public class SpringLookup implements LoggerContextAware, StrLookup { private static final Logger LOGGER = StatusLogger.getLogger(); private static final String ACTIVE = "profiles.active"; @@ -40,30 +42,27 @@ private static final Pattern ACTIVE_PATTERN = Pattern.compile(ACTIVE + PATTERN); private static final Pattern DEFAULT_PATTERN = Pattern.compile(DEFAULT + PATTERN); - public SpringLookup() { - getEnvironment(); - } + private volatile Environment environment; @Override public String lookup(String key) { - Environment env = getEnvironment(); - if (env != null) { + if (environment != null) { String lowerKey = key.toLowerCase(); if (lowerKey.startsWith(ACTIVE)) { - switch (env.getActiveProfiles().length) { + switch (environment.getActiveProfiles().length) { case 0: { return null; } case 1: { - return env.getActiveProfiles()[0]; + return environment.getActiveProfiles()[0]; } default: { Matcher matcher = ACTIVE_PATTERN.matcher(key); if (matcher.matches()) { try { int index = Integers.parseInt(matcher.group(1)); - if (index < env.getActiveProfiles().length) { - return env.getActiveProfiles()[index]; + if (index < environment.getActiveProfiles().length) { + return environment.getActiveProfiles()[index]; } LOGGER.warn("Index out of bounds for Spring active profiles: {}", index); return null; @@ -73,24 +72,24 @@ } } - return String.join(",", env.getActiveProfiles()); + return String.join(",", environment.getActiveProfiles()); } } } else if (lowerKey.startsWith(DEFAULT)) { - switch (env.getDefaultProfiles().length) { + switch (environment.getDefaultProfiles().length) { case 0: { return null; } case 1: { - return env.getDefaultProfiles()[0]; + return environment.getDefaultProfiles()[0]; } default: { Matcher matcher = DEFAULT_PATTERN.matcher(key); if (matcher.matches()) { try { int index = Integer.parseInt(matcher.group(1)); - if (index < env.getDefaultProfiles().length) { - return env.getDefaultProfiles()[index]; + if (index < environment.getDefaultProfiles().length) { + return environment.getDefaultProfiles()[index]; } LOGGER.warn("Index out of bounds for Spring default profiles: {}", index); return null; @@ -100,12 +99,12 @@ } } - return String.join(",", env.getDefaultProfiles()); + return String.join(",", environment.getDefaultProfiles()); } } } - return env.getProperty(key); + return environment.getProperty(key); } return null; @@ -115,4 +114,13 @@ public String lookup(LogEvent event, String key) { return lookup((key)); } + + @Override + public void setLoggerContext(final LoggerContext loggerContext) { + if (loggerContext != null) { + environment = (Environment) loggerContext.getObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY); + } else { + LOGGER.warn("Attempt to set LoggerContext reference to null in SpringLookup"); + } + } } diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java index 6c6cdc6..29600bd 100644 --- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java +++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringProfileArbiter.java @@ -16,6 +16,8 @@ */ package org.apache.logging.log4j.spring.boot; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.Node; import org.apache.logging.log4j.core.config.arbiters.Arbiter; @@ -23,6 +25,8 @@ import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; +import org.apache.logging.log4j.core.config.plugins.PluginLoggerContext; +import org.apache.logging.log4j.status.StatusLogger; import org.springframework.core.env.Environment; import org.springframework.core.env.Profiles; import org.springframework.util.StringUtils; @@ -32,18 +36,18 @@ */ @Plugin(name = "SpringProfile", category = Node.CATEGORY, elementType = Arbiter.ELEMENT_TYPE, deferChildren = true, printObject = true) -public class SpringProfileArbiter extends SpringEnvironmentHolder implements Arbiter { +public class SpringProfileArbiter implements Arbiter { private final String[] profileNames; + private final Environment environment; - private SpringProfileArbiter(final String[] profiles) { + private SpringProfileArbiter(final String[] profiles, Environment environment) { this.profileNames = profiles; - + this.environment = environment; } @Override public boolean isCondition() { - Environment environment = getEnvironment(); if (environment == null) { return false; } @@ -61,13 +65,17 @@ public static class Builder implements org.apache.logging.log4j.core.util.Builder { + private final Logger LOGGER = StatusLogger.getLogger(); public static final String ATTR_NAME = "name"; @PluginBuilderAttribute(ATTR_NAME) private String name; @PluginConfiguration - private Configuration configuration;; + private Configuration configuration; + + @PluginLoggerContext + private LoggerContext loggerContext; /** * Sets the Profile Name or Names. @@ -84,6 +92,11 @@ return asBuilder(); } + public Builder setLoggerContext(final LoggerContext loggerContext) { + this.loggerContext = loggerContext; + return asBuilder(); + } + private SpringProfileArbiter.Builder asBuilder() { return this; } @@ -91,7 +104,19 @@ public SpringProfileArbiter build() { String[] profileNames = StringUtils.trimArrayElements( StringUtils.commaDelimitedListToStringArray(configuration.getStrSubstitutor().replace(name))); - return new SpringProfileArbiter(profileNames); + Environment environment = null; + if (loggerContext != null) { + environment = (Environment) loggerContext.getObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY); + if (environment == null) { + LOGGER.warn("Cannot create Arbiter, no Spring Environment provided"); + return null; + } + + return new SpringProfileArbiter(profileNames, environment); + } else { + LOGGER.warn("Cannot create Arbiter, LoggerContext is not available"); + } + return null; } } } diff --git a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringPropertySource.java b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringPropertySource.java index 5749bb7..a10b397 100644 --- a/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringPropertySource.java +++ b/log4j-spring-boot/src/main/java/org/apache/logging/log4j/spring/boot/SpringPropertySource.java @@ -22,9 +22,15 @@ /** * Returns properties from Spring. */ -public class SpringPropertySource extends SpringEnvironmentHolder implements PropertySource { +public class SpringPropertySource implements PropertySource { private static final int DEFAULT_PRIORITY = -100; + + private final Environment environment; + + public SpringPropertySource(Environment environment) { + this.environment = environment; + } /** * System properties take precendence followed by properties in Log4j properties files. Spring properties @@ -38,7 +44,6 @@ @Override public String getProperty(String key) { - Environment environment = getEnvironment(); if (environment != null) { return environment.getProperty(key); } @@ -47,7 +52,6 @@ @Override public boolean containsProperty(String key) { - Environment environment = getEnvironment(); if (environment != null) { return environment.containsProperty(key); } diff --git a/log4j-spring-boot/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource b/log4j-spring-boot/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource deleted file mode 100644 index 8903871..0000000 --- a/log4j-spring-boot/src/main/resources/META-INF/services/org.apache.logging.log4j.util.PropertySource +++ /dev/null @@ -1,15 +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. -org.apache.logging.log4j.spring.boot.SpringPropertySource diff --git a/log4j-spring-boot/src/main/resources/META-INF/spring.factories b/log4j-spring-boot/src/main/resources/META-INF/spring.factories index 4ee1569..c9f5557 100644 --- a/log4j-spring-boot/src/main/resources/META-INF/spring.factories +++ b/log4j-spring-boot/src/main/resources/META-INF/spring.factories @@ -14,4 +14,4 @@ # See the license for the specific language governing permissions and # limitations under the license. # -org.springframework.boot.logging.LoggingSystemFactory=org.apache.logging.log4j.spring.boot.Log4j2CloudConfigLoggingSystem.Factory \ No newline at end of file +org.springframework.boot.logging.LoggingSystemFactory=org.apache.logging.log4j.spring.boot.Log4j2SpringBootLoggingSystem.Factory \ No newline at end of file diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystemTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystemTest.java deleted file mode 100644 index 3731f23..0000000 --- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2CloudConfigLoggingSystemTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache license, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the license for the specific language governing permissions and - * limitations under the license. - */ -package org.apache.logging.log4j.spring.boot; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.core.config.ConfigurationFactory; -import org.apache.logging.log4j.spi.LoggerContext; -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.SetSystemProperty; -import org.springframework.boot.logging.LoggingSystem; -import org.springframework.boot.logging.log4j2.Log4J2LoggingSystem; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.util.Arrays; -import java.util.List; - -public class Log4j2CloudConfigLoggingSystemTest { - - @Test - public void getStandardConfigLocations() { - String customLog4j2Location = "classpath:my_custom_log4j2.properties"; - LoggerContext lc = LogManager.getContext(); // Initialize LogManager to here to prevent a failure trying to - // initialize it from StatusLogger. - System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, customLog4j2Location); - Log4j2CloudConfigLoggingSystem cloudLoggingSystem = new Log4j2CloudConfigLoggingSystem( - this.getClass().getClassLoader()); - List standardConfigLocations = Arrays.asList(cloudLoggingSystem.getStandardConfigLocations()); - assertTrue(standardConfigLocations.contains(customLog4j2Location)); - - } - - @Test - @SetSystemProperty(key = Log4j2CloudConfigLoggingSystem.LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM, value = "true") - public void testUseLog4j2LoggingSystem() { - LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader()); - assertTrue(loggingSystem.getClass().equals(Log4J2LoggingSystem.class)); - } - - @Test - public void testLoggingSystemEnabled() { - LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader()); - assertTrue(loggingSystem.getClass().equals(Log4j2CloudConfigLoggingSystem.class)); - } -} diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java index fd36fc2..a6afb2f 100644 --- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java +++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootInitTest.java @@ -54,7 +54,9 @@ @Override public void run(ApplicationArguments args) throws Exception { + LoggerContext context = (LoggerContext) LogManager.getContext(false); SpringLookup lookup = new SpringLookup(); + lookup.setLoggerContext(context); LOGGER.info("Started: {}", lookup.lookup("spring.application.name")); } } diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystemTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystemTest.java new file mode 100644 index 0000000..83ca4b3 --- /dev/null +++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/Log4j2SpringBootLoggingSystemTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.spring.boot; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.core.config.ConfigurationFactory; +import org.apache.logging.log4j.spi.LoggerContext; +import org.junit.jupiter.api.Test; +import org.junitpioneer.jupiter.SetSystemProperty; +import org.springframework.boot.logging.LoggingSystem; +import org.springframework.boot.logging.log4j2.Log4J2LoggingSystem; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; + +public class Log4j2SpringBootLoggingSystemTest { + + @Test + public void getStandardConfigLocations() { + String customLog4j2Location = "classpath:my_custom_log4j2.properties"; + LoggerContext lc = LogManager.getContext(); // Initialize LogManager to here to prevent a failure trying to + // initialize it from StatusLogger. + System.setProperty(ConfigurationFactory.CONFIGURATION_FILE_PROPERTY, customLog4j2Location); + Log4j2SpringBootLoggingSystem cloudLoggingSystem = new Log4j2SpringBootLoggingSystem( + this.getClass().getClassLoader()); + List standardConfigLocations = Arrays.asList(cloudLoggingSystem.getStandardConfigLocations()); + assertTrue(standardConfigLocations.contains(customLog4j2Location)); + + } + + @Test + @SetSystemProperty(key = Log4j2SpringBootLoggingSystem.LOG4J2_DISABLE_CLOUD_CONFIG_LOGGING_SYSTEM, value = "true") + public void testUseLog4j2LoggingSystem() { + LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader()); + assertTrue(loggingSystem.getClass().equals(Log4J2LoggingSystem.class)); + } + + @Test + public void testLoggingSystemEnabled() { + LoggingSystem loggingSystem = LoggingSystem.get(getClass().getClassLoader()); + assertTrue(loggingSystem.getClass().equals(Log4j2SpringBootLoggingSystem.class)); + } +} diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java index eb264f8..b0abe5c 100644 --- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java +++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringLookupTest.java @@ -39,8 +39,9 @@ env.setDefaultProfiles("one", "two"); env.setProperty("app.property", "test"); LoggerContext context = (LoggerContext) LogManager.getContext(false); - context.putObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY, env); + context.putObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY, env); SpringLookup lookup = new SpringLookup(); + lookup.setLoggerContext(context); String result = lookup.lookup("profiles.active"); assertNotNull("No active profiles", result); assertEquals("Incorrect active profile", "test", result); @@ -66,9 +67,10 @@ env.setActiveProfiles("test"); env.setProperty("app.property", "test"); LoggerContext context = (LoggerContext) LogManager.getContext(false); - context.putObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY, env); - - StrLookup lookup = new Interpolator(); + context.putObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY, env); + Interpolator lookup = new Interpolator(); + lookup.setConfiguration(context.getConfiguration()); + lookup.setLoggerContext(context); String result = lookup.lookup("spring:profiles.active"); assertNotNull("No active profiles", result); assertEquals("Incorrect active profile", "test", result); diff --git a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java index 3182ae3..c35b08b 100644 --- a/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java +++ b/log4j-spring-boot/src/test/java/org/apache/logging/log4j/spring/boot/SpringProfileTest.java @@ -43,7 +43,7 @@ public static void before() { loggerContext = (LoggerContext) LogManager.getContext(false); env = new MockEnvironment(); - loggerContext.putObject(Log4j2CloudConfigLoggingSystem.ENVIRONMENT_KEY, env); + loggerContext.putObject(Log4j2SpringBootLoggingSystem.ENVIRONMENT_KEY, env); } diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml index 419a3ab..64d128f 100644 --- a/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-client/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j-spring-cloud-config - 2.18.0 + 2.19.0 log4j-spring-cloud-config-client jar @@ -36,10 +36,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.apache.logging.log4j diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml index 8c031e8..169d7b6 100644 --- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-application/pom.xml @@ -21,7 +21,7 @@ org.apache.logging.log4j.samples log4j-spring-cloud-config-samples - 2.18.0 + 2.19.0 sample-app diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml index 74811d5..f259c1f 100644 --- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/log4j-spring-cloud-config-sample-server/pom.xml @@ -21,7 +21,7 @@ org.apache.logging.log4j.samples log4j-spring-cloud-config-sample-server jar - 2.18.0 + 2.19.0 Apache Log4j Sample Configuration Service Sample Cloud Config Server @@ -305,6 +305,6 @@ - log4j-2.18.0-rc1 + log4j-2.19.0-rc2 diff --git a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/pom.xml b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/pom.xml index 7cd80ca..0d99f0c 100644 --- a/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/pom.xml +++ b/log4j-spring-cloud-config/log4j-spring-cloud-config-samples/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j-spring-cloud-config - 2.18.0 + 2.19.0 org.apache.logging.log4j.samples log4j-spring-cloud-config-samples diff --git a/log4j-spring-cloud-config/pom.xml b/log4j-spring-cloud-config/pom.xml index 249219c..ed431a5 100644 --- a/log4j-spring-cloud-config/pom.xml +++ b/log4j-spring-cloud-config/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-spring-cloud-config pom diff --git a/log4j-taglib/pom.xml b/log4j-taglib/pom.xml index b83501f..c0f9b0e 100644 --- a/log4j-taglib/pom.xml +++ b/log4j-taglib/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-taglib jar @@ -67,10 +67,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.springframework diff --git a/log4j-to-jul/pom.xml b/log4j-to-jul/pom.xml index edc4609..0cf1ba7 100644 --- a/log4j-to-jul/pom.xml +++ b/log4j-to-jul/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-to-jul jar @@ -41,6 +41,10 @@ log4j-api + org.osgi + org.osgi.core + + com.google.guava guava-testlib test @@ -48,10 +52,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.assertj @@ -81,13 +87,8 @@ maven-bundle-plugin + org.apache.logging.log4j.tojul.Activator org.apache.logging.log4j.tojul - - osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)" - - - osgi.serviceloader;osgi.serviceloader=org.apache.logging.log4j.spi.Provider - diff --git a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/Activator.java b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/Activator.java new file mode 100644 index 0000000..47cc1f3 --- /dev/null +++ b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/Activator.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.tojul; + +import org.apache.logging.log4j.util.ProviderActivator; + +public class Activator extends ProviderActivator { + + public Activator() { + super(new JULProvider()); + } +} diff --git a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java index 5f27ae3..1a67ea3 100644 --- a/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java +++ b/log4j-to-jul/src/main/java/org/apache/logging/log4j/tojul/JULLogger.java @@ -153,7 +153,8 @@ return current.getLevel(); } // This is a safety fallback that is typically never reached, because usually the root Logger.getLogger("") has a Level. - return Logger.getGlobal().getLevel(); + // Since JDK 8 the LogManager$RootLogger does not have a default level, just a default effective level of INFO. + return java.util.logging.Level.INFO; } private boolean isEnabledFor(final Level level, final Marker marker) { diff --git a/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java new file mode 100644 index 0000000..1bd2742 --- /dev/null +++ b/log4j-to-jul/src/test/java/org/apache/logging/log4j/tojul/JULLoggerTest.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache license, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the license for the specific language governing permissions and + * limitations under the license. + */ +package org.apache.logging.log4j.tojul; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.apache.logging.log4j.Level; +import org.junit.jupiter.api.Test; + +public class JULLoggerTest { + + @Test + public void testNotNullEffectiveLevel() { + // Emulates the root logger found in Tomcat, with a null level + // See: https://bz.apache.org/bugzilla/show_bug.cgi?id=66184 + final java.util.logging.Logger julLogger = new java.util.logging.Logger("", null) { + }; + final JULLogger logger = new JULLogger("", julLogger); + assertEquals(Level.INFO, logger.getLevel()); + } +} diff --git a/log4j-to-slf4j/pom.xml b/log4j-to-slf4j/pom.xml index aadf4d1..c376e1a 100644 --- a/log4j-to-slf4j/pom.xml +++ b/log4j-to-slf4j/pom.xml @@ -20,7 +20,7 @@ org.apache.logging.log4j log4j - 2.18.0 + 2.19.0 log4j-to-slf4j jar @@ -43,6 +43,10 @@ log4j-api + org.osgi + org.osgi.core + + ch.qos.logback logback-core test @@ -61,10 +65,12 @@ org.junit.vintage junit-vintage-engine + test org.junit.jupiter junit-jupiter-engine + test org.hamcrest @@ -94,13 +100,8 @@ maven-bundle-plugin + org.apache.logging.slf4j.Activator org.apache.logging.slf4j - - osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)" - - - osgi.serviceloader;osgi.serviceloader=org.apache.logging.log4j.spi.Provider - diff --git a/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java new file mode 100644 index 0000000..fc997e5 --- /dev/null +++ b/log4j-to-slf4j/src/main/java/org/apache/logging/slf4j/Activator.java @@ -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. + */ + +package org.apache.logging.slf4j; + +import org.apache.logging.log4j.util.ProviderActivator; + +public class Activator extends ProviderActivator { + + public Activator() { + super(new SLF4JProvider()); + } + +} diff --git a/log4j-web/pom.xml b/log4j-web/pom.xml index 63c3889..5ac776a 100644 --- a/log4j-web/pom.xml +++ b/log4j-web/pom.xml @@ -20,7 +20,7 @@ log4j org.apache.logging.log4j - 2.18.0 + 2.19.0 4.0.0 @@ -59,17 +59,20 @@ test - org.hamcrest - hamcrest + org.hamcrest + hamcrest + test org.junit.jupiter junit-jupiter-engine + test org.mockito mockito-junit-jupiter - + test + org.springframework spring-test diff --git a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java index af756e3..5a2ff5f 100644 --- a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java +++ b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletContextListener.java @@ -35,13 +35,13 @@ */ public class Log4jServletContextListener implements ServletContextListener { - private static final int DEFAULT_STOP_TIMEOUT = 30; + private static final int DEFAULT_STOP_TIMEOUT = 30; private static final TimeUnit DEFAULT_STOP_TIMEOUT_TIMEUNIT = TimeUnit.SECONDS; - private static final String KEY_STOP_TIMEOUT = "log4j.stop.timeout"; - private static final String KEY_STOP_TIMEOUT_TIMEUNIT = "log4j.stop.timeout.timeunit"; + private static final String KEY_STOP_TIMEOUT = "log4j.stop.timeout"; + private static final String KEY_STOP_TIMEOUT_TIMEUNIT = "log4j.stop.timeout.timeunit"; - private static final Logger LOGGER = StatusLogger.getLogger(); + private static final Logger LOGGER = StatusLogger.getLogger(); private ServletContext servletContext; private Log4jWebLifeCycle initializer; @@ -53,10 +53,10 @@ if ("true".equalsIgnoreCase(servletContext.getInitParameter( Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED))) { - throw new IllegalStateException("Do not use " + getClass().getSimpleName() + " when " - + Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED + " is true. Please use " - + Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + " instead of " - + getClass().getSimpleName() + "."); + throw new IllegalStateException("Do not use " + getClass().getSimpleName() + " when " + + Log4jWebSupport.IS_LOG4J_AUTO_SHUTDOWN_DISABLED + " is true. Please use " + + Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + " instead of " + + getClass().getSimpleName() + "."); } this.initializer = WebLoggerContextUtils.getWebLifeCycle(this.servletContext); @@ -69,25 +69,25 @@ } @Override - public void contextDestroyed(final ServletContextEvent event) { - if (this.servletContext == null || this.initializer == null) { - LOGGER.warn("Context destroyed before it was initialized."); - return; - } - LOGGER.debug("Log4jServletContextListener ensuring that Log4j shuts down properly."); + public void contextDestroyed(final ServletContextEvent event) { + if (this.servletContext == null || this.initializer == null) { + LOGGER.warn("Context destroyed before it was initialized."); + return; + } + LOGGER.debug("Log4jServletContextListener ensuring that Log4j shuts down properly."); - this.initializer.clearLoggerContext(); // the application is finished - // shutting down now - if (initializer instanceof LifeCycle2) { - final String stopTimeoutStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT); - final long stopTimeout = Strings.isEmpty(stopTimeoutStr) ? DEFAULT_STOP_TIMEOUT - : Long.parseLong(stopTimeoutStr); - final String timeoutTimeUnitStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT_TIMEUNIT); - final TimeUnit timeoutTimeUnit = Strings.isEmpty(timeoutTimeUnitStr) ? DEFAULT_STOP_TIMEOUT_TIMEUNIT - : TimeUnit.valueOf(timeoutTimeUnitStr.toUpperCase(Locale.ROOT)); - ((LifeCycle2) this.initializer).stop(stopTimeout, timeoutTimeUnit); - } else { - this.initializer.stop(); - } - } + this.initializer.clearLoggerContext(); // the application is finished + // shutting down now + if (initializer instanceof LifeCycle2) { + final String stopTimeoutStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT); + final long stopTimeout = Strings.isEmpty(stopTimeoutStr) ? DEFAULT_STOP_TIMEOUT + : Long.parseLong(stopTimeoutStr); + final String timeoutTimeUnitStr = servletContext.getInitParameter(KEY_STOP_TIMEOUT_TIMEUNIT); + final TimeUnit timeoutTimeUnit = Strings.isEmpty(timeoutTimeUnitStr) ? DEFAULT_STOP_TIMEOUT_TIMEUNIT + : TimeUnit.valueOf(timeoutTimeUnitStr.toUpperCase(Locale.ROOT)); + ((LifeCycle2) this.initializer).stop(stopTimeout, timeoutTimeUnit); + } else { + this.initializer.stop(); + } + } } diff --git a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java index e95f334..6b1d1e0 100644 --- a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java +++ b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jServletFilter.java @@ -71,6 +71,8 @@ chain.doFilter(request, response); } finally { this.initializer.clearLoggerContext(); + // Execute once per thread + request.removeAttribute(ALREADY_FILTERED_ATTRIBUTE); } } } diff --git a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java index 9bbe9c1..af036f3 100644 --- a/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java +++ b/log4j-web/src/main/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListener.java @@ -44,12 +44,12 @@ @Override public void contextInitialized(final ServletContextEvent event) { LOGGER.debug(Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + - " ensuring that Log4j started up properly."); + " ensuring that Log4j started up properly."); servletContext = event.getServletContext(); if (null == servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) { - throw new IllegalStateException( - "Context did not contain required Log4jWebLifeCycle in the " - + Log4jWebSupport.SUPPORT_ATTRIBUTE + " attribute."); + throw new IllegalStateException( + "Context did not contain required Log4jWebLifeCycle in the " + + Log4jWebSupport.SUPPORT_ATTRIBUTE + " attribute."); } this.initializer = WebLoggerContextUtils.getWebLifeCycle(servletContext); } @@ -61,7 +61,7 @@ return; } LOGGER.debug(Log4jShutdownOnContextDestroyedListener.class.getSimpleName() + - " ensuring that Log4j shuts down properly."); + " ensuring that Log4j shuts down properly."); this.initializer.clearLoggerContext(); // the application is finished // shutting down now diff --git a/log4j-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java b/log4j-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java index c9a70f6..fff91dc 100644 --- a/log4j-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java +++ b/log4j-web/src/main/java/org/apache/logging/log4j/web/appender/ServletAppender.java @@ -38,30 +38,30 @@ @Plugin(name = "Servlet", category = "Core", elementType = "appender", printObject = true) public class ServletAppender extends AbstractAppender { - public static class Builder> extends AbstractAppender.Builder - implements org.apache.logging.log4j.core.util.Builder { + public static class Builder> extends AbstractAppender.Builder + implements org.apache.logging.log4j.core.util.Builder { @PluginBuilderAttribute private boolean logThrowables; - @Override - public ServletAppender build() { - final String name = getName(); - if (name == null) { - LOGGER.error("No name provided for ServletAppender"); - } - final ServletContext servletContext = WebLoggerContextUtils.getServletContext(); - if (servletContext == null) { - LOGGER.error("No servlet context is available"); - return null; - } - final Layout layout = getOrCreateLayout(); - if (!(layout instanceof StringLayout)) { - LOGGER.error("Layout must be a StringLayout to log to ServletContext"); - return null; - } - return new ServletAppender(name, layout, getFilter(), servletContext, isIgnoreExceptions(), logThrowables); - } + @Override + public ServletAppender build() { + final String name = getName(); + if (name == null) { + LOGGER.error("No name provided for ServletAppender"); + } + final ServletContext servletContext = WebLoggerContextUtils.getServletContext(); + if (servletContext == null) { + LOGGER.error("No servlet context is available"); + return null; + } + final Layout layout = getOrCreateLayout(); + if (!(layout instanceof StringLayout)) { + LOGGER.error("Layout must be a StringLayout to log to ServletContext"); + return null; + } + return new ServletAppender(name, layout, getFilter(), servletContext, isIgnoreExceptions(), logThrowables); + } /** * Logs with {@link ServletContext#log(String, Throwable)} if true and with {@link ServletContext#log(String)} if false. @@ -79,7 +79,7 @@ this.logThrowables = logThrowables; } - } + } @PluginBuilderFactory public static > B newBuilder() { @@ -120,9 +120,9 @@ public static ServletAppender createAppender(final Layout layout, final Filter filter, final String name, final boolean ignoreExceptions) { // @formatter:off - return newBuilder().setFilter(filter).setIgnoreExceptions(ignoreExceptions).setLayout(layout).setName(name) - .build(); - // @formatter:on + return newBuilder().setFilter(filter).setIgnoreExceptions(ignoreExceptions).setLayout(layout).setName(name) + .build(); + // @formatter:on } } diff --git a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java index 35d662d..afc22ca 100644 --- a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java +++ b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletContextListenerTest.java @@ -34,12 +34,12 @@ @ExtendWith(MockitoExtension.class) public class Log4jServletContextListenerTest { - /* event and servletContext are marked lenient because they aren't used in the - * testDestroyWithNoInit but are only accessed during initialization - */ - @Mock(lenient = true) - private ServletContextEvent event; - @Mock(lenient = true) + /* event and servletContext are marked lenient because they aren't used in the + * testDestroyWithNoInit but are only accessed during initialization + */ + @Mock(lenient = true) + private ServletContextEvent event; + @Mock(lenient = true) private ServletContext servletContext; @Mock private Log4jWebLifeCycle initializer; diff --git a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java index 479ac26..6383f01 100644 --- a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java +++ b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jServletFilterTest.java @@ -72,9 +72,9 @@ @Test public void testDestroy() { - assertThrows(IllegalStateException.class, () -> { - this.filter.destroy(); - }); + assertThrows(IllegalStateException.class, () -> { + this.filter.destroy(); + }); } @Test @@ -93,6 +93,7 @@ then(chain).should().doFilter(same(request), same(response)); then(chain).shouldHaveNoMoreInteractions(); then(initializer).should().clearLoggerContext(); + then(request).should().removeAttribute(Log4jServletFilter.ALREADY_FILTERED_ATTRIBUTE); } @Test diff --git a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java index 5577381..5ea7a59 100644 --- a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java +++ b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jShutdownOnContextDestroyedListenerTest.java @@ -43,15 +43,15 @@ public void setUp(boolean mockInitializer) { this.listener = new Log4jShutdownOnContextDestroyedListener(); given(event.getServletContext()).willReturn(servletContext); - if (mockInitializer) { - given(servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) - .willReturn(initializer); + if (mockInitializer) { + given(servletContext.getAttribute(Log4jWebSupport.SUPPORT_ATTRIBUTE)) + .willReturn(initializer); } } - + @Test public void testInitAndDestroy() throws Exception { - setUp(true); + setUp(true); this.listener.contextInitialized(this.event); then(initializer).should(never()).start(); @@ -65,7 +65,7 @@ @Test public void testDestroy() throws Exception { - setUp(true); + setUp(true); this.listener.contextDestroyed(this.event); then(initializer).should(never()).clearLoggerContext(); @@ -74,10 +74,10 @@ @Test public void whenNoInitializerInContextTheContextInitializedShouldThrowAnException() { - setUp(false); - - assertThrows(IllegalStateException.class, () -> { - this.listener.contextInitialized(this.event); - }); + setUp(false); + + assertThrows(IllegalStateException.class, () -> { + this.listener.contextInitialized(this.event); + }); } } diff --git a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java index 40e10df..2a67aa6 100644 --- a/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java +++ b/log4j-web/src/test/java/org/apache/logging/log4j/web/Log4jWebInitializerImplTest.java @@ -45,11 +45,11 @@ @ExtendWith(MockitoExtension.class) public class Log4jWebInitializerImplTest { - /* Marking servletContext lenient because otherwise testCompositeLocationParameterWithEmptyUriListSetsDefaultConfiguration fails - * when null is passed in as the initial param because Mockito deciced null isn't a String rather than the absence of a string. - */ - @Mock(lenient = true) - private ServletContext servletContext; + /* Marking servletContext lenient because otherwise testCompositeLocationParameterWithEmptyUriListSetsDefaultConfiguration fails + * when null is passed in as the initial param because Mockito deciced null isn't a String rather than the absence of a string. + */ + @Mock(lenient = true) + private ServletContext servletContext; @Captor private ArgumentCaptor initializerCaptor; @Captor @@ -73,9 +73,9 @@ @Test public void testDeinitializeBeforeInitialize() { - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.stop(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.stop(); + }); } @Test @@ -214,9 +214,9 @@ then(servletContext).should().removeAttribute(eq(Log4jWebSupport.CONTEXT_ATTRIBUTE)); - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.start(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.start(); + }); } @Test @@ -248,9 +248,9 @@ given(servletContext.getResourcePaths("/WEB-INF/")).willReturn(null); assertNull(ContextAnchor.THREAD_CONTEXT.get(), "The context should be null."); - assertThrows(IllegalStateException.class, () -> { - this.initializerImpl.start(); - }); + assertThrows(IllegalStateException.class, () -> { + this.initializerImpl.start(); + }); } @Test diff --git a/pom.xml b/pom.xml index 41b996d..9543788 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ log4j pom Apache Log4j 2 - 2.18.0 + 2.19.0 org.apache.logging logging-parent @@ -160,7 +160,7 @@ Piotr P. Karwasz pkarwasz@apache.org - Committer + PMC Member Europe/Warsaw @@ -224,12 +224,12 @@ scm:git:https://gitbox.apache.org/repos/asf/logging-log4j2.git scm:git:https://gitbox.apache.org/repos/asf/logging-log4j2.git https://gitbox.apache.org/repos/asf?p=logging-log4j2.git - log4j-2.18.0-rc1 + log4j-2.19.0-rc2 ${basedir} - 2.18.0 + 2.19.0 2.12.4 2.3.2 Ralph Goers @@ -239,18 +239,18 @@ 1.7.36 1.2.11 1.9.13 - 2.13.3 + 2.13.4 2.6.7 5.3.20 5.12.2 - 1.10.0 + 1.10.1 3.4.4 1.2.15 - 7.17.4 + 7.17.6 3.12.11 4.5.0 - 6.2.8 + 6.3.1 3.0.10 3.10.1 3.16.0 @@ -296,12 +296,13 @@ 5.17.1 3.3.0 4.13.2 - 5.8.2 + 5.9.0 1.6.2 4.4.0 2.9.0 1.2.0 + 1 2.0.1 2.3.3 1.6.2 @@ -315,7 +316,7 @@ 9.4.48.v20220622 - 4.1.72.Final + 4.1.80.Final @@ -334,6 +335,13 @@ + org.apache.logging.log4j + log4j-bom + ${project.version} + pom + import + + org.slf4j slf4j-api ${slf4jVersion} @@ -459,12 +467,22 @@ org.apache.logging.log4j - log4j-slf4j18-impl + log4j-slf4j2-impl ${project.version} org.apache.logging.log4j log4j-jcl + ${project.version} + + + org.apache.logging.log4j + log4j-to-jul + ${project.version} + + + org.apache.logging.log4j + log4j-to-slf4j ${project.version} @@ -714,6 +732,12 @@ provided + javax.inject + javax.inject + ${javax.inject.version} + provided + + javax.jms javax.jms-api ${javax.jms.version} @@ -805,36 +829,11 @@ - org.junit.jupiter - junit-jupiter-engine - ${junitJupiterVersion} - test - - - org.junit.jupiter - junit-jupiter-api - ${junitJupiterVersion} - test - - - org.junit.platform - junit-platform-commons - 1.8.2 - test - - - - org.junit.jupiter - junit-jupiter-migrationsupport - ${junitJupiterVersion} - test - - - - org.junit.jupiter - junit-jupiter-params - ${junitJupiterVersion} - test + org.junit + junit-bom + ${junit5Version} + pom + import @@ -848,13 +847,6 @@ junit junit ${junitVersion} - test - - - - org.junit.vintage - junit-vintage-engine - ${junitJupiterVersion} test @@ -988,7 +980,7 @@ net.javacrumbs.json-unit json-unit - 2.32.0 + 2.35.0 test @@ -1013,7 +1005,7 @@ co.elastic.logging log4j2-ecs-layout - 1.4.0 + 1.5.0 org.elasticsearch.client @@ -1300,7 +1292,7 @@ io.fabric8 docker-maven-plugin - 0.40.1 + 0.40.2 @@ -1701,7 +1693,7 @@ log4j-core-its log4j-1.2-api log4j-slf4j-impl - log4j-slf4j18-impl + log4j-slf4j2-impl log4j-to-slf4j log4j-to-jul log4j-jcl diff --git a/src/changes/announcement.vm b/src/changes/announcement.vm index 8372038..06423d9 100644 --- a/src/changes/announcement.vm +++ b/src/changes/announcement.vm @@ -68,9 +68,8 @@ This release primarily contains bug fixes and minor enhancements. Due to a break in compatibility in the SLF4J binding, Log4j now ships with two versions of the SLF4J to Log4j adapters. -log4j-slf4j-impl should be used with SLF4J 1.7.x and earlier and log4j-slf4j18-impl should be used with SLF4J 1.8.x and -later. SLF4J-2.0.0 alpha releases are not fully supported. See https://issues.apache.org/jira/browse/LOG4J2-2975 and -https://jira.qos.ch/browse/SLF4J-511. +log4j-slf4j-impl should be used with SLF4J 1.7.x and earlier and log4j-slf4j2-impl should be used with SLF4J 2.x and +later. SLF4J-1.8.x is no longer supported as a GA release never occurred. The Log4j ${relVersion} API, as well as many core components, maintains binary compatibility with previous releases. diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9ab5cb8..377fbc8 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -29,6 +29,71 @@ - "update" - Change - "remove" - Removed --> + + + Add getExplicitLevel method to LoggerConfig. + + + Allow Plugins to be injected with the LoggerContext reference. + + + Allow PropertySources to be added. + + + Generate new SSL certs for testing. + + + Make JsonTemplateLayout stack trace truncation operate for each label block. + + + Removed build page in favor of a single build instructions file. + + + SystemPropertyArbiter was assigning the value as the name. + + + Logger$PrivateConfig.filter(Level, Marker, String) was allocating empty varargs array. + + + Allows a space separated list of style specifiers in the %style pattern for consistency with %highlight. + + + Fix NPE in `log4j-to-jul` in the case the root logger level is null. + + + Add correct manifest entries for OSGi to log4j-jcl + + + Fix RollingRandomAccessFileAppender with DirectWriteRolloverStrategy can't create the first log file of different directory. + + + Fix ServiceLoaderUtil behavior in the presence of a SecurityManager. + + + Fix resolution of properties not starting with `log4j2.`. + + + Add support for SLF4J2 stack-valued MDC. + + + Add implementation of SLF4J2 fluent API. + + + Remove SLF4J 1.8.x binding. + + + Fix recursion between Log4j 1.2 LogManager and Category. + + + Fix regression in Rfc5424Layout default values. + + + Improve support for passwordless keystores. + + + Add async support to `Log4jServletFilter`. + + DirectWriteRolloverStrategy should use the current time when creating files. @@ -408,70 +473,70 @@ Add optional additional fields to NoSQLAppender. - + Bump Tomcat from 8.5.20 to 10.0.14 in log4j-appserver along with some dependency clean-up. - + Bump mongodb3.version from 3.12.4 to 3.12.10 #605. - + Bump awaitility from 4.0.3 to 4.1.1 #663. - + Bump Mockito from 3.11.2 to 4.2.0. - + Bump Jackson from 2.12.4 to 2.13.1. - + Bump org.junit.jupiter:junit-* from 5.7.2 to 5.8.2. - + Bump org.springframework:spring-* from 5.3.13 to 5.3.15. - + Bump de.flapdoodle.embed:de.flapdoodle.embed.mongo from 3.0.0 to 3.4.1. Update tests for binary incompatibilities in APIs. - + Bump org.fusesource.jansi:jansi from 2.3.4 to 2.4.0. - + Bump net.javacrumbs.json-unit:json-unit from 2.27.0 to 2.28.0. - + Bump org.assertj:assertj-core from 3.20.2 to 3.22.0. - + Bump org.xmlunit:xmlunit-* from 2.8.3 to 2.8.4. - + Bump org.codehaus.groovy:groovy-* from 3.0.8 to 3.0.9. - + Bump org.mongodb:bson from 4.2.2 to 4.5.0. - + Bump org.mongodb:mongodb-driver-sync from 4.2.2 to 4.5.0. - + Bump Woodstox 6.2.6 to 6.2.8. - + Bump maven-compiler-plugin from 3.8.1 to 3.9.0. - + Bump org.eclipse.persistence:org.eclipse.persistence.jpa from 2.7.9 to 2.7.10. - + Bump org.junit-pioneer:junit-pioneer from 1.5.0 to 1.6.1. - + Bump org.apache.activemq:activemq-broker from 5.16.3 to 5.16.4. - + Bump org.jacoco:jacoco-maven-plugin from 0.8.6 to 0.8.7. - + Bump org.apache.maven.plugins:maven-javadoc-plugin from 3.3.1 to 3.3.2. @@ -500,7 +565,7 @@ Fix MapLookup to lookup MapMessage before DefaultMap - Buffered I/O checked had inverted logic in RollingFileAppenderBuidler. + Buffered I/O checked had inverted logic in RollingFileAppenderBuilder. Fix NPE when input is null in StrSubstitutor.replace(String, Properties). @@ -552,7 +617,7 @@ using '%m{lookups}'. - Allow fractional attributes for size attribute of SizeBsaedTriggeringPolicy. + Allow fractional attributes for size attribute of SizeBasedTriggeringPolicy. Add support for Jakarta EE 9 (Tomcat 10 / Jetty 11) @@ -599,7 +664,7 @@ Allow MapMessage and ThreadContext attributes to be prefixed. - Add improved MapMessge support to GelfLayout. + Add improved MapMessage support to GelfLayout. Add RepeatPatternConverter. @@ -634,7 +699,7 @@ Fix Log Event Level vs Logger Config Level table. - Minor documentation correctsion regarding log levels. + Minor documentation corrections regarding log levels. Minor documentation corrections in the configuration section. @@ -1069,7 +1134,7 @@ Move Spring Lookup and Spring PropertySource to its own module. - Log4j-web should now stores the servlet context as a map entry instead of in the single external context field. + Log4j-web should now store the servlet context as a map entry instead of in the single external context field. Javadoc link in ThreadContext description was incorrect. @@ -1085,7 +1150,7 @@ size based rollover after a time based rollover had occurred. - Rollover was failing to create directories when using a DirectFileeRolloverStrategy. + Rollover was failing to create directories when using a DirectFileRolloverStrategy. Fixed typos where mergeFactory should be mergeStrategy. @@ -1260,7 +1325,7 @@ Slow initialization on Windows due to accessing network interfaces. - + Conditionally perform status logging calculations in PluginRegistry. @@ -1297,7 +1362,7 @@ PluginProcessor should use Messager instead of System.out. - MapMessage.getFormattedMesssage() would incorrectly format objects. + MapMessage.getFormattedMessage() would incorrectly format objects. Always write header on a new OutputStream. @@ -1313,7 +1378,7 @@ NullPointerException when using a custom DirectFileRolloverStrategy without a file name. - Add mulit-parameter overloads to LogBuilder. + Add multi-parameter overloads to LogBuilder. Fixed NullPointerException after reconfiguring via JMX. @@ -1388,7 +1453,7 @@ Add ThreadContext.putIfNotNull method. - Add a Level Patttern Selector. + Add a Level Pattern Selector. Update Jackson to 2.9.10. @@ -1484,7 +1549,7 @@ Restore constructor to ThrowablePatternConverter that was removed in 2.8.2. - StructuredDataId was ignoring maxLength atribute. + StructuredDataId was ignoring maxLength attribute. RFC5424Layout was not properly setting default Structured Element id for the MDC @@ -1554,7 +1619,7 @@ PatternConverter instanceOf methods with unknown parameter types no longer elide those with known parameters. - AsyncQueueFullPolicy configuration short values "Default" and "Discard" are case insensitive to avoid confusion. + AsyncQueueFullPolicy configuration short values "Default" and "Discard" are case-insensitive to avoid confusion. NullPointerException at org.apache.logging.log4j.core.appender.db.jdbc.JdbcDatabaseManager.writeInternal(JdbcDatabaseManager.java:803). @@ -1868,7 +1933,7 @@ logged throwable and provided varargs array. - Nested logging doesn't clobber AbstractStringLayout cached StringBuidlers + Nested logging doesn't clobber AbstractStringLayout cached StringBuilders StringBuilders.escapeJson implementation runs in linear time. Escaping large JSON strings @@ -2104,7 +2169,7 @@ MapMessage should use deep toString for values. - MapMessage supports both StringBuilderFormattable and MultiformatMessage. + MapMessage supports both StringBuilderFormattable and MultiFormatMessage. MapMessage JSON encoding will escape keys and values. @@ -2349,7 +2414,7 @@ Consider the StringBuilder's capacity instead of content length when trimming. - Register log4j-core as an OSGi service. Skip tests for LOG4J2-1766 on MacOS. Use group "staff" for LOG4J2-1699 test on MacOS. + Register log4j-core as an OSGi service. Skip tests for LOG4J2-1766 on macOS. Use group "staff" for LOG4J2-1699 test on macOS. TcpSocketServer does not close accepted Sockets. @@ -2409,7 +2474,7 @@ JMS Appender does not know how to recover from a broken connection. - JMS Appender should be able connect to a broker (later) even it is not present at configuration time. + JMS Appender should be able to connect to a broker (later) even it is not present at configuration time. JMS Appender broker password should be a char[], not a String. @@ -3306,7 +3371,7 @@ minSize attribute. - Fixed minor issues with the 2.6.1 web site. + Fixed minor issues with the 2.6.1 website. Ensure that the thread-local StringBuilders used by Layouts to format log events to text will not @@ -3390,7 +3455,7 @@ Added documentation about plugin builders compared to factories. - Fixed minor issues with the 2.6 web site. + Fixed minor issues with the 2.6 website. @@ -4111,7 +4176,7 @@ Avoid creating temporary array object in org.apache.logging.slf4j.Log4jMarker.iterator(). - log4j-web-2.1 should workaround a bug in JBOSS EAP 6.2. + log4j-web-2.1 should work around a bug in JBOSS EAP 6.2. MongoDB appender, username and password should be optional. @@ -4362,7 +4427,7 @@ Documentation: add sections on the JUL Adapter, IO Streams and NoSQL Appenders to the Maven and Ivy page. - Documentation: clarified why log4j-core is a compile time dependency in Maven and Ivy page. + Documentation: clarified why log4j-core is a compile-time dependency in Maven and Ivy page. Documentation: fix broken links on Appenders manual page. @@ -4387,7 +4452,7 @@ Documentation: added section on XInclude to user manual Configuration page. - Documentation: fixed minor issues with Log4j2 web site/documentation. + Documentation: fixed minor issues with Log4j2 website/documentation. Update JMH to 1.1 from 0.7.2. @@ -4735,7 +4800,7 @@ Create an appender to route log events to the ServletContext log. - Support default value for missing key in look ups with fallback to looking in the properties map. + Support default value for missing key in look-ups with fallback to looking in the properties map. FlumeAvroManager now always uses a client type of default_failover. @@ -5213,7 +5278,7 @@ [Pattern Layout] Customize level names to lower-case. - Add support for multiple SD-ELEMENTs in a RFC 5424 syslog message. + Add support for multiple SD-ELEMENTs in an RFC 5424 syslog message. Cleaned up tests and cleared up documentation for the JPA appender following the resolution of EclipseLink @@ -5557,7 +5622,7 @@ Improved error reporting when misconfigured. - Disruptor will now shutdown during Tomcat shutdown. + Disruptor will now shut down during Tomcat shutdown. Renamed AsynchAppender to AsyncAppender. Plugin name became Async (was Asynch). @@ -5854,7 +5919,7 @@ Created combined jar to combine API and Core contents for users who only want the Log4j implementation. - Convert LogManager binding to use "regular" java properties instead of XML properties to workaround a + Convert LogManager binding to use "regular" java properties instead of XML properties to work around a bug in Oracle's xmlparserv2 jar. diff --git a/src/tools/sort-pom.xslt b/src/tools/sort-pom.xslt new file mode 100644 index 0000000..7d38b37 --- /dev/null +++ b/src/tools/sort-pom.xslt @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file