Чи повинен реєстратор бути приватним статичним чи ні


103

Чи слід оголосити реєстратор статичним чи ні? Зазвичай я бачив два типи декларації для реєстратора:

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

або

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

Який слід використовувати? які плюси і мінуси обох?


1
Лісозаготівля - всебічна проблема. Використовуйте аспекти, і питання суперечить.
Дейв Джарвіс

4
static- одна довідка на клас. нестатичний - це одна посилання на екземпляр (+ ініціалізація). Тому в деяких випадках останній піддається значному впливу на пам'ять, якщо у вас є багато примірників. Ніколи не використовуйте нестатичні речовини в частих об'єктах. Я завжди використовую статичну версію. (Який повинен бути в верхньому регістрі LOG )
припиняв - Anony-Мус

2
як уже запропоновано, використовуйте AOP та примітки, наприклад: jcabi.com/jcabi-aspects/annotation-loggable.html
yegor256

1
RobertHume статична версія буде з допомогою константи. Саме тому його слід мати великі літери.
Мав QUIT - Anonymous-Mousse

2
Ні, це не повинно бути private static final Log logмалі літери. Журнал не є постійною, реєстратор - статичним кінцевим об'єктом (який можна мутувати). Особисто я завжди користуюся logger.
osundblad

Відповіді:


99

Перевага нестатичної форми полягає в тому, що ви можете оголосити її у (абстрактному) базовому класі, як слід, не переживаючи, що буде використано правильне ім'я класу:

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

Однак його недолік очевидно, що для кожного екземпляра класу буде створений цілий новий екземпляр реєстратора. Це само по собі не може бути дорогим, але це додає значних витрат. Якщо ви хочете цього уникнути, staticзамість цього скористайтеся формою. Але його недолік у свою чергу полягає в тому, що вам потрібно оголосити це в кожному окремому класі та подбати про те, щоб під час створення лісорубника було використано правильне ім'я класу, оскільки getClass()його не можна використовувати в статичному контексті. Однак у середньому IDE ви можете створити для цього шаблон автозаповнення. Наприклад logger+ctrl+space .

З іншого боку, якщо ви отримаєте реєстратор на заводі, який, в свою чергу, може кешувати вже створені реєстратори, використання нестатичної форми не призведе до великих витрат. Наприклад, Log4j має LogManagerдля цього.

protected Log log = LogManager.getLogger(getClass());

6
Декларуйте abstract Log getLogger();в абстрактному класі. Реалізуйте цей метод, повернувши статичний реєстратор для конкретного екземпляра. Додайте private final static Log LOG = LogManager.getLogger(Clazz.class);до шаблону класу IDE.
Є QUIT - Anonymous-Mousse

2
Для slf4j:protected Logger log = LoggerFactory.getLogger(getClass());
Markus Pscheidt

3
@BalusC Проблема з передачею getClass () методу getLogger полягає в тому, що він повертає клас поточного екземпляра. Зазвичай бажано, щоб журнал був пов'язаний з класом, де знаходиться код. Наприклад, якщо код реєстрації знаходиться в класі Parent, ми хочемо, щоб журнал асоціювався з Parent, навіть якщо виконавчим екземпляром є і екземпляр класу Child, який є підкласом Parent. З GetClass () це буде пов'язано з Немовлям, неправильно
INOR

@inor: "неправильно"? Якщо ви не хочете абстрагувати клас, то вам не слід використовувати спочатку успадкований getClass (). Є розробники, які вважають це правильним і корисним, оскільки вони розкривають інформацію, в якій саме підкласі виконується логіка.
BalusC

2
@ BalusC getLogger (getClass ()) призводить до того, що завжди записує ім’я підкласу неправильно. Класи ведення журналу завжди повинні робити getLogger (Clazz.class), щоб асоціювати журнал, зроблений кодом, у класі Clazz. Розробники, які хочуть знати, який із підкласів виконується (наприклад, SubClazz розширює Clazz), повинні робити в SubClazz: getLogger (SubClazz.class) і щось на зразок: log.info ("виклик <щось у моєму базовому класі>");
inor

44

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

Я насправді не бачу жодного способу обійти цю проблему витоку завантажувача, крім декларування реєстраторів як нестатичних.


4
Я підозрював, що статичне поле також матиме проблему з витоком пам'яті. Нестатичні проблеми можуть мати проблеми, як говорили інші. Який ідеальний спосіб тоді?
laang

@piepera Основна проблема, описана у статті, на яку ви посилаєтесь, - це можливість контролювати рівень реєстрації в кожній програмі, коли "розглянемо випадок, коли клас, що використовує" приватний статичний журнал журналу = ", розгортається через ClassLoader, який є в родині декількох нібито незалежні "програми" ". Я не вважаю це проблемою, оскільки в цій конкретній ситуації програми мають "спільну основу", і в тій "спільній основі" визначається рівень журналу для класу, і так, це справедливо для всіх програм ... на увазі , що цей клас [завантажений] поза цими додатків
INOR

17

Найголовніша відмінність полягає в тому, як це впливає на ваші файли журналів: в яку категорію переходять журнали?

  • У вашому першому виборі журнали підкласу потрапляють у категорію надкласу. Це здається мені дуже протиінтуїтивним.
  • Існує варіант вашого першого випадку:

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

    У такому випадку у вашій категорії журналу вказано, над яким об’єктом працював код, який увійшов у систему.

  • У вашому другому виборі (приватна статична) категорія журналу - це клас, який містить код журналу. Так зазвичай клас, який робить те, що реєструється.

Я настійно рекомендую цей останній варіант. У порівнянні з іншими рішеннями він має ці переваги:

  • Між логіном і кодом існує пряме відношення. Неважко знайти там, звідки прийшло повідомлення журналу.
  • Якщо комусь доводиться налаштовувати рівні журналу (що робиться для кожної категорії), це зазвичай тому, що вони зацікавлені (або ні) в деяких конкретних повідомленнях, написаних певним класом. Якщо категорія не є класом, який пише повідомлення, складніше налаштувати рівні.
  • Ви можете увійти в статичні методи
  • Реєстратори потрібно ініціалізувати (або шукати вгору) лише один раз на клас, тому при запуску замість кожного створеного екземпляра.

Він також має недоліки:

  • Це потрібно задекларувати в кожному класі, де ви реєструєте повідомлення (не використовуйте повторно реєстраторів суперкласових класів).
  • Потрібно подбати про те, щоб поставити правильне ім’я класу при ініціалізації журналу. (Але добре, що IDE подбає про це за вас).

4

Використовуйте інверсію управління та передайте реєстратор в конструктор. Якщо ви створите реєстратор всередині класу, вам доведеться мати чорт часу з вашими одиничними тестами. Ви пишете одиничні тести, чи не так?


5
Тестові одиниці, які вивчають ведення журналу, який ви виробляєте, звучать і марно, і неймовірно крихко.
Майкл

1
Корисність залежно від тестуваної системи. Іноді ведення журналу - це все, до чого ти маєш доступ.
Уейн Аллен

@Wayne Allen, коли ви робите одиничні тести, за визначенням у вас також є результати тестів. Чи пропонуєте ви ситуацію, коли людина робить тестовий пристрій, але не має результатів тестів? є лише журнали?
inor

створення реєстратора всередині класу не створює проблем. Чи можете ви показати простий приклад, важкий для UT, оскільки клас створює власний реєстратор?
inor

1
Звичайно. Як щодо реєстратора, який надсилає електронні листи. Не хочуть цього робити щоразу, коли запускаєте тести. Плюс як ти стверджуєш побічний ефект?
Уейн Аллен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.