Конфигурирование Log4j Loggers программно

191

Я пытаюсь использовать SLF4J (с log4jпривязкой) в первый раз.

Я хотел бы настроить 3 разных именованных регистратора, которые могут быть возвращены LoggerFactory, который будет регистрировать разные уровни и отправлять сообщения разным приложениям:

  • Logger 1 "FileLogger" регистрирует отладку и добавляет к DailyRollingFileAppender
  • Logger 2 "TracingLogger" регистрирует TRACE + и добавляет к JmsAppender
  • Logger 3 «ErrorLogger» регистрирует ОШИБКУ + и добавляет к другому JmsAppender

Кроме того, я хочу, чтобы они были настроены программно (в Java, в отличие от XML или log4j.propertiesфайла).

Я предполагаю, что обычно я определяю их Loggerгде-то в некотором загрузочном коде, например, в init()методе. Однако из-за того, что я хочу использовать slf4j-log4j, я не понимаю, где я могу определить регистраторы и сделать их доступными для пути к классам.

Я не верю, что это является нарушением основной цели SLF4J (как фасад), потому что мой код, использующий API SLF4J, никогда не узнает о существовании этих регистраторов. Мой код просто выполняет обычные вызовы API SLF4J, который затем перенаправляет их в Log4j Loggers, которые он находит на пути к классам.

Но как мне настроить эти log4j Logger на пути к классам ... в Java ?!

IAmYourFaja
источник
3
Для log4j 1.x используйте принятый ответ ниже для 2.x, см. Logging.apache.org/log4j/2.x/manual/customconfig.html
earcam

Ответы:

279

Вы можете добавить / удалить Appender программно в Log4j:

  ConsoleAppender console = new ConsoleAppender(); //create appender
  //configure the appender
  String PATTERN = "%d [%p|%c|%C{1}] %m%n";
  console.setLayout(new PatternLayout(PATTERN)); 
  console.setThreshold(Level.FATAL);
  console.activateOptions();
  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(console);

  FileAppender fa = new FileAppender();
  fa.setName("FileLogger");
  fa.setFile("mylog.log");
  fa.setLayout(new PatternLayout("%d %-5p [%c{1}] %m%n"));
  fa.setThreshold(Level.DEBUG);
  fa.setAppend(true);
  fa.activateOptions();

  //add appender to any Logger (here is root)
  Logger.getRootLogger().addAppender(fa);
  //repeat with all other desired appenders

Я бы посоветовал вам поместить его в init () где-нибудь, где вы уверены, что это будет выполнено раньше всего. Затем вы можете удалить все существующие приложения в корневом логгере с помощью

 Logger.getRootLogger().getLoggerRepository().resetConfiguration();

и начните с добавления своего. Вам нужно log4j в classpath, конечно, чтобы это работало.

Замечание:
Вы можете взять все, что Logger.getLogger(...)захотите, чтобы добавить дополнения. Я просто взял root logger, потому что он лежит в основе всех вещей и будет обрабатывать все, что передается через другие appenders в других категориях (если не настроено иначе путем установки флага аддитивности).

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

  Logger fizz = LoggerFactory.getLogger("com.fizz")

выдаст вам логгер для категории "com.fizz".
Для приведенного выше примера это означает, что все, что было зарегистрировано с ним, будет передано в консоль и файл appender в корневом логгере.
Если вы добавляете appender в Logger.getLogger ("com.fizz"). AddAppender (newAppender), то вход в систему fizzбудет обрабатываться всеми приложениями из корневого регистратора и newAppender.
Вы не создаете регистраторы с конфигурацией, вы просто предоставляете обработчики для всех возможных категорий в вашей системе.

