Должен ли регистратор быть частным статическим или нет

103

Должен ли регистратор быть объявлен статическим или нет? Обычно я видел два типа объявления регистратора:

    защищенный журнал журнала = новый Log4JLogger (aClass.class);

или

    частный статический журнал журнала = новый Log4JLogger (aClass.class);

Какой из них использовать? каковы плюсы и минусы обоих?

Драхакар
источник
1
Лесозаготовка - это сквозная проблема. Используйте аспекты, и вопрос спорный.
Дэйв Джарвис,
4
static- одна ссылка на класс. нестатический - одна ссылка на экземпляр (+ инициализация). Так что в некоторых случаях последнее оказывает значительное влияние на память, если у вас множество экземпляров. Никогда не используйте нестатические предметы в часто посещаемых объектах. Я всегда использую статическую версию. (который должен быть в верхнем регистре LOG )
ВЫЙТИ - Anony-Mousse
2
как уже предлагалось, используйте АОП и аннотации, например: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256 05
1
RobertHume статическая версия будет с помощью константы. Именно поэтому он должен быть в верхнем регистре.
ВЫЙТИ - Anony-Mousse
2
Нет, это должно быть private static final Log logстрочные буквы. Регистратор не является константой, регистратор - это статический конечный объект (который может быть изменен). Лично я всегда пользуюсь logger.
osundblad 04

Ответы:

99

Преимущество нестатической формы состоит в том, что вы можете объявить ее в (абстрактном) базовом классе, как показано ниже, не беспокоясь о том, что будет использовано правильное имя класса:

protected Log log = new Log4JLogger(getClass());

Однако его недостаток, очевидно, заключается в том, что для каждого экземпляра класса будет создаваться совершенно новый экземпляр регистратора. Само по себе это может быть не дорого, но добавляет значительные накладные расходы. Если вы не хотите этого, используйте staticвместо этого форму. Но его недостаток, в свою очередь, состоит в том, что вы должны объявлять его в каждом отдельном классе и заботиться в каждом классе о том, чтобы правильное имя класса использовалось во время создания регистратора, поскольку getClass()не может использоваться в статическом контексте. Однако в средней среде IDE для этого можно создать шаблон автозаполнения. Например logger+ctrl+space .

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

protected Log log = LogManager.getLogger(getClass());
BalusC
источник
6
Объявите abstract Log getLogger();в абстрактном классе. Реализуйте этот метод, вернув статический регистратор для конкретного экземпляра. Добавьте private final static Log LOG = LogManager.getLogger(Clazz.class);в свой шаблон класса IDE.
ВЫЙТИ - Anony-Mousse
2
Для slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Маркус Пшайдт
3
@BalusC проблема с передачей getClass () методу getLogger заключается в том, что он возвращает класс текущего экземпляра. Обычно желательно, чтобы ведение журнала было связано с классом, в котором находится код. Например, если код ведения журнала находится в классе Parent, тогда мы хотим, чтобы журнал был связан с Parent, даже если исполняемый экземпляр является экземпляром класса Child, который является подклассом Parent. С getClass () он будет неправильно связан с Child
inor
@inor: "неправильно"? Если вы не хотите абстрагироваться от класса, вам просто не следует использовать унаследованный getClass () в первую очередь. Есть разработчики, которые считают его правильным и полезным, поскольку он раскрывает информацию, в каком именно подклассе была выполнена логика.
BalusC
2
@ BalusC getLogger (getClass ()) всегда приводит к неправильной регистрации имени подкласса. Классы ведения журнала всегда должны выполнять getLogger (Clazz.class), чтобы связать журналирование, выполненное кодом в классе Clazz. Разработчики, которые хотят знать, какой из подклассов выполняется (например, SubClazz extends Clazz), должны сделать в SubClazz: getLogger (SubClazz.class) и что-то вроде: log.info ("вызов <что-то в моем базовом классе>");
inor
45

