'Logback: Use main class name as the log file name even when run from Maven
I have some Java classes that are executed as a batch process. I am using Logback and I want to use FQCN of the main class in the log file name. For example, I want to write a log from the main class com.example.demo.DemoApplication to application-com.example.demo.DemoApplication.log.
I want a portable way that runs correctly in the following three environments:
- Run application with ordinaly
javacommand likejava -classpath "./target/classes;./lib/*" com.example.demo.DemoApplication - Run application as a Spring executable jar like
java -jar .\demo-0.0.1.jar - Run application from Maven like
mvn test
I have referred questions like Logback: use Java main class name as log filename or Portable way to find name of main class from Java code and tried to use PropertyDefiner in the following three ways. But all of these are not fully portable (go wrong in some environments).
1: Specify it explicitly in the class
I made the following PropertyDefiner:
package com.example.demo;
import ch.qos.logback.core.PropertyDefinerBase;
public class MainClassPropertyDefinerWithStaticVariable extends PropertyDefinerBase {
private static String mainClassName;
public static void setMainClassName(String className) {
mainClassName = className;
}
@Override
public String getPropertyValue() {
return mainClassName;
}
}
and set the name of the "main" class in main method:
public static void main(String[] args) {
MainClassPropertyDefinerWithStaticVariable.setMainClassName(DemoApplication.class.getName());
Logger logger = LoggerFactory.getLogger(DemoApplication.class);
logger.info("hello world");
SpringApplication.run(DemoApplication.class, args);
}
This works collecrly when I run the application with java -classpath "./target/classes;./lib/*" com.example.demo.DemoApplication, or run as a Spring executable jar java -jar .\demo-0.0.1.jar.
On the other hand, when I run this class from mvn test, the log file was named application-mainClassName_IS_UNDEFINED.log.
2: See the call stack
I made the following PropertyDefiner according to https://stackoverflow.com/a/8275751/3902663 :
package com.example.demo;
import ch.qos.logback.core.PropertyDefinerBase;
import java.util.Map.Entry;
public class MainClassPropertyDefinerWithStackTrace extends PropertyDefinerBase {
@Override
public String getPropertyValue() {
for (Entry<Thread, StackTraceElement[]> entry : Thread.getAllStackTraces().entrySet()) {
Thread thread = entry.getKey();
if (thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals("main")) {
for (StackTraceElement stackTraceElement : entry.getValue()) {
if (stackTraceElement.getMethodName().equals("main")) {
try {
Class<?> c = Class.forName(stackTraceElement.getClassName());
Class[] argTypes = new Class[] { String[].class };
c.getDeclaredMethod("main", argTypes);
return stackTraceElement.getClassName();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}
This works collecrly when I run the application with java -classpath "./target/classes;./lib/*" com.example.demo.DemoApplication, or run as a Spring executable jar java -jar .\demo-0.0.1.jar.
When you run the application from mvn test, the log file was named application-org.apache.maven.surefire.booter.ForkedBooter.log.
3: See the system property
I made the following PropertyDefiner:
package com.example.demo;
import ch.qos.logback.core.PropertyDefinerBase;
public class MainClassPropertyDefinerWithSunJavaCommand extends PropertyDefinerBase {
@Override
public String getPropertyValue() {
return System.getProperty("sun.java.command").split("\\s")[0];
}
}
This works collecrly when I run the application with java -classpath "./target/classes;./lib/*" com.example.demo.DemoApplication.
When you run the application from a Spring executable jar or mvn test, the creation of the log file was failed because the system property sun.java.command contains the path of the jar file.
4: Using SiftingAppender
Define SiftingAppender in logback.xml:
<configuration>
<appender name="siftingAppender" class="ch.qos.logback.classic.sift.SiftingAppender">
<discriminator>
<key>FQCN</key>
<defaultValue>UNKNOWN</defaultValue>
</discriminator>
<sift>
<appender name="${FQCN}" class="ch.qos.logback.core.FileAppender">
<file>application-${FQCN}.log</file>
<encoder>
<charset>UTF-8</charset>
<pattern>%m%n</pattern>
</encoder>
</appender>
</sift>
</appender>
<root level="DEBUG">
<appender-ref ref="siftingAppender" />
</root>
</configuration>
and set the name of the "main" class to the MDC in the main method:
public static void main(String[] args) {
MDC.put("FQCN", DemoApplication.class.getName());
Logger logger = LoggerFactory.getLogger(DemoApplication.class);
logger.info("hello world");
SpringApplication.run(DemoApplication.class, args);
}
When I run this class from java -classpath "./target/classes;./lib/*" com.example.demo.DemoApplication or run as a Spring executable jar java -jar .\demo-0.0.1.jar, two log files application-com.example.demo.DemoApplication.log and application-UNKNOWN.log are made. application-UNKNOWN.log contains the following line:
Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@525575, started on Thu Feb 17 23:34:00 JST 2022
When I run this class from mvn test, the log file was named application-UNKNOWN.log.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
