This is an automated email from the ASF dual-hosted git repository. pkarwasz pushed a commit to branch slf4j-2.0 in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit 8dcdb7c7d9c38ab80c2d6957cf52ab2cefc48e2a Author: Piotr P. Karwasz <[email protected]> AuthorDate: Mon Apr 11 23:59:26 2022 +0200 [LOG4J2-3370] Initial SLF4J 2.0.x support --- log4j-slf4j20-impl/pom.xml | 255 +++++++++++++ .../java/org/apache/logging/slf4j/Log4jLogger.java | 420 +++++++++++++++++++++ .../apache/logging/slf4j/Log4jLoggerFactory.java | 75 ++++ .../org/apache/logging/slf4j/Log4jMDCAdapter.java | 60 +++ .../java/org/apache/logging/slf4j/Log4jMarker.java | 126 +++++++ .../apache/logging/slf4j/Log4jMarkerFactory.java | 138 +++++++ .../logging/slf4j/SLF4JLoggingException.java | 41 ++ .../apache/logging/slf4j/SLF4JServiceProvider.java | 59 +++ .../org/apache/logging/slf4j/package-info.java | 22 ++ .../services/org.slf4j.spi.SLF4JServiceProvider | 1 + log4j-slf4j20-impl/src/site/markdown/index.md | 40 ++ log4j-slf4j20-impl/src/site/site.xml | 52 +++ .../logging/other/pkg/LoggerContextAnchorTest.java | 91 +++++ .../logging/slf4j/CallerInformationTest.java | 67 ++++ .../org/apache/logging/slf4j/CustomFlatMarker.java | 76 ++++ .../org/apache/logging/slf4j/Log4j1222Test.java | 57 +++ .../logging/slf4j/Log4j2_1482_Slf4jTest.java | 41 ++ .../org/apache/logging/slf4j/Log4jMarkerTest.java | 47 +++ .../apache/logging/slf4j/LoggerContextTest.java | 44 +++ .../java/org/apache/logging/slf4j/LoggerTest.java | 161 ++++++++ .../java/org/apache/logging/slf4j/MarkerTest.java | 186 +++++++++ .../org/apache/logging/slf4j/OverflowTest.java | 43 +++ .../org/apache/logging/slf4j/SerializeTest.java | 46 +++ .../src/test/resources/log4j-test1.xml | 33 ++ .../src/test/resources/log4j2-1482.xml | 27 ++ pom.xml | 1 + 26 files changed, 2209 insertions(+) diff --git a/log4j-slf4j20-impl/pom.xml b/log4j-slf4j20-impl/pom.xml new file mode 100644 index 0000000000..8d516fdc1b --- /dev/null +++ b/log4j-slf4j20-impl/pom.xml @@ -0,0 +1,255 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j</artifactId> + <version>2.17.3-SNAPSHOT</version> + </parent> + <artifactId>log4j-slf4j20-impl</artifactId> + <packaging>jar</packaging> + <name>Apache Log4j SLF4J 2.0+ Binding</name> + <description>The Apache Log4j SLF4J 2.0 API binding to Log4j 2 Core</description> + <properties> + <log4jParentDir>${basedir}/..</log4jParentDir> + <docLabel>SLF4J Documentation</docLabel> + <projectDir>/slf4j18</projectDir> + <slf4j.version>2.0.0-alpha5</slf4j.version> + <module.name>org.apache.logging.log4j.slf4j</module.name> + <maven.doap.skip>true</maven.doap.skip> + </properties> + <dependencies> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${slf4j.version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-ext</artifactId> + <version>${slf4j.version}</version> + <optional>true</optional> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <scope>runtime</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-api</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-csv</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-core</artifactId> + <type>test-jar</type> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-to-slf4j</artifactId> + <scope>test</scope> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.junit.vintage</groupId> + <artifactId>junit-vintage-engine</artifactId> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + </dependency> + </dependencies> + <build> + <plugins> + <!-- Include the standard NOTICE and LICENSE --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-remote-resources-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>process</goal> + </goals> + <configuration> + <skip>false</skip> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <executions> + <execution> + <id>loop-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <includes> + <include>**/OverflowTest.java</include> + </includes> + </configuration> + </execution> + <execution> + <id>default-test</id> + <phase>test</phase> + <goals> + <goal>test</goal> + </goals> + <configuration> + <includes> + <include>**/*Test.java</include> + </includes> + <excludes> + <exclude>**/OverflowTest.java</exclude> + </excludes> + <classpathDependencyExcludes> + <classpathDependencyExcludes>org.apache.logging.log4j:log4j-to-slf4j</classpathDependencyExcludes> + </classpathDependencyExcludes> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Export-Package> + org.apache.logging.slf4j, + org.slf4j.impl + </Export-Package> + <Require-Capability> + osgi.extender;filter:="(osgi.extender=osgi.serviceloader.registrar)" + </Require-Capability> + <Provide-Capability> + osgi.serviceloader;osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider + </Provide-Capability> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-changes-plugin</artifactId> + <version>${changes.plugin.version}</version> + <reportSets> + <reportSet> + <reports> + <report>changes-report</report> + </reports> + </reportSet> + </reportSets> + <configuration> + <issueLinkTemplate>%URL%/show_bug.cgi?id=%ISSUE%</issueLinkTemplate> + <useJql>true</useJql> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-checkstyle-plugin</artifactId> + <version>${checkstyle.plugin.version}</version> + <configuration> + <!--<propertiesLocation>${vfs.parent.dir}/checkstyle.properties</propertiesLocation> --> + <configLocation>${log4jParentDir}/checkstyle.xml</configLocation> + <suppressionsLocation>${log4jParentDir}/checkstyle-suppressions.xml</suppressionsLocation> + <enableRulesSummary>false</enableRulesSummary> + <propertyExpansion>basedir=${basedir}</propertyExpansion> + <propertyExpansion>licensedir=${log4jParentDir}/checkstyle-header.txt</propertyExpansion> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>${javadoc.plugin.version}</version> + <configuration> + <bottom><![CDATA[<p align="center">Copyright © {inceptionYear}-{currentYear} {organizationName}. All Rights Reserved.<br /> + 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.</p>]]></bottom> + <!-- module link generation is completely broken in the javadoc plugin for a multi-module non-aggregating + project --> + <detectOfflineLinks>false</detectOfflineLinks> + <linksource>true</linksource> + </configuration> + <reportSets> + <reportSet> + <id>non-aggregate</id> + <reports> + <report>javadoc</report> + </reports> + </reportSet> + </reportSets> + </plugin> + <plugin> + <groupId>com.github.spotbugs</groupId> + <artifactId>spotbugs-maven-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jxr-plugin</artifactId> + <version>${jxr.plugin.version}</version> + <reportSets> + <reportSet> + <id>non-aggregate</id> + <reports> + <report>jxr</report> + </reports> + </reportSet> + <reportSet> + <id>aggregate</id> + <reports> + <report>aggregate</report> + </reports> + </reportSet> + </reportSets> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-pmd-plugin</artifactId> + <version>${pmd.plugin.version}</version> + <configuration> + <targetJdk>${maven.compiler.target}</targetJdk> + </configuration> + </plugin> + </plugins> + </reporting> +</project> + diff --git a/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java new file mode 100644 index 0000000000..10ad49ca7e --- /dev/null +++ b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLogger.java @@ -0,0 +1,420 @@ +/* + * 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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.java new file mode 100644 index 0000000000..99e6817a1a --- /dev/null +++ b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jLoggerFactory.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 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<Logger> implements ILoggerFactory { + + private static final StatusLogger LOGGER = StatusLogger.getLogger(); + private static final String SLF4J_PACKAGE = "org.slf4j"; + private static final Predicate<Class<?>> 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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java new file mode 100644 index 0000000000..b12c297ac3 --- /dev/null +++ b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMDCAdapter.java @@ -0,0 +1,60 @@ +/* + * 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<String, String> 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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMarker.java new file mode 100644 index 0000000000..fb21659103 --- /dev/null +++ b/log4j-slf4j20-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<Marker> iterator() { + final org.apache.logging.log4j.Marker[] log4jParents = this.marker.getParents(); + final List<Marker> 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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java new file mode 100644 index 0000000000..69ea94b33c --- /dev/null +++ b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/Log4jMarkerFactory.java @@ -0,0 +1,138 @@ +/* + * 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<String, Marker> 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<Marker>()); + } + + private static org.apache.logging.log4j.Marker convertMarker(final Marker original, + final Collection<Marker> visited) { + final org.apache.logging.log4j.Marker marker = MarkerManager.getMarker(original.getName()); + if (original.hasReferences()) { + final Iterator<Marker> 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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/SLF4JLoggingException.java new file mode 100644 index 0000000000..0a41215383 --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/SLF4JServiceProvider.java new file mode 100644 index 0000000000..b711941a91 --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/main/java/org/apache/logging/slf4j/package-info.java b/log4j-slf4j20-impl/src/main/java/org/apache/logging/slf4j/package-info.java new file mode 100644 index 0000000000..ec0f031c37 --- /dev/null +++ b/log4j-slf4j20-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 <em>not</em> 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-slf4j20-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider b/log4j-slf4j20-impl/src/main/resources/META-INF/services/org.slf4j.spi.SLF4JServiceProvider new file mode 100644 index 0000000000..1577f12daf --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/site/markdown/index.md b/log4j-slf4j20-impl/src/site/markdown/index.md new file mode 100644 index 0000000000..6e3f3f69d1 --- /dev/null +++ b/log4j-slf4j20-impl/src/site/markdown/index.md @@ -0,0 +1,40 @@ +<!-- vim: set syn=markdown : --> +<!-- + 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. +--> + +# 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. + +<div class="alert alert-danger"> +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. +</div> diff --git a/log4j-slf4j20-impl/src/site/site.xml b/log4j-slf4j20-impl/src/site/site.xml new file mode 100644 index 0000000000..a1f9106932 --- /dev/null +++ b/log4j-slf4j20-impl/src/site/site.xml @@ -0,0 +1,52 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> +<project name="SLF4J Binding Using Log4j" + xmlns="http://maven.apache.org/DECORATION/1.4.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/DECORATION/1.4.0 http://maven.apache.org/xsd/decoration-1.4.0.xsd"> + <body> + <links> + <item name="Apache" href="http://www.apache.org/" /> + <item name="Logging Services" href="http://logging.apache.org/"/> + <item name="Log4j" href="../index.html"/> + </links> + + <!-- Component-specific reports --> + <menu ref="reports"/> + + <!-- Overall Project Info --> + <menu name="Log4j Project Information" img="icon-info-sign"> + <item name="Dependencies" href="../dependencies.html" /> + <item name="Dependency Convergence" href="../dependency-convergence.html" /> + <item name="Dependency Management" href="../dependency-management.html" /> + <item name="Project Team" href="../team-list.html" /> + <item name="Mailing Lists" href="../mail-lists.html" /> + <item name="Issue Tracking" href="../issue-tracking.html" /> + <item name="Project License" href="../license.html" /> + <item name="Source Repository" href="../source-repository.html" /> + <item name="Project Summary" href="../project-summary.html" /> + </menu> + + <menu name="Log4j Project Reports" img="icon-cog"> + <item name="Changes Report" href="../changes-report.html" /> + <item name="JIRA Report" href="../jira-report.html" /> + <item name="Surefire Report" href="../surefire-report.html" /> + <item name="RAT Report" href="../rat-report.html" /> + </menu> + </body> +</project> diff --git a/log4j-slf4j20-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/other/pkg/LoggerContextAnchorTest.java new file mode 100644 index 0000000000..2b0fe91c20 --- /dev/null +++ b/log4j-slf4j20-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<String> 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.java new file mode 100644 index 0000000000..7efc0e5090 --- /dev/null +++ b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/CallerInformationTest.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.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<String> 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<String> 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/CustomFlatMarker.java new file mode 100644 index 0000000000..0138cead6e --- /dev/null +++ b/log4j-slf4j20-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 <a href="https://issues.apache.org/jira/browse/LOG4J2-793">LOG4J2-793</a> + */ +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<Marker> 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4j1222Test.java new file mode 100644 index 0000000000..59f69e0d2d --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4j2_1482_Slf4jTest.java new file mode 100644 index 0000000000..8934f29a77 --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/Log4jMarkerTest.java new file mode 100644 index 0000000000..ac6ad22b39 --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/LoggerContextTest.java new file mode 100644 index 0000000000..de37a3614f --- /dev/null +++ b/log4j-slf4j20-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<LoggerContext> 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java new file mode 100644 index 0000000000..7c8e71379f --- /dev/null +++ b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/LoggerTest.java @@ -0,0 +1,161 @@ +/* + * 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 <a href="https://issues.apache.org/jira/browse/LOG4J2-793">LOG4J2-793</a> + */ + @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); + } + + private void verify(final String name, final String expected) { + final ListAppender listApp = ctx.getListAppender(name); + assertNotNull("Missing Appender", listApp); + final List<String> 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/MarkerTest.java new file mode 100644 index 0000000000..c078fe03fe --- /dev/null +++ b/log4j-slf4j20-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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java new file mode 100644 index 0000000000..bcb9d0b669 --- /dev/null +++ b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/OverflowTest.java @@ -0,0 +1,43 @@ +/* + * 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-slf4j20-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java b/log4j-slf4j20-impl/src/test/java/org/apache/logging/slf4j/SerializeTest.java new file mode 100644 index 0000000000..d3d3db6c03 --- /dev/null +++ b/log4j-slf4j20-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 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-slf4j20-impl/src/test/resources/log4j-test1.xml b/log4j-slf4j20-impl/src/test/resources/log4j-test1.xml new file mode 100644 index 0000000000..07a2be6316 --- /dev/null +++ b/log4j-slf4j20-impl/src/test/resources/log4j-test1.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration status="error" name="LoggerTest"> + <properties> + <property name="filename">target/test.log</property> + </properties> + <ThresholdFilter level="trace"/> + + <Appenders> + <Console name="STDOUT"> + <PatternLayout pattern="%C{1.} %m MDC%X%n"/> + </Console> + <File name="File" fileName="${filename}"> + <PatternLayout> + <pattern>%d %p %C{1.} [%t] %m%n</pattern> + </PatternLayout> + </File> + <List name="List"> + <PatternLayout pattern="%C{1.} %m MDC%X%n%ex{0}"/> + </List> + <SLF4J name="SLF4J"/> + </Appenders> + + <Loggers> + <Logger name="org.apache.logging.log4j.test2" level="debug" additivity="false"> + <AppenderRef ref="File"/> + </Logger> + + <Root level="trace"> + <AppenderRef ref="List"/> + </Root> + </Loggers> + +</configuration> diff --git a/log4j-slf4j20-impl/src/test/resources/log4j2-1482.xml b/log4j-slf4j20-impl/src/test/resources/log4j2-1482.xml new file mode 100644 index 0000000000..e17953ca93 --- /dev/null +++ b/log4j-slf4j20-impl/src/test/resources/log4j2-1482.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Configuration status="warn" name="MyApp" packages=""> + <Properties> + <Property name="audit-path">target/log4j2-1482</Property> + <Property name="file-name">audit</Property> + <Property name="file-header">param1,param2,param3${sys:line.separator} + </Property> + </Properties> + + <Appenders> + <RollingFile name="auditfile" fileName="${audit-path}/${file-name}.tmp" + filePattern="${audit-path}/${file-name}-%d{yyyy-MM-dd}-%i.csv"> + <CsvParameterLayout delimiter="," header="${file-header}"> + </CsvParameterLayout> + <Policies> + <SizeBasedTriggeringPolicy size="80 B" /> + </Policies> + <DefaultRolloverStrategy max="2" /> + </RollingFile> + </Appenders> + + <Loggers> + <Root level="info"> + <AppenderRef ref="auditfile" /> + </Root> + </Loggers> +</Configuration> \ No newline at end of file diff --git a/pom.xml b/pom.xml index 11ccfd43da..e816010853 100644 --- a/pom.xml +++ b/pom.xml @@ -1621,6 +1621,7 @@ <module>log4j-1.2-api</module> <module>log4j-slf4j-impl</module> <module>log4j-slf4j18-impl</module> + <module>log4j-slf4j20-impl</module> <module>log4j-to-slf4j</module> <module>log4j-to-jul</module> <module>log4j-jcl</module>