Oers
источник
2
Спасибо, oers! Быстрый вопрос - я заметил, что вы добавляете appenders в корневой Logger. Для этого есть причина?
IAmYourFaja
И, что более важно, мне нужно будет указать, какой Logger получить из SLF4J LoggerFactory. Можно ли попросить SLF4J для корневого регистратора log4j?
IAmYourFaja
3
@AdamTannon Вы можете взять любой Logger.getLogger (...), который вам нравится. Я просто взял root logger, потому что он лежит в основе всех вещей и будет обрабатывать все, что передается через другие приложения в других категориях (если не указано иное). Смотрите иерархию
логгеров
@AdamTannon вы не можете использовать фабрику sl4j для получения корневого логгера log4j. SL4j - это лесозаготовительный фасад. Вы не получите ничего конкретного от log4j.
Декабрь
2
oers - я ценю ваши замечательные отзывы, но я просто не соединяю здесь все точки. Можете ли вы изменить свой пример, чтобы показать добавление нового регистратора (не корневого регистратора), который после добавления в систему будет доступен любому другому классу, который его запрашивает? Например, регистратор, к которому обычно обращаются, скажем, Logger fizz = LoggerFactory.getLogger("com.fizz");спасибо!
IAmYourFaja
47

Похоже, вы пытаетесь использовать log4j с «обоих концов» (конечного пользователя и конечного пункта конфигурации).

Если вы хотите закодировать в API-интерфейсе slf4j, но заранее определить (и программно) конфигурацию Log4j Logger, которую вернет classpath, вам абсолютно необходимо иметь какую-то адаптацию регистрации, которая использует ленивую конструкцию.

public class YourLoggingWrapper {
    private static boolean loggingIsInitialized = false;

    public YourLoggingWrapper() {
        // ...blah
    }

    public static void debug(String debugMsg) {
        log(LogLevel.Debug, debugMsg);
    }

    // Same for all other log levels your want to handle.
    // You mentioned TRACE and ERROR.

    private static void log(LogLevel level, String logMsg) {
        if(!loggingIsInitialized)
            initLogging();

        org.slf4j.Logger slf4jLogger = org.slf4j.LoggerFactory.getLogger("DebugLogger");

        switch(level) {
        case: Debug:
            logger.debug(logMsg);
            break;
        default:
            // whatever
        }
    }

    // log4j logging is lazily constructed; it gets initialized
    // the first time the invoking app calls a log method
    private static void initLogging() {
        loggingIsInitialized = true;

        org.apache.log4j.Logger debugLogger = org.apache.log4j.LoggerFactory.getLogger("DebugLogger");

        // Now all the same configuration code that @oers suggested applies...
        // configure the logger, configure and add its appenders, etc.
        debugLogger.addAppender(someConfiguredFileAppender);
    }

При таком подходе вам не нужно беспокоиться о том, где и когда ваши регистраторы log4j будут настроены. В первый раз, когда classpath запрашивает их, они лениво создаются, передаются обратно и становятся доступными через slf4j. Надеюсь, это помогло!

IAmYourFaja
источник
2
Успешно справился! Большое спасибо за полезный пример! @Oers - спасибо за попытку направить меня в правильном направлении - я собираюсь дать вам зеленый чек на вашу преданность, но я должен дать Жарвею награду, потому что это было именно то, что я искал. Еще раз спасибо всем!
IAmYourFaja
4

Если вы определили appender в свойствах log4j и хотите обновить его программно, задайте имя в свойствах log4j и получите его по имени.

Вот пример записи в log4j.properties:

log4j.appender.stdout.Name=console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.Threshold=INFO

Чтобы обновить его, сделайте следующее:

((ConsoleAppender) Logger.getRootLogger().getAppender("console")).setThreshold(Level.DEBUG);
Кайл Шрейдер
источник
1

Если кто-то ищет программную настройку log4j2 на Java, эта ссылка может помочь: ( https://www.studytonight.com/post/log4j2-programmatic-configuration-in-java-class )

Вот основной код для настройки Console Appender:

ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();

builder.setStatusLevel(Level.DEBUG);
// naming the logger configuration
builder.setConfigurationName("DefaultLogger");

// create a console appender
AppenderComponentBuilder appenderBuilder = builder.newAppender("Console", "CONSOLE")
                .addAttribute("target", ConsoleAppender.Target.SYSTEM_OUT);
// add a layout like pattern, json etc
appenderBuilder.add(builder.newLayout("PatternLayout")
                .addAttribute("pattern", "%d %p %c [%t] %m%n"));
RootLoggerComponentBuilder rootLogger = builder.newRootLogger(Level.DEBUG);
rootLogger.add(builder.newAppenderRef("Console"));

builder.add(appenderBuilder);
builder.add(rootLogger);
Configurator.reconfigure(builder.build());

Это перенастроит rootLogger по умолчанию, а также создаст новый appender .

iamabhishek
источник