Log4J: стратегии создания экземпляров Logger

Я решил использовать logging framework для нового Java-проекта. Мне интересно, какую стратегию я должен использовать для создания/управления экземплярами Logger и почему?

  • один экземпляр регистратора для каждого класса например.

    class Foo {
        private static final Logger log = Logger.getLogger(Foo.class);
    }
    
  • один экземпляр журнала в потоке
  • один экземпляр регистратора для каждого приложения
  • горизонтальная нарезка: один экземпляр регистратора в каждом слое приложения (например, слой представления, слой контроллера и уровень сохранения)
  • вертикальная нарезка: один экземпляр Logger в функциональных разделах приложения

Примечание. Эта проблема уже в некоторой степени рассматривается в следующих статьях:

Какие накладные расходы на создание Log4j Logger

Ответ 1

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

Что касается приложений или логгеров на основе слоев, проблема в том, что вам нужно найти место для хранения этого объекта Logger. Не очень большая сделка. Большая проблема заключается в том, что некоторые классы могут использоваться на нескольких уровнях из нескольких приложений... может быть трудно получить ваш регистратор правильно. Или, по крайней мере, сложно.

... и последнее, что вы хотите, - это плохие предположения в вашей настройке ведения журнала.

Если вы заботитесь о приложениях и слоях и имеете простые точки разделения, то NDC - это путь. Иногда код может быть немного чрезмерным, но я не знаю, сколько раз я был сохранен точным стеком контекста, показывающим, что Foo.bar() вызывается из приложения X в слое Y.

Ответ 2

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

Создание журналов для каждого класса имеет преимущество включения/выключения регистрации в структуре пакета ваших классов:

log4j.logger.org.apache = INFO
log4j.logger.com.example = DEBUG
log4j.logger.com.example.verbose = ERROR

Вышеупомянутый код для всей библиотеки apache должен быть INFO, перейдите в журнал регистрации с вашего собственного кода на уровень DEBUG, за исключением пакета verbose.

Ответ 3

Я уверен, что это не лучшая практика, но я уволил некоторое время запуска приложений, прежде чем сохранять строки кода. В частности, при вставке:

Logger logger = Logger.getLogger(MyClass.class);

... разработчики часто забывают изменить "MyClass" на текущее имя класса, и несколько регистраторов всегда замыкаются в неправильном месте. Это плохо.

Я иногда писал:

static Logger logger = LogUtil.getInstance(); 

и

class LogUtil {
   public Logger getInstance() {
      String callingClassName = 
         Thread.currentThread().getStackTrace()[2].getClass().getCanonicalName();
      return Logger.getLogger(callingClassName);
   }
}

"2" в этом коде может быть неправильным, но суть есть; (на загрузку класса, как статическая переменная) найдите имя класса, чтобы у разработчика не было способа ошибиться или ввести какую-либо ошибку.

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

Ответ 4

Как было сказано другими, я создал Logger для каждого класса:

private final static Logger LOGGER = Logger.getLogger(Foo.class);

или

private final Logger logger = Logger.getLogger(this.getClass());

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

Самый простой способ сделать это - использовать MDC, но вы можете использовать Logger, созданный для каждого экземпляра класса с именем, включая идентификатор пользователя.

Другим преимуществом использования MDC является использование SL4J, вы можете изменить настройки в зависимости от значений в вашем MDC. Поэтому, если вы хотите зарегистрировать всю активность для определенного пользователя на уровне DEBUG и оставить всех остальных пользователей в ERROR, вы можете. Вы также можете перенаправить различные выходные данные в разные места в зависимости от вашего MDC.

Некоторые полезные ссылки:

http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/MDC.html

http://www.slf4j.org/api/index.html?org/slf4j/MDC.html

Ответ 5

  • Создайте один регистратор для каждого класса.
  • Если у вас есть зависимости, требующие ведения Commons Logging (скорее всего), используйте slf4j bridge для ведения Commons Logging. Выполните активацию ваших регистраторов (для каждого класса) с помощью интерфейса Commons Logging: private static final Log log = LogFactory.getLog(MyClass.class);
  • Проявите этот шаблон в своей среде IDE, используя ярлыки. Для этой цели я использую IDEA живые шаблоны.
  • Предоставить контекстную информацию для потоков с помощью NDC (поток локального стека строк) или MDC (укажите локальную карту строки String →?).

