From 385e8eae208173c6962f4274d1d8bb02b6fb1883 Mon Sep 17 00:00:00 2001 From: "Piotr P. Karwasz" Date: Sun, 28 Jan 2024 22:17:57 +0100 Subject: [PATCH] Add Tomcat JULI implementation Adds a Tomcat JULI implementation that can replace the one in `org.apache.logging.log4:log4j-appserver`. The main differences between this implementation and the official one: * it retrieves the correct logger context by setting `fqcn` to `org.apache.juli.LogFactory` instead of the class name of the logger, * it does not treat the filenames `log4j2-tomcat.*` differently, leaving the configuration entirely to the implementation. --- log4j-tomcat-juli/pom.xml | 75 +++++++++ .../copernik/log4j/tomcat/juli/Log4jLog.java | 143 ++++++++++++++++++ .../src/test/java/Log4jLogTest.java | 81 ++++++++++ .../src/test/resources/log4j2-test.xml | 26 ++++ pom.xml | 1 + src/changelog/.3.x.x/101_add_tomcat_juli.xml | 10 ++ 6 files changed, 336 insertions(+) create mode 100644 log4j-tomcat-juli/pom.xml create mode 100644 log4j-tomcat-juli/src/main/java/eu/copernik/log4j/tomcat/juli/Log4jLog.java create mode 100644 log4j-tomcat-juli/src/test/java/Log4jLogTest.java create mode 100644 log4j-tomcat-juli/src/test/resources/log4j2-test.xml create mode 100644 src/changelog/.3.x.x/101_add_tomcat_juli.xml diff --git a/log4j-tomcat-juli/pom.xml b/log4j-tomcat-juli/pom.xml new file mode 100644 index 0000000..590c27d --- /dev/null +++ b/log4j-tomcat-juli/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + eu.copernik + log4j-plugins-parent + 3.0.0-SNAPSHOT + + + org.apache.logging.log4j + log4j-tomcat-juli + Tomcat JULI to Log4j API bridge + Tomcat JULI implementation that forwards to the Log4j API. + + + false + + + + + biz.aQute.bnd + biz.aQute.bnd.annotation + provided + + + org.apache.logging.log4j + log4j-api + + + org.apache.tomcat + tomcat-juli + provided + + + org.assertj + assertj-core + test + + + org.junit.jupiter + junit-jupiter-api + test + + + org.apache.logging.log4j + log4j-api-test + test + + + org.apache.logging.log4j + log4j-core-test + test + + + org.mockito + mockito-core + test + + + diff --git a/log4j-tomcat-juli/src/main/java/eu/copernik/log4j/tomcat/juli/Log4jLog.java b/log4j-tomcat-juli/src/main/java/eu/copernik/log4j/tomcat/juli/Log4jLog.java new file mode 100644 index 0000000..817846e --- /dev/null +++ b/log4j-tomcat-juli/src/main/java/eu/copernik/log4j/tomcat/juli/Log4jLog.java @@ -0,0 +1,143 @@ +/* + * Copyright © 2024 Piotr P. Karwasz + * + * Licensed 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 eu.copernik.log4j.tomcat.juli; + +import aQute.bnd.annotation.Resolution; +import aQute.bnd.annotation.spi.ServiceProvider; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.MarkerManager; +import org.apache.logging.log4j.spi.ExtendedLogger; +import org.apache.logging.log4j.spi.LoggerContextFactory; + +/** + * An implementation of {@link Log} that forwards everything to the appropriate Log4j API logger. + */ +@ServiceProvider(value = Log.class, resolution = Resolution.OPTIONAL) +public class Log4jLog implements Log { + + private static final String FQCN = Log4jLog.class.getName(); + private static final String LOG_FACTORY_FQCN = LogFactory.class.getName(); + private static final Marker TOMCAT_MARKER = MarkerManager.getMarker("TOMCAT"); + + private ExtendedLogger logger; + + // Only used by ServiceLoader + public Log4jLog() { + this(LogManager.ROOT_LOGGER_NAME); + } + + public Log4jLog(final String name) { + this(LogManager.getFactory(), name); + } + + Log4jLog(final LoggerContextFactory factory, final String name) { + this.logger = factory.getContext(LOG_FACTORY_FQCN, null, null, false).getLogger(name); + } + + @Override + public boolean isDebugEnabled() { + return logger.isDebugEnabled(TOMCAT_MARKER); + } + + @Override + public boolean isErrorEnabled() { + return logger.isDebugEnabled(TOMCAT_MARKER); + } + + @Override + public boolean isFatalEnabled() { + return logger.isFatalEnabled(TOMCAT_MARKER); + } + + @Override + public boolean isInfoEnabled() { + return logger.isInfoEnabled(TOMCAT_MARKER); + } + + @Override + public boolean isTraceEnabled() { + return logger.isTraceEnabled(TOMCAT_MARKER); + } + + @Override + public boolean isWarnEnabled() { + return logger.isWarnEnabled(TOMCAT_MARKER); + } + + @Override + public void trace(final Object message) { + logger.logIfEnabled(FQCN, Level.TRACE, TOMCAT_MARKER, message, null); + } + + @Override + public void trace(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.TRACE, TOMCAT_MARKER, message, t); + } + + @Override + public void debug(final Object message) { + logger.logIfEnabled(FQCN, Level.DEBUG, TOMCAT_MARKER, message, null); + } + + @Override + public void debug(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.DEBUG, TOMCAT_MARKER, message, t); + } + + @Override + public void info(final Object message) { + logger.logIfEnabled(FQCN, Level.INFO, TOMCAT_MARKER, message, null); + } + + @Override + public void info(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.INFO, TOMCAT_MARKER, message, t); + } + + @Override + public void warn(final Object message) { + logger.logIfEnabled(FQCN, Level.WARN, TOMCAT_MARKER, message, null); + } + + @Override + public void warn(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.WARN, TOMCAT_MARKER, message, t); + } + + @Override + public void error(final Object message) { + logger.logIfEnabled(FQCN, Level.ERROR, TOMCAT_MARKER, message, null); + } + + @Override + public void error(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.ERROR, TOMCAT_MARKER, message, t); + } + + @Override + public void fatal(final Object message) { + logger.logIfEnabled(FQCN, Level.FATAL, TOMCAT_MARKER, message, null); + } + + @Override + public void fatal(final Object message, final Throwable t) { + logger.logIfEnabled(FQCN, Level.FATAL, TOMCAT_MARKER, message, t); + } +} diff --git a/log4j-tomcat-juli/src/test/java/Log4jLogTest.java b/log4j-tomcat-juli/src/test/java/Log4jLogTest.java new file mode 100644 index 0000000..3a738c1 --- /dev/null +++ b/log4j-tomcat-juli/src/test/java/Log4jLogTest.java @@ -0,0 +1,81 @@ +/* + * Copyright © 2024 Piotr P. Karwasz + * + * Licensed 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. + */ +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.logging.log4j.Level; +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.test.appender.ListAppender; +import org.junit.jupiter.api.Test; + +class Log4jLogTest { + + private static final String MESSAGE = "MESSAGE"; + private static final Throwable T = new RuntimeException(); + + @Test + void location() { + final Log log = LogFactory.getLog("location"); + int i = 0; + int currentLine = 37; + log.trace(MESSAGE + i++); + log.trace(MESSAGE + i++, T); + log.debug(MESSAGE + i++); + log.debug(MESSAGE + i++, T); + log.info(MESSAGE + i++); + log.info(MESSAGE + i++, T); + log.warn(MESSAGE + i++); + log.warn(MESSAGE + i++, T); + log.error(MESSAGE + i++); + log.error(MESSAGE + i++, T); + log.fatal(MESSAGE + i++); + log.fatal(MESSAGE + i, T); + // Verification + final LoggerContext context = LoggerContext.getContext(false); + final Configuration config = context.getConfiguration(); + final ListAppender list = config.getAppender("list"); + final List events = list.getEvents(); + i = 0; + assertThat(events).hasSize(12); + assertLocation(events.get(i), Level.TRACE, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.TRACE, MESSAGE + i++, T, ++currentLine); + assertLocation(events.get(i), Level.DEBUG, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.DEBUG, MESSAGE + i++, T, ++currentLine); + assertLocation(events.get(i), Level.INFO, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.INFO, MESSAGE + i++, T, ++currentLine); + assertLocation(events.get(i), Level.WARN, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.WARN, MESSAGE + i++, T, ++currentLine); + assertLocation(events.get(i), Level.ERROR, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.ERROR, MESSAGE + i++, T, ++currentLine); + assertLocation(events.get(i), Level.FATAL, MESSAGE + i++, null, ++currentLine); + assertLocation(events.get(i), Level.FATAL, MESSAGE + i, T, ++currentLine); + } + + private void assertLocation( + final LogEvent event, final Level level, final String message, final Throwable t, final int lineNumber) { + assertThat(event.getLevel()).isEqualTo(level); + assertThat(event.getMessage().getFormattedMessage()).isEqualTo(message); + assertThat(event.getThrown()).isEqualTo(t); + final StackTraceElement location = event.getSource(); + assertThat(location.getClassName()).isEqualTo(Log4jLogTest.class.getName()); + assertThat(location.getMethodName()).isEqualTo("location"); + assertThat(location.getLineNumber()).isEqualTo(lineNumber); + } +} diff --git a/log4j-tomcat-juli/src/test/resources/log4j2-test.xml b/log4j-tomcat-juli/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000..69ec410 --- /dev/null +++ b/log4j-tomcat-juli/src/test/resources/log4j2-test.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index b19124a..4ded00f 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ log4j-tomcat log4j-tomcat-env + log4j-tomcat-juli diff --git a/src/changelog/.3.x.x/101_add_tomcat_juli.xml b/src/changelog/.3.x.x/101_add_tomcat_juli.xml new file mode 100644 index 0000000..7bac115 --- /dev/null +++ b/src/changelog/.3.x.x/101_add_tomcat_juli.xml @@ -0,0 +1,10 @@ + + + + + Add Tomcat JULI implementation in artifact `log4j-tomcat-juli`. + +