Невозможно записать в разные журналы для разных потоков, используя log4j2

Я пытаюсь записывать журналы в разные файлы для каждой группы потоков/потоков. Мой код создает разные файлы для каждого потока, но записывает журналы всех остальных потоков в один файл.

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

Я ожидаю несколько файлов журнала для каждого потока/группы потоков. Но у меня получилось несколько файлов журналов для каждого потока, и все журналы потоков в один файл.

Заранее спасибо.

Вы не хотите этого делать. Если вы отслеживаете ошибку, у вас будет кошмар, когда вы пытаетесь восстановить реальную последовательность событий. Легче отделить информацию для каждого потока из одного файла журнала, чем реконструировать последовательность выполнения из нескольких файлов журнала для каждого потока.

user207421 08.05.2022 07:38
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
1
61
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Каждый из ваших потоков вызывает LoggerContext#setConfigLocation, что вызывает реконфигурацию Log4j2. В перенастройке удаляет все добавленные вами аппендеры програмно. Вот почему вы в конечном итоге с одним приложением.

Обратите внимание, что вам не нужна программная конфигурация, чтобы иметь разные файлы для каждого потока. Вы можете просто использовать RoutingAppender как в этот вопрос.

Редактировать: Если вы действительно хотите использовать программную конфигурацию (что не рекомендуется по соображениям совместимости), вам также необходимо использовать уникальное имя для каждого приложения, иначе они будут молча игнорироваться (метод Logger#addAppender, который вы используете, не является частью общедоступного API). , ср. javadoc, поэтому об ошибках не сообщается).

Спасибо за предложение RoutingAppender. Но мне нужна программная настройка. Как вы предложили, я удалил вызов функции LoggerContext#setConfigLocation внутри потока и сохранил его в основном классе. но все же я сталкиваюсь с той же проблемой.

Manoj Kare 09.05.2022 05:25

Спасибо, это сработало. после этих двух предложений. 1. не используйте вызов функции LoggerContext#setConfigLocation внутри потока. 2. использовать уникальное имя для каждого приложения.

Manoj Kare 10.05.2022 08:19

переместите этот фрагмент кода из 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);
}

Спасибо за решение Поитр П. Карваш

Другие вопросы по теме