Примеры шаблонов:

private static final Log log = LogFactory.getLog($class$.class); // live template 'log'

if (log.isDebugEnabled())
    log.debug(String.format("$string$", $vars$)); // live template 'ld', 'lw', 'le' ...

Ответ 6

Еще один вариант: вы можете попробовать выполнить AspectJ. Проверьте здесь: Упростите ведение журнала. (Если вы не хотите использовать AOP, вы можете посмотреть slf4j)

//Without AOP

    Class A{
       methodx(){
        logger.info("INFO");
       }
    }

    Class B{
       methody(){
        logger.info("INFO");
       }
    }

//With AOP

    Class A{
       methodx(){
         ......
       }
    }

    Class B{
       methody(){
         ......
       }
    }

    Class LoggingInterceptor{

       //Catched defined method before process
       public void before(...xyz){
         logger.info("INFO" + ...xyz);
       }

       //Catched defined method after processed          
       public void after(...xyz){
         logger.info("INFO" + ...xyz);
       }
       .....

    }

PS: AOP будет лучше, это DRY (не повторяйте себя).

Ответ 7

лучший и простой способ создания пользовательских журналов, не связанных с любым именем класса:

// create logger
Logger customLogger = Logger.getLogger("myCustomLogName");

// create log file, where messages will be sent, 
// you can also use console appender
FileAppender fileAppender = new FileAppender(new PatternLayout(), 
                                             "/home/user/some.log");

// sometimes you can call this if you reuse this logger 
// to avoid useless traces
customLogger.removeAllAppenders();

// tell to logger where to write
customLogger.addAppender(fileAppender);

 // send message (of type :: info, you can use also error, warn, etc)
customLogger.info("Hello! message from custom logger");

теперь, если вам нужен другой журнал в том же классе, не проблема:) просто создайте новый

// create logger
Logger otherCustomLogger = Logger.getLogger("myOtherCustomLogName");

теперь см. код выше и создайте новый fileappender, чтобы ваш вывод был отправлен в другом файле

Это полезно для (по крайней мере) 2 ситуаций

  • когда вам нужна отдельная ошибка из информации и предупреждения

  • когда вы управляете несколькими процессами, и вам нужно выводить из каждого процесса

пс. есть вопросы? не стейсняйся спросить!:)

Ответ 8

Общее соглашение - это "класс logger pr и использует имя класса как его имя". Это хороший совет.

Мой личный опыт заключается в том, что эту переменную регистратора НЕ следует объявлять статической, но переменную экземпляра, которая извлекается для каждого нового. Это позволяет системе ведения журнала обрабатывать два вызова по-разному в зависимости от того, откуда они происходят. Статическая переменная одинакова для ВСЕХ экземпляров этого класса (в этом загрузчике классов).

Кроме того, вы должны изучить все возможности с помощью своего бэкэнда для ведения журнала. У вас могут быть возможности, которых вы не ожидали.

Ответ 9

При развертывании нескольких EAR/WARs лучше всего упаковать log4j.jar выше в иерархии загрузчика классов.
то есть не в WAR или EAR, а в System-classloader вашего контейнера, в противном случае несколько экземпляров Log4J будут записывать в тот же файл, одновременно приводя к странному поведению.

Ответ 10

Если ваше приложение соответствует принципам SOA, для каждой службы A вы будете иметь следующие компоненты:

  • Контроллер
  • Выполнение службы
  • Исполнитель
  • Устойчивость

Таким образом, жизнь облегчает жизнь   aController.log   aService.log   aExecutor.log   и aPersistance.log

Это разделение на основе слоев, поэтому все классы Remoting/REST/SOAP будут записываться в aController.log

Все ваши механизмы планирования, бэкэнд-сервис и т.д. будут писать в aService.log

И все выполнения задач записываются в aExecutor.log и т.д.

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

Таким образом, вы всегда будете иметь 4 файла журнала, которые не так много и не слишком меньше, я рассказываю вам по опыту, что облегчает жизнь.