Я пытаюсь записывать журналы в разные файлы для каждой группы потоков/потоков. Мой код создает разные файлы для каждого потока, но записывает журналы всех остальных потоков в один файл.
MainTest.java
public class MainTest {
public static void main(String[] args) {
ThreadGroup tg1 = new ThreadGroup("Group A");
Thread t1 = new Thread(tg1, new LoggingTest(), "t1");
t1.start();
Thread t2 = new Thread(tg1, new LoggingTest(), "t2");
t2.start();
//
Thread t3 = new Thread(new LoggingTest(),"t3");
t3.start();
}
}
LoggingTest.java
public class LoggingTest implements Runnable {
private static final Logger logger = LogManager.getLogger(LoggingTest.class);
private static final String logFilePath = "/output/logs/";
private static final String pattern = "%d{dd MMM yyyy HH:mm:ss.SSS} (%F:%L) - %m%n";
public void run() {
Thread runner = Thread.currentThread();
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
try {
URL configFile = LoggingTest.class.getResource("/log4j2.properties");
context.setConfigLocation(configFile.toURI());
}catch(Exception e) {
e.printStackTrace();
}
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
FileAppender appender = null;
try {
appender = FileAppender
.newBuilder()
.setName("Name")
.setLayout(PatternLayout.newBuilder().withPattern(pattern).build())
.withFileName(logFilePath + runner.getName() + ".log")
.build();
} catch (Exception e) {
logger.error("error -> "+e);
}
if (appender != null) {
if ( runner.getThreadGroup().getParent() != null )
{
appender.addFilter( new ThreadGroupFilter( runner.getThreadGroup() ) );
}
else
{
appender.addFilter( new ThreadFilter( runner ) );
}
appender.start();
ctx.getRootLogger().addAppender(appender);
ctx.updateLoggers();
}
logger.info("info msg");
logger.debug("debug msg");
logger.error("error msg");
if ( appender != null ) {
ctx.getRootLogger().removeAppender(appender);
ctx.updateLoggers();
appender.stop();
}
}
}
Здесь я создал пару классов фильтров для Thread и ThreadGroup. Но похоже, что они не работают должным образом. ThreadGroupFilter.java
import org.apache.logging.log4j.core.Filter;
public class ThreadGroupFilter implements Filter {
private final ThreadGroup filterThreadGroup;
public ThreadGroupFilter( ThreadGroup filterThreadGroup ) {
this.filterThreadGroup = filterThreadGroup;
}
@Override
public Result filter(LogEvent event) {
if ( Thread.currentThread().getThreadGroup() != filterThreadGroup )
return Filter.Result.DENY;
return Filter.Result.NEUTRAL;
}
}
ThreadFilter.java
public class ThreadFilter implements Filter {
private final Thread filterThread;
public ThreadFilter( Thread filterThread ) {
this.filterThread = filterThread;
}
@Override
public Result filter(LogEvent event) {
if ( Thread.currentThread() != filterThread )
return Filter.Result.DENY;
return Filter.Result.NEUTRAL;
}
}
log4j2.properties
name=PropertiesConfig
property.basePath = ../logs
appenders = console
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %5p [%c] [%t] (%F:%L) - %m%n
rootLogger.level = debug
rootLogger.appenderRefs = console
rootLogger.appenderRef.stdout.ref = consoleLogger
Я ожидаю несколько файлов журнала для каждого потока/группы потоков. Но у меня получилось несколько файлов журналов для каждого потока, и все журналы потоков в один файл.
Заранее спасибо.
Каждый из ваших потоков вызывает LoggerContext#setConfigLocation
, что вызывает реконфигурацию Log4j2. В перенастройке удаляет все добавленные вами аппендеры програмно. Вот почему вы в конечном итоге с одним приложением.
Обратите внимание, что вам не нужна программная конфигурация, чтобы иметь разные файлы для каждого потока. Вы можете просто использовать RoutingAppender
как в этот вопрос.
Редактировать: Если вы действительно хотите использовать программную конфигурацию (что не рекомендуется по соображениям совместимости), вам также необходимо использовать уникальное имя для каждого приложения, иначе они будут молча игнорироваться (метод Logger#addAppender
, который вы используете, не является частью общедоступного API). , ср. javadoc, поэтому об ошибках не сообщается).
Спасибо за предложение RoutingAppender
. Но мне нужна программная настройка. Как вы предложили, я удалил вызов функции LoggerContext#setConfigLocation внутри потока и сохранил его в основном классе. но все же я сталкиваюсь с той же проблемой.
Спасибо, это сработало. после этих двух предложений. 1. не используйте вызов функции LoggerContext#setConfigLocation внутри потока. 2. использовать уникальное имя для каждого приложения.
переместите этот фрагмент кода из LoggingTest в MainTest
LoggerContext context = (org.apache.logging.log4j.core.LoggerContext) LogManager.getContext(false);
try {
URL configFile = LoggingTest.class.getResource("/log4j2.properties");
context.setConfigLocation(configFile.toURI());
}catch(Exception e) {
e.printStackTrace();
}
И небольшие изменения в конфигурации Appender.
FileAppender appender = null;
String appenderName = (runner.getThreadGroup() != null) ? "appender_"+runner.getThreadGroup().getName() : "appender_"+runner.getName();
try {
appender = FileAppender
.newBuilder()
.setName(appenderName)
.setLayout(PatternLayout.newBuilder().withPattern(pattern).build())
.withFileName(logFilePath + appenderName + ".log")
.build();
} catch (Exception e) {
logger.error("error -> "+e);
}
Спасибо за решение Поитр П. Карваш
Вы не хотите этого делать. Если вы отслеживаете ошибку, у вас будет кошмар, когда вы пытаетесь восстановить реальную последовательность событий. Легче отделить информацию для каждого потока из одного файла журнала, чем реконструировать последовательность выполнения из нескольких файлов журнала для каждого потока.