I have a Web Servlet (6.0) running on Tomcat 10.1
in Eclipse and Java 11
that I just migrated to Jakarta. At compile-time, everything is fine, but at runtime, Tomcat keeps giving me the following error:
java.lang.IllegalStateException: Error starting child
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:686)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:658)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:713)
... 37 more
Caused by: java.lang.NoClassDefFoundError: Lorg/apache/logging/log4j/Logger;
at java.base/java.lang.Class.getDeclaredFields0(Native Method)
at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3061)
... 38 more
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1437)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1245)
... 51 more
I have followed various approaches across stackoverflow, but to no avail.
I have the following dependencies in my build.gradle
file:
plugins {
id 'eclipse'
id 'eclipse-wtp'
id 'war'
id 'application'
}
dependencies {
compileOnly group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '6.1.0'
implementation 'org.glassfish.jersey.core:jersey-server:3.1.7'
implementation 'org.glassfish.jersey.media:jersey-media-json-jackson:3.1.7'
implementation 'org.glassfish.jersey.containers:jersey-container-servlet:3.1.7'
implementation group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '3.1.7'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.14.0'
implementation group: 'jakarta.ws.rs', name: 'jakarta.ws.rs-api', version: '4.0.0'
implementation 'org.apache.logging.log4j:log4j-api:2.23.1'
implementation 'org.apache.logging.log4j:log4j-core:2.23.1'
implementation group: 'org.apache.logging.log4j', name: 'log4j-jakarta-web', version: '2.23.1'
implementation "net.bull.javamelody:javamelody-core2129.0"
}
I also tried adding SLF4J2 dependencies as suggested in this answer, but that didn't help either:
implementation group: 'org.slf4j', name: 'slf4j-api', version: '2.0.13'
implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j2-impl', version: '2.23.1'
implementation group: 'org.slf4j', name: 'slf4j-reload4j', version: '2.0.13'
I tried adding the path to my log4j2.xml
configuration file to context.xml
as suggested in this answer, also without success:
<Context>
<!-- ... -->
<Parameter name="log4j2.configurationFile" value="../WEB-INF/log4j2.xml"/>
</Context>
Additionally, I
What can I do to further investigate or resolve this issue?
Minimum executable successful example code:
environment:
├── settings.gradle (1)
├── build.gradle (2)
├── gradle
│ └── ...
├── gradlew
├── gradlew.bat
└── src
└── main
├── java
│ └── com
│ └── example
│ └── MyServlet.java (3)
├── resources
│ └── log4j2.xml (4)
└── webapp (empty, no file)
└── WEB-INF
rootProject.name = 'SimpleLog4j2Web'
plugins {
id 'java'
id 'war'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
compileOnly 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0'
// SLF4J API
implementation 'org.slf4j:slf4j-api:2.0.7'
// Log4j2 implementation for SLF4J
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0'
// Log4j2 core
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
// Log4j2 API
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
// Set the name of the generated WAR file
tasks.war {
archiveFileName = 'simplelog4j2web.war'
}
tasks.withType(JavaCompile) {
sourceCompatibility = '17'
targetCompatibility = '17'
}
The jars related to logging only require the following four:
// SLF4J API
implementation 'org.slf4j:slf4j-api:2.0.7'
// Log4j2 implementation for SLF4J
implementation 'org.apache.logging.log4j:log4j-slf4j2-impl:2.20.0'
// Log4j2 core
implementation 'org.apache.logging.log4j:log4j-core:2.20.0'
// Log4j2 API
implementation 'org.apache.logging.log4j:log4j-api:2.20.0'
package com.example;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
@WebServlet("/hello")
public class MyServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(MyServlet.class);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.info("Received request at /hello");
resp.getWriter().write("Hello, Jakarta EE 10 with SLF4J and Log4j2!");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- Console Appender Configuration -->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
</Console>
<!-- File Appender Configuration -->
<File name="File" fileName="../logs/app.log" append="true">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
</File>
</Appenders>
<Loggers>
<!-- Root Logger Configuration -->
<Root level="info">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Root>
<!-- Application Specific Logger Configuration -->
<Logger name="com.example" level="debug" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="File"/>
</Logger>
</Loggers>
</Configuration>
Note that the output setting of fileName="../logs/app.log" takes Tomcat/bin as the starting point, so "../logs" must be added to output to the Tomcat/logs directory.
fileName="../logs/app.log"
gradle clean build
copy build/libs/simplelog4j2web.war
to Tomcat/webapps/
cd Tomcat/bin
./startup.sh
Browser open "http://localhost:8080/simplelog4j2web/hello"
Tomcat/logs/
Find app.log under Tomcat/logs
Success write log.
apache-tomcat-10.1.24/webapps/simplelog4j2web
├── META-INF
│ ├── MANIFEST.MF
│ └── war-tracker
└── WEB-INF
├── classes
│ ├── com
│ │ └── example
│ │ └── MyServlet.class
│ └── log4j2.xml
└── lib
├── log4j-api-2.20.0.jar
├── log4j-core-2.20.0.jar
├── log4j-slf4j2-impl-2.20.0.jar
└── slf4j-api-2.0.7.jar
plugins {
id 'java'
id 'war'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.0.0'
compileOnly 'jakarta.servlet.jsp:jakarta.servlet.jsp-api:3.1.0'
// SLF4J API
implementation 'org.slf4j:slf4j-api:2.0.13'
// Logback Classic implementation (includes SLF4J binding)
implementation 'ch.qos.logback:logback-classic:1.5.6'
implementation 'ch.qos.logback:logback-core:1.5.6'
testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}
test {
useJUnitPlatform()
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
// Set the name of the generated WAR file
tasks.war {
archiveFileName = 'simplelogbackweb.war'
}
tasks.withType(JavaCompile) {
sourceCompatibility = '17'
targetCompatibility = '17'
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- Console Appender Configuration -->
<appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n</pattern>
</encoder>
</appender>
<!-- File Appender Configuration -->
<appender name="File" class="ch.qos.logback.core.FileAppender">
<file>../logs/app.log</file>
<append>true</append>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n</pattern>
</encoder>
</appender>
<!-- Root Logger Configuration -->
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="File"/>
</root>
<!-- Application Specific Logger Configuration -->
<logger name="com.example" level="debug">
<appender-ref ref="Console"/>
<appender-ref ref="File"/>
</logger>
</configuration>
Notice:
Interesting to see what that would be with logback instead of log4j<<<
I guess the interesting thing you want to express is this: logback output log, it will repeat the output twice.