Должен ли логгер Log4J быть объявлен временным?

Я использую Java 1.4 с Log4J.

Часть моего кода включает сериализацию и десериализацию объектов значений (POJO).

Каждый из моих POJO объявляет регистратор с

private final Logger log = Logger.getLogger(getClass());

Сериализатор жалуется на то, что org.apache.log4j.Logger не поддерживает сериализацию.

Должен ли я использовать

private final transient Logger log = Logger.getLogger(getClass());

вместо?

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

svrist 17.09.2008 15:50

Я должен был упомянуть, что мне нравится идея иметь регистратор для каждого экземпляра, а не по одному для каждого класса. У меня было бы много экземпляров одного и того же класса, созданных и управляемых контейнером (скажем, контекстом приложения Spring), и я хотел бы переключать уровни ведения журнала для каждого компонента, а не для каждого класса. В примере контекста приложения Spring регистратор будет объявлен как нечто вроде закрытого конечного переходного процесса. Logger log = Logger.getLogger (getBeanName ()); где bean-компонент больше не является POJO и реализует BeanNameAware

Vihung 23.11.2010 00:30

См. Также FAQ по SLF4J: «Должны ли члены регистратора класса быть объявлены как статические? [...] Таким образом, объявление членов регистратора как статических переменных требует меньше процессорного времени и имеет немного меньший объем памяти. [...] Однако переменные экземпляра делают это возможным для создания отдельной среды регистратора для каждого приложения, даже для регистраторов, объявленных в разделяемых библиотеках. Возможно, более важным, чем ранее упомянутые соображения, является то, что переменные экземпляра удобны для IOC, а статические - нет ".

Arjan 16.09.2012 20:48

Начните использовать SLF4J, и о сериализации позаботятся автоматически: «Начиная с SLF4J версии 1.5.3, экземпляры регистратора выдерживают сериализацию. Таким образом, сериализация класса хоста больше не требует каких-либо специальных действий, даже если регистраторы объявлены как переменные экземпляра. В предыдущих версиях экземпляры регистратора нужно было объявить как временные в классе хоста. "(См. В [SLF4J FAQ] (slf4j.org/faq.html))

Istvan Devai 18.02.2017 19:02
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
31
4
18 515
9
Перейти к ответу Данный вопрос помечен как решенный

Ответы 9

Регистратор должен быть статическим; это сделало бы его несериализуемым.

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

Иногда может быть полезно иметь нестатический регистратор, если вы хотите использовать разные имена регистратора для каждого экземпляра (например, добавление уникальной для экземпляра строки в конец имени регистратора) или если у вас есть регистратор в объекте суперкласса и вы хотите, чтобы он использовал имя экземпляра подкласса.

MB. 17.09.2008 15:53
«Нет причин делать регистратор нестатичным, если только у вас нет для этого веских причин». -- this makes my head explode! ;-)
Arjan 16.09.2012 20:43
Ответ принят как подходящий

Как насчет использования статического регистратора? Или вам нужна отдельная ссылка на регистратор для каждого экземпляра класса? Статические поля по умолчанию не сериализуются; вы можете явно объявить поля для сериализации с помощью частного статического окончательного массива ObjectStreamField с именем serialPersistentFields. См. Документацию Oracle

Добавлен контент: Поскольку вы используете getLogger (getClass ()), вы будете использовать один и тот же регистратор в каждом экземпляре. Если вы хотите использовать отдельный регистратор для каждого экземпляра, вы должны различать имя регистратора в методе getLogger (). например getLogger (getClass (). getName () + hashCode ()). Затем вы должны использовать атрибут transient, чтобы убедиться, что регистратор не сериализован.

@ ThorbjørnRavnAndersen Не могли бы вы объяснить, почему это не подходит для повторного развертывания веб-приложений? Или эта проблема исправлена ​​в более новых AS, таких как WildFly 8.2 и т. д.?

CSchulz 13.08.2015 10:41

статические регистраторы имеют тенденцию - если ваше приложение не очень тщательно написано - удерживать загрузчики классов, чтобы они не могли быть собраны мусором. Это показано, например, в неуклонно увеличивающееся использование памяти во время сборки maven, которое исчезает, когда maven разветвляет javac.

Thorbjørn Ravn Andersen 13.08.2015 14:24

В этой статье есть хорошее объяснение, почему не использовать статический логгер wiki.apache.org/commons/Logging/StaticLog

Joshua H 18.02.2016 11:51

Предотвращает ли статический регистратор проблем параллелизма в контексте Spark?

Peter 17.05.2019 13:21

Вместо этого попробуйте сделать Регистратор статическим. Тогда вам не нужно заботиться о сериализации, потому что она обрабатывается загрузчиком классов.

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

Регистраторы не сериализуемы, поэтому вы должны использовать переходный процесс при их сохранении в полях экземпляра. Если вы хотите восстановить регистратор после десериализации, вы можете сохранить уровень (String) в отдельном объекте, который сериализуется.

Такие случаи, особенно в EJB, обычно лучше всего обрабатываются через локальное состояние потока. Обычно вариант использования - это что-то вроде того, что у вас есть конкретная транзакция, в которой возникла проблема, и вам нужно повысить уровень ведения журнала для отладки для этой операции, чтобы вы могли создать подробный журнал для проблемной операции. Перенести некоторое локальное состояние потока через транзакцию и использовать его для выбора правильного регистратора. Честно говоря, я не знаю, где было бы полезно установить уровень для INSTANCE в этой среде, потому что отображение экземпляров в транзакцию должно быть функцией уровня контейнера, вы фактически не будете контролировать, какой экземпляр используется в в любом случае данная транзакция.

Даже в тех случаях, когда вы имеете дело с DTO, обычно не рекомендуется проектировать вашу систему таким образом, чтобы требовался конкретный конкретный экземпляр, потому что дизайн может легко развиваться таким образом, что это плохой выбор. Вы можете прийти через месяц и решить, что соображения эффективности (кэширование или какая-либо другая оптимизация изменения жизненного цикла) нарушит ваше предположение о отображении экземпляров в единицы работы.

Либо объявите поле регистратора как статическое, либо как временное.

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

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

Если вы В самом деле хотите использовать переходный подход, вам нужно будет сбросить журнал, когда ваш объект десериализуется. Для этого нужно реализовать метод:

 private void readObject(java.io.ObjectInputStream in) 
   throws IOException, ClassNotFoundException;

В javadocs для Сериализуемый есть информация об этом методе.

Ваша реализация будет выглядеть примерно так:

 private void readObject(java.io.ObjectInputStream in) 
     throws IOException, ClassNotFoundException {
   log = Logger.getLogger(...);
   in.defaultReadObject();
 }

Если вы этого не сделаете, то после десериализации вашего объекта журнал будет нулевым.

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

(Как уже упоминалось, используйте статические или переходные).

Другие вопросы по теме