Ведение журнала и внедрение зависимостей

Пытаюсь построить и приложение на базе Java.

Для внедрения зависимостей я использую Google Guice.

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

Я ищу ручное ведение журнала. Мне нужен способ входа почти в каждый класс в моем приложении. Поэтому я подумал о двух вариантах:

  1. получение регистратора с помощью инфраструктуры инъекций Guice, которая делает это за меня через конструктор (или сеттер, или частный ...), но похоже, что добавление проблемы регистрации действительно к каждому классу и загрязняет мой конструктор
  2. используя глобальный локатор службы в методе, в котором я хочу вызвать журнал. Но все фанаты DI будут ненавидеть меня за это

Итак, какой способ лучше всего с практической точки зрения?


person SimFirehawk    schedule 12.07.2013    source источник
comment
Какую информацию вы регистрируете при «ручном ведении журнала»?   -  person Steven    schedule 12.07.2013
comment
Все известные мне фреймворки ведения журналов используют одноэлементную фабрику для конкретных экземпляров логгеров. Если вы хотите быть гибкими по отношению к реализации регистратора, вы можете использовать фасад регистрации, например slf4j.   -  person Pyranja    schedule 12.07.2013
comment
Я не согласен с @Pyranja. Если вам нужна большая гибкость, определите свою собственную ILogger абстракцию (ту, которая содержит только один член), спрячьте свою структуру ведения журнала за этой абстракцией и внедрите этот интерфейс в классы, которые в нем нуждаются. Это позволяет вам правильно тестировать этот код и скрывает стороннюю зависимость. Взгляните на этот ответ.   -  person Steven    schedule 12.07.2013
comment
Я думаю, что мы должны соглашаться, чтобы не соглашаться. На мой взгляд, ведение журнала - это настолько общая задача, что использование настраиваемой абстракции не дает значительных преимуществ. Стандартные лесозаготовительные фасады, такие как slf4j, уже предлагают такую ​​абстракцию (которая также может быть добавлена, если кому-то не нравятся статические фабрики). Если нет особых требований, я ожидаю, что пользовательский интерфейс регистратора будет настолько похож на уже доступные, что они будут взаимозаменяемыми. И уже проделана тяжелая работа по подключению фасада к бетонным лесозаготовительным сооружениям!   -  person Pyranja    schedule 12.07.2013
comment
@Steven: отладочная информация, например, анализ файла (из какого файла). Но по мере того, как я думаю об этом, я понимаю, что некоторые из них противоречат SRP.   -  person SimFirehawk    schedule 12.07.2013


Ответы (2)


Мне нужен способ входа почти в каждый класс в моем приложении.

Подумай еще раз. Если вы думаете, что вам нужно входить почти в каждый класс, что-то не так с вашим дизайном. В этом ответе на Stackoverflow рассказывается о том, что может быть не так с вашим дизайном. Ответ на него дан в контексте .NET, но ответ применим и к Java.

В этом ответе в основном говорится о ведении журнала исключений, для ведения журнала без исключений я бы сказал: предотвращайте регистрацию слишком большого количества информации в слишком многих местах. Для каждой информации или предупреждения, которые вы хотите зарегистрировать, сначала спросите, не должно ли это быть исключением. Например, не записывайте в журнал такие вещи, как «нас не должно быть в этой ветке», а генерируйте исключение!

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

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

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

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

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

но это похоже на добавление заботы о ведении журнала к каждому классу и загрязняет мой конструктор

Это так, и вы получите конструкторы со слишком большим количеством параметров. Но не вините регистратора, вините свой код. Вы нарушаете здесь принцип единой ответственности. Вы можете «скрыть» эту зависимость, вызвав ее через статический фасад, но это не снижает количество зависимостей и общую сложность класса.

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

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

