Является ли Logger.getLogger (MyClass.class) лучшим способом инициализации логгеров log4j?

13

Этот учебник Mkyong предлагает инициализировать регистраторы следующим образом:

@Controller
public class WelcomeController {

    private static final Logger logger = Logger.getLogger(WelcomeController.class);

   // etc

}

Предположительно, теперь любой другой используемый вами класс, имеющий регистратор, будет инициализировать свои регистраторы таким же образом.

Мой вопрос - это лучший способ сделать это? Кажется ... повторяющимся.

dwjohnston
источник
2
Что вы находите подробным об этом (за исключением синтаксиса Java)? Вы должны создать переменную для хранения регистратора, и вы должны указать getLogger()имя регистратора, чтобы получить.
kdgregory
2
@kdgregory тот факт, что я делаю то же самое в каждом классе.
dwjohnston
1
Посмотрите на этот вопрос о переполнении стека
toniedzwiedz
3
Слишком стар для миграции, но этот вопрос здесь не по теме и больше подходит для StackOverflow.
Андрес Ф.
1
@AndresF. Я думаю, что я поставил это здесь, потому что это больше вопрос о стилях кода / шаблонах проектирования, чем о технической проблеме.
Двонстон

Ответы:

16

Ваш комментарий говорит, что «многословный» относится к необходимости повторять эту строку кода в каждом классе. Мой первый ответ заключается в том, что добавление двух строк кода (определение переменной плюс оператор импорта) к каждому классу не так уж и сложно. Тем более, что вам нужно только добавить их в классы, которые ведут себя и, следовательно, должны вести журналы. Тем не менее, конкретная строка кода, которую вы используете, подвержена ошибкам копирования-вставки (подробнее об этом позже).

Но, поскольку вы хотите альтернативы, вот несколько, с причинами, по которым вы можете или не захотите их использовать.

Используйте один регистратор для всего приложения

Если вас не волнует, какой класс сообщает, или вы хотите поместить в сообщение весь необходимый контекст, тогда простой одноэлементный регистратор сделает эту работу:

LoggerSingleton.getInstance().debug("MyController is running")

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

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

Создайте свои регистраторы на месте использования

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

Logger.getLogger(getClass()).debug("blah blah blah");

Используйте постпроцессор бина, чтобы внедрить регистратор

В вашем примере используется Spring, а Spring позволяет вам подключиться к коду инициализации компонента. Вы можете создать постпроцессор, который проверяет бин на loggerпеременную-член и создает Loggerэкземпляр, когда находит его.

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

Используйте миксин

Scala и Groovy предоставляют черты , которые позволяют вам инкапсулировать поведение. Типичным шаблоном Scala является создание Loggingпризнака, а затем добавление его в класс, который требует регистрации:

class MyController with Logging

К сожалению, это означает, что вам нужно переключать языки. Если вы не используете Java 8, в этом случае вы можете создать Loggingинтерфейс с «методом по умолчанию»:

public interface Logging {
    default Logger getLogger() {
        return Logger.getLogger(getClass());
    } 
}

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

getLogger().debug("blah blah blah");

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

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

И вам все еще нужно заявление на импорт.

Переместить регистратор в суперкласс

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

public abstract class AbstractController {
    protected Logger logger = Logger.getLogger(getClass());
}

Теперь ваши классы контроллеров наследуются AbstractControllerи имеют доступ к loggerпеременной. Помните, что вы должны поместить @Controllerаннотацию в конкретный класс.

Некоторые люди сочтут это извращением наследства. Я попытался смягчить их, назвав класс, AbstractControllerа не AbstractProjectClass. Вы сами можете решить, есть ли отношения.

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

kdgregory
источник
Я думаю, что вы должны быть осторожны с getClass () в классе наследования, MethodHandles.lookup (). LookupClass () лучше после jdk 7
yuxh
@luxh - у тебя есть объяснение этому?
kdgregory
проверьте здесь: stackoverflow.com/a/6653577/4652536
19
@yuxh - единственное упоминание о MethodHandles в этом вопросе (которого не было в ответе, который вы связали) - это аналогичное неподдерживаемое утверждение с комментарием, требующим разъяснения. У вас есть авторитетная поддержка для утверждения, что MethodHandles.lookup().lookupClass()лучше, чем Object.getClass()?
kdgregory
MethodHandles.lookup().lookupClass()может использоваться для статических переменных, не подвержен ошибкам копирования и вставки и быстр: stackoverflow.com/a/47112323/898747 Это означает дополнительный вход, но мне очень нравятся мои логгеры, находящиеся в статическом состоянии, поэтому, по крайней мере, стоит упомянуть :)
FableBlaze
0

Для того, чтобы расширить ответ предоставленного @kdgregory, Groovy обеспечивает @Slf4j( groovy.util.logging.Slf4jвыход) из коробки , как аннотация , которая выполняет преобразование AST на классе лавировать его с регистратором , который принимает имя переменного logпо умолчанию , если не указано слева.

Даниэль Пол Анзальдо
источник