Log4j2: динамическое создание файлов журнала для нескольких журналов.

В настоящее время я создаю систему, которая может иметь модули (думайте о них как о плагинах), где каждый из них может иметь свой собственный выделенный журнал.

Я хотел бы использовать проект log4j2 для ведения журнала, но, похоже, у меня возникли проблемы с файловыми приложениями.

Основной проект (загрузчик модулей и «ядро» всего этого) должен иметь свой собственный файл журнала, а модули должны иметь свои собственные (например, mod_XXXXXXXX.log).

Прочитав документацию об аппендерах, я обнаружил класс FileAppender и собирался его использовать. Пока я не узнал, что я не могу просто добавить приложение к регистратору по умолчанию, созданному LogManager.getLog().

Регистратор, возвращаемый LogManager, отличается от регистратора интерфейса Logger.

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

Спасибо за чтение; даже малейшая подсказка приветствуется :)


person spaceemotion    schedule 27.09.2013    source источник


Ответы (3)


если вам действительно нужно определить файл журнала динамически, взгляните на Log4J2 RoutingAppender. Более длинный пример находится в разделе FAQ. представлять интерес: Шаблон подстановки для RoutingAppender Log4j2 и Как писать разные журналы в разных файлах с помощью log4j2 ( MDC в xml)?

Обратите внимание, что вам нужно установить значения в карте ThreadContext, которую использует RoutingAppender, чтобы решить, к какому аппендеру направить событие журнала. Это означает, что вам нужно будет помещать некоторое значение в карту ThreadContext каждый раз, когда ваш код входит в другой плагин.

Однако вам действительно нужно, чтобы он был таким динамичным? Если вы заранее знаете, какие плагины у вас есть, вы можете просто объявить логгер для каждого плагина (обычно это делается с помощью имени пакета плагина) и сопоставить каждый такой логгер с отдельным приложением.

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" name="MyApp" packages="">
  <Appenders>
    <File name="MyFile" fileName="logs/app.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <File name="plugin1" fileName="logs/plugin1.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
    <File name="plugin2" fileName="logs/plugin2.log">
      <PatternLayout>
        <Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
      </PatternLayout>
    </File>
  </Appenders>
  <Loggers>
    <Logger name="com.mycomp.project.plugin1" level="debug">
      <AppenderRef ref="plugin1" level="debug" />
    </Logger>
    <Logger name="com.mycomp.project.plugin2" level="debug">
      <AppenderRef ref="plugin2" level="debug" />
    </Logger>
    <Root level="trace">
      <AppenderRef ref="MyFile" level="trace" />
    </Root>
  </Loggers>
</Configuration>
person Remko Popma    schedule 28.09.2013
comment
Обязательно ли указывать имена пакетов в имени атрибута для логгера. Что делать, если классы находятся в разных пакетах. Нужна помощь в этом, так как мне нужен журнал потока. stackoverflow.com/questions/43586574/ - person Tushar Banne; 04.05.2017
comment
Не обязательно, но многие люди делают это, потому что это позволяет настроить уровень журнала для всего в пакете, что может быть полезно. - person Remko Popma; 04.05.2017
comment
добавлено additivity=false для регистратора plugin1 и plugin2, но журналы записываются в app.log. Что мне здесь не хватает? - person Tushar Banne; 04.05.2017
comment
Нашел ошибку. ПРИМЕЧАНИЕ. Я заменил имя пакета на plug1 и plug2 соответственно. Делал Logger adminLog = LogManager.getLogger(plug1+Example.class); Вместо Logger adminLog = LogManager.getLogger(plug1); Теперь нужно разобраться, как печатать имена классов в логах. Любая помощь приветствуется. - person Tushar Banne; 04.05.2017

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

http://logging.apache.org/log4j/2.x/manual/extending.html

Что бы это ни стоило, я раньше занимался большими системами на основе плагинов (используя OSGi), и мы, честно говоря, не пошли по этому пути. Обычно проще найти интересующий вас класс или пакет из одного файла журнала.

person babernathy    schedule 27.09.2013

Несмотря на то, что ответ Ремко Попмы может быть наиболее эффективным способом ведения журнала, я создал небольшой класс, который может самостоятельно создавать файлы журнала.

Я думаю, что буду использовать решение принятого ответа, поэтому вот код, который я написал для работы с файлом XML:

import gnu.trove.map.hash.THashMap;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.FileAppender;
import org.apache.logging.log4j.core.async.AsyncLoggerContext;
import org.apache.logging.log4j.core.layout.PatternLayout;
import org.apache.logging.log4j.message.FormattedMessageFactory;
import org.apache.logging.log4j.message.MessageFactory;

import java.io.File;
import java.io.IOException;
import java.util.Map;

/**
 * Represents a manager for custom log files stored inside a log folder.
 */
public class LoggingManager {
    /** The default log file extension */
    public static final String FILE_EXTENSION = "log";

    /** The global context used for all loggers */
    private final LoggerContext context;

    /** The global message factory used for all loggers */
    private final MessageFactory msgFactory;

    /** A map of all created logs */
    private final Map<String, Logger> logCache;

    /** The folder containing the log files */
    private final File logFolder;


    public LoggingManager(String name, File logFolder) throws IOException {
        this.logFolder = logFolder;

        if(!logFolder.exists()) {
            if(!logFolder.mkdirs()) {
                throw new IOException("Could not create log folder");
            }
        }

        this.logCache = new THashMap<String, Logger>();

        // Create logger context
        this.context = new AsyncLoggerContext(name);

        // Create formatted message factory
        this.msgFactory = new FormattedMessageFactory();
    }

    public Logger getLogger(String name) {
        Logger logger = logCache.get(name);

        // Create a new one
        if(logger == null) {
            logger = new SimpleLogger(name);

            FileAppender appender = FileAppender.createAppender(
                    new File(logFolder, name + "." + FILE_EXTENSION).getAbsolutePath(),
                    "true",
                    "false",
                    "file_appender-" + name,
                    "true",
                    "false",
                    "true",
                    PatternLayout.createLayout(PatternLayout.SIMPLE_CONVERSION_PATTERN, null, null, "UTF-8", "true"),
                    null,
                    "false",
                    null,
                    null
            );

            appender.start();
            logger.getContext().getConfiguration().getLoggerConfig("root").addAppender(appender, Level.ALL, null);

            // Add to log cache
            logCache.put(name, logger);
        }

        // Return the logger
        return logger;
    }

    private class SimpleLogger extends Logger {

        public SimpleLogger(String name) {
            super(context, name, msgFactory);

            // Set to all levels
            this.setLevel(Level.ALL);
        }

    }

}

Если вы не используете trove, вы можете заменить его обычным java HashMap, если хотите.

person spaceemotion    schedule 30.09.2013
comment
Отвечает ли это на вопрос ›? динамическое создание файлов журнала? - person Caffeinated; 27.06.2015