person Steven    schedule 12.07.2013
comment
Большое спасибо за ссылку. Это очень помогло понять мою проблему дизайна. Итак, я понимаю, что для исключений это не имеет смысла, и это намного лучше с декоратором. Для регистрации отладочной информации все еще остается вопрос сверху. - person SimFirehawk; 12.07.2013
comment
@SimFirehawk, хотя инженеры могут не читать журналы, другие системы могут обрабатывать их, чтобы сделать информацию, содержащуюся в журналах, более доступной. В таких средах имеет смысл писать модульные тесты для более важных журналов (например, чтобы убедиться, что что-то регистрируется в определенных сценариях). Для таких тестов может потребоваться удвоение регистратора. - person Noel Yap; 15.12.2016

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

Как и на многие вопросы, ответ на вопрос, как следует подходить к ведению журнала, зависит от ситуации. Конечно, есть некоторые варианты использования, которые можно уменьшить без необходимости в компонентах, зависящих от ведения журнала. Например, потребность в единообразном ведении журнала всех вызовов методов в библиотеке может быть решена с помощью шаблона декоратора, а избыточное использование исключений журналирования может быть устранено путем централизации такой регистрации в верхней части стека вызовов. Такие варианты использования важно учитывать, но они не отражают сути вопроса, который на самом деле Когда мы хотим добавить подробное ведение журнала в компонент на строго типизированных языках, таких как Java и C #, если зависимость быть выражена через конструктор компонента?

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

Цель избежать использования шаблона Service Locator - в конечном итоге упростить использование компонентов. При создании компонента мы не хотим, чтобы потребители гадали, что необходимо для того, чтобы компонент функционировал должным образом. Разработчикам, использующим наши библиотеки, не нужно смотреть на детали реализации, чтобы понять, какие зависимости необходимы для работы компонента. Однако во многих случаях ведение журнала является вспомогательной и необязательной задачей и служит только для предоставления отслеживающей информации для специалистов по обслуживанию библиотеки для диагностики проблем или для ведения журнала аудита с подробностями использования, о которых потребители не осведомлены или не заинтересованы. В случаях, когда потребители вашей библиотеки должны предоставлять зависимости, которые не нужны для основной функции компонента, выражение таких зависимостей как инвариантных (то есть параметров конструктора) фактически сводит на нет саму цель, к которой стремятся, избегая шаблона Service Locator. Кроме того, из-за того, что ведение журнала является сквозной проблемой (что означает, что такие потребности могут быть широко желательными для многих компонентов в библиотеке), введение зависимостей ведения журнала через конструктор еще больше усложняет использование.

Еще одним соображением является минимизация изменений в API-интерфейсе библиотеки. Поверхностный API вашей библиотеки - это любые общедоступные интерфейсы или классы, необходимые для построения. Часто библиотеки создаются с помощью контейнера DI, особенно для внутренних библиотек, не предназначенных для общего пользования. В таких случаях модули регистрации могут быть предоставлены библиотекой для конкретных контейнеров DI или могут использоваться такие методы, как регистрация на основе соглашений, которые скрывают типы верхнего уровня, но это не меняет того факта, что они все еще являются частью поверхности. -area API. Библиотека, которую можно легко использовать с контейнером DI, но также можно использовать и без него, лучше, чем та, которая должна использоваться с контейнером DI. Даже с контейнером DI часто бывает, что сложные библиотеки ссылаются на типы реализации напрямую для целей настраиваемой регистрации. Если используется стратегия внедрения необязательных зависимостей, открытый интерфейс изменяется каждый раз, когда разработчик хочет добавить ведение журнала к новому типу.

Лучший подход - следовать шаблону, установленному большинством библиотек журналов, таких как Serilog, log4net, NLog и т. Д., И получать логгеры через фабрику логгеров (например, Log.ForContext<MyClass>();). У этого есть и другие преимущества, такие как использование возможностей фильтрации каждой соответствующей библиотекой. Дополнительное обсуждение этой темы см. В этой статье < / а>.

person Derek Greer    schedule 28.07.2020