Я пытаюсь программно настроить Log4J для записи журналов как на консоль, так и в файл журнала. Хотя вывод консоли выглядит нормально, файл журнала остается пустым (но тем не менее создается). Я использую OpenJDK 22 на Zorin 17.1 Core.
Почему это и как это исправить?
Вот минимальный рабочий пример, который напоминает проблему в моем реальном проекте:
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.config.builder.api.AppenderComponentBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
public class Main {
public static void main(String[] args) {
Level level = Level.WARN;
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(level);
builder.setConfigurationName("BuilderTest");
AppenderComponentBuilder appenderBuilder =
builder.newAppender("Stdout", "CONSOLE")
.addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT)
.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "xxxxx %d [%t] %-5level: %msg%n%throwable"))
.add(builder.newFilter("MarkerFilter", Filter.Result.DENY, Filter.Result.NEUTRAL)
.addAttribute("marker", "FLOW"));
builder.add(appenderBuilder);
appenderBuilder =
builder.newAppender("File", "File")
.addAttribute("fileName", "logs/main.log")
.addAttribute("append", true)
.addAttribute("locking", false)
.addAttribute("immediateFlush", true)
.add(builder.newLayout("PatternLayout")
.addAttribute("pattern", "xxxxxx %d [%t] %-5level: %msg%n%throwable"));
builder.add(appenderBuilder);
builder
.add(builder.newLogger("org.apache.logging.log4j", level)
.add(builder.newAppenderRef("Stdout"))
.addAttribute("additivity", false));
builder.add(
builder
.newRootLogger(level)
.add(builder.newAppenderRef("Stdout"))
);
Configurator.reconfigure(builder.build());
LoggerConfig loggerConfig = new LoggerConfig(Main.class.getPackageName(), level, false);
Configurator.setLevel(Main.class.getPackageName(), level);
LogManager.getRootLogger().error("setLoggingLevel(): Logging level set to %s.".formatted(level));
}
}
Вывод консоли:
xxxxx 2024-08-30 01:43:22,830 [main] ERROR: setLoggingLevel(): Logging level set to WARN.
Вот мой pom.xml
:
<?xml version = "1.0" encoding = "UTF-8"?>
<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>
<groupId>com.example.log4j-test</groupId>
<artifactId>log4j-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.3.4</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>3.0.0-beta2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>3.0.0-beta2</version>
</dependency>
</dependencies>
</project>
├── pom.xml
└── src
└── main
└── java
└── com
└── example
└── Main.java
<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.log4j-test</groupId>
<artifactId>log4j-test</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<!--
<log4j2.version>2.19.0</log4j2.version>
-->
<log4j2.version>3.0.0-beta2</log4j2.version>
</properties>
<dependencies>
<!-- Log4j2 core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2.version}</version>
</dependency>
<!-- Log4j2 API -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j2.version}</version>
</dependency>
</dependencies>
</project>
package com.example;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.config.Configurator;
import org.apache.logging.log4j.core.config.builder.api.*;
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
public class Main {
//private static final Logger logger = LogManager.getLogger(Main.class);
//private static final Logger appLoggerInstance = LogManager.getLogger("com.example");
public static void main(String[] args) {
// Create a ConfigurationBuilder instance
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
builder.setStatusLevel(Level.WARN);
// Create a pattern layout
String pattern1 = "XXXX %d [%t] %-5level: %msg%n%throwable";
String pattern2 = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n";
LayoutComponentBuilder layout = builder.newLayout("PatternLayout")
.addAttribute("pattern", pattern1 );
// Create Console Appender
AppenderComponentBuilder consoleAppender = builder.newAppender("Stdout", "Console")
.addAttribute("target", "SYSTEM_OUT")
.add(layout);
// Create File Appender
AppenderComponentBuilder fileAppender = builder.newAppender("File", "File")
.addAttribute("fileName", "logs/main.log")
.addAttribute("append", true)
.addAttribute("locking", false)
.addAttribute("immediateFlush", true)
.add(layout);
// Add appenders to the configuration
builder.add(consoleAppender);
builder.add(fileAppender);
// Root Logger configuration
RootLoggerComponentBuilder rootLogger = builder.newRootLogger("info");
rootLogger.add(builder.newAppenderRef("Stdout"));
rootLogger.add(builder.newAppenderRef("File"));
builder.add(rootLogger);
// Application-specific Logger configuration
LoggerComponentBuilder appLogger = builder.newLogger("com.example", "debug")
.add(builder.newAppenderRef("Stdout"))
.add(builder.newAppenderRef("File"))
.addAttribute("additivity", false);
// Add logger to the configuration
builder.add(appLogger);
// Apply the configuration
Configurator.initialize(builder.build());
// Test logging
Logger logger = LogManager.getLogger(Main.class);
logger.info("This is an info message from the root logger.");
logger.debug( "This is a debug message from the root logger.");
logger.error( "This is an error message from the root logger.");
Logger appLoggerInstance = LogManager.getLogger("com.example");
appLoggerInstance.debug("ZZZ This is a debug message from com.example logger.");
LogManager.getRootLogger().error("setLoggingLevel(): Logging level set to %s.".formatted(Level.WARN));
}
}
Программа не использует log4j2.xml, но наш программный код может использовать log4j2.xml в качестве базового преобразования преобразования.
<?xml version = "1.0" encoding = "UTF-8"?>
<Configuration status = "WARN">
<Appenders>
<!-- Console Appender Configuration -->
<Console name = "Stdout" 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-log4j-xml.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 = "Stdout"/>
<AppenderRef ref = "File"/>
</Root>
<!-- Application Specific Logger Configuration -->
<Logger name = "com.example" level = "debug" additivity = "false">
<AppenderRef ref = "Stdout"/>
<AppenderRef ref = "File"/>
</Logger>
</Loggers>
</Configuration>
mvn clean package
mvn dependency:copy-dependencies -DoutputDirectory=target/libs
java -cp "target/libs/*:target/log4j-test-1.0-SNAPSHOT.jar" com.example.Main
Результат:
XXXX 2024-08-30 09:20:01,980 [main] INFO : This is an info message from the root logger.
XXXX 2024-08-30 09:20:02,002 [main] DEBUG: This is a debug message from the root logger.
XXXX 2024-08-30 09:20:02,002 [main] ERROR: This is an error message from the root logger.
XXXX 2024-08-30 09:20:02,003 [main] DEBUG: ZZZ This is a debug message from com.example logger.
XXXX 2024-08-30 09:20:02,003 [main] ERROR: setLoggingLevel(): Logging level set to WARN.
<Configuration status = "WARN">
builder.setStatusLevel(Level.WARN);
<PatternLayout pattern = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
// Create a pattern layout
String pattern1 = "XXXX %d [%t] %-5level: %msg%n%throwable";
String pattern2 = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n";
LayoutComponentBuilder layout = builder.newLayout("PatternLayout")
.addAttribute("pattern", pattern1 );
<!-- Console Appender Configuration -->
<Console name = "Stdout" target = "SYSTEM_OUT">
<PatternLayout pattern = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
</Console>
// Create Console Appender
AppenderComponentBuilder consoleAppender = builder.newAppender("Stdout", "Console")
.addAttribute("target", "SYSTEM_OUT")
.add(layout);
Код несовместим с XML. Настройте код так, чтобы он соответствовал программе, описанной в проблеме.
<!-- File Appender Configuration -->
<File name = "File" fileName = "logs/app-log4j-xml.log" append = "true">
<PatternLayout pattern = "%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n"/>
</File>
// Create File Appender
AppenderComponentBuilder fileAppender = builder.newAppender("File", "File")
.addAttribute("fileName", "logs/main.log")
.addAttribute("append", true)
.addAttribute("locking", false)
.addAttribute("immediateFlush", true)
.add(layout);
// Add appenders to the configuration
builder.add(consoleAppender);
builder.add(fileAppender);
<!-- Root Logger Configuration -->
<Root level = "info">
<AppenderRef ref = "Stdout"/>
<AppenderRef ref = "File"/>
</Root>
// Root Logger configuration
RootLoggerComponentBuilder rootLogger = builder.newRootLogger("info");
rootLogger.add(builder.newAppenderRef("Stdout"));
rootLogger.add(builder.newAppenderRef("File"));
builder.add(rootLogger);
<!-- Application Specific Logger Configuration -->
<Logger name = "com.example" level = "debug" additivity = "false">
<AppenderRef ref = "Stdout"/>
<AppenderRef ref = "File"/>
</Logger>
// Application-specific Logger configuration
LoggerComponentBuilder appLogger = builder.newLogger("com.example", "debug")
.add(builder.newAppenderRef("Stdout"))
.add(builder.newAppenderRef("File"))
.addAttribute("additivity", false);
// Add logger to the configuration
builder.add(appLogger);
(1) Main.java
— это полноценная программа, которая уже содержит AppenderRef и File Appender. (2) В разделе Console Appender
вы можете обнаружить, что Appenders
уже содержит сопоставление File Appender.
Код, в котором нет всех AppenderRef
, — это код, о котором идет речь, а не ваш. Возможно, было бы полезно это отметить. Вот почему Pixelcode ничего не видит в своих файлах журналов.
Вы правы, спасибо, что отметили это.
Большое спасибо! Log4J действительно кажется слишком сложным для правильной настройки:/
Хороший ответ, хотя, вероятно, было бы полезно краткое изложение. То, что ОП забыл добавить в свой код, это элементы
AppenderRef
для приложения «Файл».