Раньше я думал, что все логгеры должны быть статичными; однако эта статья на wiki.apache.org поднимает некоторые важные проблемы с памятью, касающиеся утечек загрузчика классов. Объявление регистратора статическим предотвращает сбор мусора объявляемого класса (и связанных загрузчиков классов) в контейнерах J2EE, которые используют общий загрузчик классов. Это приведет к ошибкам PermGen, если вы повторно развернете приложение достаточное количество раз.

Я действительно не вижу никакого способа обойти эту проблему с утечкой загрузчика классов, кроме объявления регистраторов нестатическими.

пипера
источник
4
Я подозревал, что статическое поле также будет иметь проблему с утечкой памяти. Как говорили другие, нестатичность может иметь проблемы с производительностью. Какой же тогда идеальный способ?
лян
@piepera основная проблема, описанная в статье, на которую вы ссылались, - это возможность контролировать уровень ведения журнала в каждом приложении, когда "рассмотрим случай, когда класс, использующий" private static Log log = ", развертывается через ClassLoader, который является предком нескольких предположительно независимые «приложения» ». Я не вижу в этом проблемы, потому что в данной конкретной ситуации у приложений есть «точки соприкосновения», и в этой «общей точке» определяется уровень ведения журнала для класса, и да, он сохраняется для всех приложений ... но сохраните имея в виду, что этот класс [загружается] вне этих приложений
inor
17

Самая важная разница заключается в том, как это влияет на ваши файлы журналов: в какую категорию попадают журналы?

  • При первом выборе журналы подкласса попадают в категорию суперкласса. Мне это кажется очень нелогичным.
  • Есть вариант вашего первого случая:

    защищенный журнал журнала = новый Log4JLogger (getClass ());

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

  • Во втором варианте (частный статический) категория журнала - это класс, содержащий код ведения журнала. Итак, обычно класс, который делает то, что регистрируется.

Я настоятельно рекомендую последний вариант. По сравнению с другими решениями он имеет следующие преимущества:

  • Между журналом и кодом существует прямая связь. Легко найти, откуда пришло сообщение журнала.
  • Если кому-то нужно настроить уровни ведения журнала (что делается для каждой категории), обычно это происходит потому, что они заинтересованы (или нет) в некоторых конкретных сообщениях, написанных определенным классом. Если категория не соответствует классу, который пишет сообщения, настроить уровни сложнее.
  • Вы можете войти в статические методы
  • Регистраторы нужно инициализировать (или искать) только один раз для каждого класса, поэтому при запуске, а не для каждого созданного экземпляра.

Также у него есть недостатки:

  • Его необходимо объявить в каждом классе, в котором вы регистрируете сообщения (без повторного использования регистраторов суперкласса).
  • Вам нужно позаботиться о том, чтобы указать правильное имя класса при инициализации регистратора. (Но хорошая IDE позаботится об этом за вас).
Wouter Coekaerts
источник
4

Используйте инверсию управления и передайте логгер в конструктор. Если вы создадите регистратор внутри класса, у вас будет чертовски время с вашими модульными тестами. Вы пишете модульные тесты, не так ли?

Уэйн Аллен
источник
5
Модульные тесты, которые исследуют журналы, которые вы производите, бесполезны и невероятно хрупки.
Michael
1
Полезность в зависимости от тестируемой системы. Иногда регистрация - это все, к чему у вас есть доступ.
Уэйн Аллен
@Wayne Allen, когда вы выполняете модульные тесты, по определению, у вас также есть результаты тестов. Вы предлагаете ситуацию, в которой выполняется модульное тестирование, но нет результатов тестов? есть только логи?
inor
создание логгера внутри класса не создает проблем. Можете ли вы показать простой пример, который сложно UT, потому что класс создает свой собственный регистратор?
inor
1
Конечно. Как насчет регистратора, который отправляет электронные письма. Не хочу делать это каждый раз при запуске тестов. Плюс как вы утверждаете побочный эффект?
Уэйн Аллен