Почему бы не использовать java.util.logging?


351

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

Для ведения журнала я (да и те люди, с которыми я работаю) всегда использовал JUL (java.util.logging) и никогда не сталкивался с какими-либо проблемами. Однако теперь мне нужно более подробно понять, что я должен делать для разработки своего API. Я провел некоторое исследование по этому вопросу, и с информацией, которую я получил, я просто запутался. Отсюда и этот пост.

Так как я приехал из июля, я склонен к этому. Мои знания об отдыхе не так уж велики.

Из проведенного мною исследования я пришел к выводу, что людям не нравится JUL:

  1. «Я начал разрабатывать на Java задолго до того, как Sun выпустила JUL, и мне было проще перейти на logging-framework-X, чем изучать что-то новое» . Хм. Я не шучу, это на самом деле то, что говорят люди. С этим аргументом мы все могли бы делать COBOL. (однако я, конечно, могу относиться к тому, что я ленивый чувак)

  2. «Мне не нравятся названия уровней логирования в JUL» . Хорошо, серьезно, этого просто недостаточно для введения новой зависимости.

  3. «Мне не нравится стандартный формат вывода из JUL» . Хм. Это просто конфигурация. Вам даже не нужно ничего делать по коду. (правда, в старые времена вам, возможно, приходилось создавать свой собственный класс Formatter, чтобы сделать это правильно).

  4. «Я использую другие библиотеки, которые также используют logging-framework-X, поэтому я подумал, что проще использовать эту» . Это круговой аргумент, не так ли? Почему «все» используют logging-framework-X, а не JUL?

  5. «Все остальные используют logging-framework-X» . Для меня это просто особый случай из вышеперечисленного. Большинство не всегда верно.

Так что настоящий большой вопрос - почему не JUL? , Что я пропустил? Основанием для регистрации фасадов (SLF4J, JCL) является то, что несколько реализаций регистрации существовали исторически, и причина этого действительно восходит к эпохе, предшествующей JUL, как я ее вижу. Если бы JUL был безупречен, то не было бы вырубки фасадов или как? Чтобы сделать вещи более запутанными, JUL - это в какой-то степени сам фасад, позволяющий менять местами обработчики, форматеры и даже LogManager.

Вместо того, чтобы использовать несколько способов сделать одно и то же (ведение журнала), не должны ли мы задаться вопросом, почему они были необходимы в первую очередь? (и посмотрите, если эти причины все еще существуют)

Итак, мои исследования до сих пор привели к нескольким вещам, которые, как я вижу, могут быть реальными проблемами с JUL:

  1. Производительность . Некоторые говорят, что производительность в SLF4J превосходит остальные. Мне кажется, это случай преждевременной оптимизации. Если вам нужно регистрировать сотни мегабайт в секунду, тогда я не уверен, что вы все равно на правильном пути. JUL также эволюционировал, и тесты, которые вы проводили на Java 1.4, могут больше не соответствовать действительности. Вы можете прочитать об этом здесь, и это исправление вошло в Java 7. Многие также говорят о накладных расходах конкатенации строк в методах ведения журнала. Однако ведение журнала на основе шаблонов позволяет избежать этой стоимости и существует также в JUL. Лично я никогда не пишу логи на основе шаблонов. Слишком ленив для этого. Например, если я сделаю это с JUL:

    log.finest("Lookup request from username=" + username 
       + ", valueX=" + valueX
       + ", valueY=" + valueY));

    моя IDE предупредит меня и попросит разрешения изменить его на:

    log.log(Level.FINEST, "Lookup request from username={0}, valueX={1}, valueY={2}", 
       new Object[]{username, valueX, valueY});

    .. что я, конечно, приму. Разрешение получено ! Спасибо за помощь.

    Так что я на самом деле не пишу такие заявления сам, это делается в IDE.

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

  2. Конфигурация из classpath . Готовый JUL не может загрузить файл конфигурации из пути к классам. Это несколько строк кода, чтобы сделать это. Я понимаю, почему это может раздражать, но решение является коротким и простым.

  3. Наличие обработчиков вывода . JUL поставляется с 5 встроенными обработчиками вывода: консоль, файловый поток, сокет и память. Они могут быть расширены или могут быть написаны новые. Например, это может быть запись в системный журнал UNIX / Linux и журнал событий Windows. Лично у меня никогда не было этого требования, и я не видел, чтобы оно использовалось, но я определенно могу понять, почему оно может быть полезным. Logback поставляется с приложением для Syslog, например. Тем не менее я бы сказал, что

    1. 99,5% потребностей в конечных пунктах назначения покрываются за счет того, что имеется в JUL «из коробки».
    2. Особые потребности могут удовлетворяться пользовательскими обработчиками поверх JUL, а не поверх чего-то еще. Для меня нет ничего, что предполагало бы, что для написания обработчика вывода Syslog для JUL требуется больше времени, чем для другой среды ведения журналов.

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

Так что мне делать с моим API? Я хочу, чтобы это стало успешным. Я, конечно, могу просто «плыть по течению» и внедрить SLF4J (который кажется самым популярным в наши дни), но ради себя, я все еще должен точно понять, что не так с сегодняшним JUL, который оправдывает весь пух? Смогу ли я саботировать себя, выбрав JUL для своей библиотеки?

Тестирование производительности

(раздел добавлен nolan600 07 июля 2012 г.)

Ниже приводится ссылка Ceki на то, что параметризация SLF4J в 10 или более раз быстрее, чем в JUL. Итак, я начал делать несколько простых тестов. На первый взгляд претензия, безусловно, верна. Вот предварительные результаты (но читайте дальше!):

  • Время выполнения SLF4J, выход из базы данных: 1515
  • Время исполнения SLF4J, бэкэнд, JUL: 12938
  • Время выполнения ИЮЛЬ: 16911

Числа выше - это мсек, поэтому чем меньше, тем лучше. Так что разница в производительности в 10 раз на первый взгляд довольно близка. Моя первоначальная реакция: это много!

Вот суть теста. Как видно, целое число и строка строятся в цикле, который затем используется в операторе log:

    for (int i = 0; i < noOfExecutions; i++) {
        for (char x=32; x<88; x++) {
            String someString = Character.toString(x);
            // here we log 
        }
    }

(Я хотел, чтобы оператор log имел как примитивный тип данных (в данном случае int), так и более сложный тип данных (в данном случае String). Не уверен, что это имеет значение, но он у вас есть.)

Оператор журнала для SLF4J:

logger.info("Logging {} and {} ", i, someString);

Оператор журнала для JUL:

logger.log(Level.INFO, "Logging {0} and {1}", new Object[]{i, someString});

JVM был «подогрет» тем же тестом, который был выполнен один раз перед тем, как было проведено реальное измерение. Java 1.7.03 использовалась в Windows 7. Использовались последние версии SLF4J (v1.6.6) и Logback (v1.0.6). Stdout и stderr были перенаправлены на нулевое устройство.

Однако, будьте осторожны, оказывается, что JUL тратит большую часть своего времени, getSourceClassName()потому что JUL по умолчанию печатает имя исходного класса в выводе, а Logback - нет. Итак, мы сравниваем яблоки и апельсины. Я должен сделать тест снова и сконфигурировать реализации ведения журнала аналогичным образом, чтобы они фактически выводили один и тот же материал. Однако я подозреваю, что SLF4J + Logback все равно выйдет на первое место, но далеко от начальных чисел, приведенных выше. Следите за обновлениями.

Кстати: тест был первый раз, когда я фактически работал с SLF4J или Logback. Приятный опыт. JUL, конечно, гораздо менее гостеприимный, когда вы начинаете.

Тестирование производительности (часть 2)

(раздел добавлен nolan600 08 июля 2012 г.)

Как выясняется, для производительности не имеет значения, как вы конфигурируете свой шаблон в JUL, т.е. включает ли он имя источника или нет. Я попробовал с очень простым шаблоном:

java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"

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

Поэтому я делаю вывод о проблеме производительности, согласно которой, по крайней мере, для протестированного оператора журнала на основе шаблонов реальная разница в производительности между JUL (медленным) и SLF4J + Logback (быстрым) кажется примерно в 10 раз больше. Как сказал Цеки.

Я также вижу еще одну вещь, а именно то, что getLogger()вызов SLF4J намного дороже, чем тот же JUL . (95 мс против 0,3 мс, если мой профилировщик точен). Это имеет смысл. SLF4J нужно некоторое время на связывание базовой реализации ведения журнала. Это не пугает меня. Эти вызовы должны быть несколько редкими в течение жизни приложения. Стойкость должна быть в реальном журнале звонков.

Окончательный вывод

(раздел добавлен nolan600 08 июля 2012 г.)

Спасибо за все ваши ответы. Вопреки тому, что я изначально думал, я решил использовать SLF4J для своего API. Это основано на ряде вещей и ваших данных:

  1. Это дает гибкость выбора реализации журнала во время развертывания.

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

  3. SLF4J, конечно, намного быстрее, как описано выше, в частности, если вы соедините его с Logback. Даже если это был грубый тест, у меня есть основания полагать, что в SLF4J + Logback было затрачено гораздо больше усилий, чем в JUL.

  4. Документация. Документация для SLF4J просто намного более полная и точная.

  5. Гибкость рисунка. Выполняя тесты, я решил, что JUL будет имитировать шаблон по умолчанию из Logback. Этот шаблон включает в себя имя потока. Оказывается, JUL не может сделать это из коробки. Хорошо, я не пропустил это до сих пор, но я не думаю, что это должно быть упущено из каркаса журнала. Период!

  6. Большинство (или многие) Java-проектов сегодня используют Maven, поэтому добавление зависимости не так уж и важно, особенно если эта зависимость довольно стабильна, то есть не постоянно меняет свой API. Похоже, что это верно для SLF4J. Также баночка и друзья SLF4J имеют небольшие размеры.

Так что странная вещь, которая произошла, состояла в том, что я на самом деле очень расстроился из-за JUL после того, как немного поработал с SLF4J. Я все еще сожалею, что так должно быть с JUL. JUL далеко не идеален, но отчасти делает свою работу. Просто не совсем хорошо. То же самое можно сказать Propertiesв качестве примера, но мы не думаем об абстрагировании, чтобы люди могли подключить свою собственную библиотеку конфигурации и то, что у вас есть. Я думаю, что причина в том, что он Propertiesнаходится чуть выше планки, в то время как для JUL сегодня все наоборот ... и в прошлом он пришел в ноль, потому что его не было.


8
Я не буду спрашивать закрытие, так как этот хорошо представленный вопрос интересен, но он граничит, если вы прочитаете FAQ: будет трудно найти окончательный уникальный ответ, не основанный на мнениях.
Денис Сегюре

Возможно, вы упустили то, что многие авторы фреймворков перестали пытаться использовать JUL, и поэтому его часто сложнее использовать, если вы не просто делаете vanilla java.
Денис Сегюре

3
Вводить в заблуждение использование общего термина "logging-framework-X" при обращении к популярным каркасу (логикам), предшествующим июлю. Вы должны использовать «log4j» в этом случае. Другие популярные фреймворки, такие как SLF4J и logback, появились после выхода jul.
Ceki

1
@Acuariano. Проект Netty просто использует Reflection для проверки того, какая структура ведения журнала доступна в classpath. Смотрите здесь для источника. См InternalLoggerFactory.java.
Петер

1
@xenoterracide еще более важным было бы обновление для Java 9, как это было представлено java.lang.System.Logger, представляющего собой интерфейс , который можно перенаправить на любую реальную инфраструктуру ведения журналов, которую вы хотите, при условии, что эта среда догоняет и обеспечивает реализацию этого интерфейса. В сочетании с модульностью вы можете даже развернуть приложение с JRE, не содержащим пакет java.util.logging, если вы предпочитаете другую среду.
Хольгер

Ответы:


207

Отказ от ответственности : я являюсь основателем проектов log4j, SLF4J и logback.

Есть объективные причины для предпочтения SLF4J. Во- первых , SLF4J позволяет конечному пользователю свободно выбирать базовую структуру ведения журнала . Кроме того, более опытные пользователи, как правило, предпочитают logback, который предлагает возможности, выходящие за пределы log4j , а jul отстает. Для некоторых пользователей может быть достаточно функционального джула, но для многих других его просто нет. В двух словах, если ведение журнала важно для вас, вы можете использовать SLF4J с logback в качестве базовой реализации. Если регистрация не важна, с Джулем все в порядке.

Однако, как разработчик oss, вы должны учитывать предпочтения своих пользователей, а не только свои собственные. Из этого следует, что вы должны использовать SLF4J не потому, что вы убеждены, что SLF4J лучше, чем jul, а потому, что большинство разработчиков Java (в настоящее время (июль 2012 г.)) предпочитают SLF4J в качестве своего API для ведения журнала. Если в конечном итоге вы решили не заботиться о распространенном мнении, примите во внимание следующие факты:

  1. те, кто предпочитают джул, делают это из-за удобства, потому что джул в комплекте с JDK. Насколько мне известно, других объективных аргументов в пользу юля нет.
  2. Ваше собственное предпочтение джулу - это просто предпочтение .

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

Если все еще не убежден, JB Nizet приводит дополнительный и убедительный аргумент:

За исключением того, что конечный пользователь мог уже выполнить эту настройку для своего собственного кода или другой библиотеки, которая использует log4j или logback. jul является расширяемым, но необходимость расширять logback, jul, log4j, и Бог знает только, какие другие каркасы журналирования, потому что он использует четыре библиотеки, которые используют четыре разные каркасы логирования, громоздка. Используя SLF4J, вы позволяете ему настраивать необходимые каркасы логирования, а не ту, которую вы выбрали. Помните, что типичный проект использует множество библиотек, а не только вашу .

Если по какой-то причине вы ненавидите SLF4J API и используете его, вы потеряете удовольствие от своей работы, тогда непременно идите на июль. В конце концов, есть способы перенаправить джул на SLF4J .

Кстати, джул-параметризация, по крайней мере, в 10 раз медленнее, чем у SLF4J, что в итоге дает заметную разницу.


2
@Ceki, возможно, вы захотите немного рассказать об отказе от ответственности, чтобы в нем упоминалась ваша текущая роль в проектах log4j, slf4j и logback. Причина заключается в том, чтобы объяснить вашу предвзятость.
Турбьёрн Равн Андерсен

2
Есть ли какая-то поддержка утверждению, что большинство разработчиков Java предпочитают SLF4J в качестве своего API для регистрации?
Оливье Кайо

3
Суть моего поста в том, что у разных разработчиков разные предпочтения, что кажется бесспорным. Да?
Ceki

1
Честно говоря, я бы хотел увидеть тесты 2018 года для Java 11 (или чего-то еще) и против log4j2 в асинхронном режиме.
ксенотеррацид

5
Здесь я использую SLF4J, и мне все еще приходится иметь дело со всеми остальными средами журналирования, которые используют другие библиотеки. Использование SLF4J не решает проблему разнородных регистраторов, а только ухудшает ее. xkcd.com/927
Чарли

34
  1. java.util.loggingбыл введен в Java 1.4. До этого были случаи использования логирования, поэтому существует много других API логирования. Те API, которые интенсивно использовались до Java 1.4 и, следовательно, имели большой рыночный ресурс, который не просто упал до 0, когда был выпущен 1.4.

  2. JUL начинал не так уж и хорошо, многие из тех вещей, о которых вы упомянули, были намного хуже в 1.4 и стали лучше только в 1.5 (и я думаю, что и в 6, но я не слишком уверен).

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

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

  5. Библиотеки не могут легко измениться. Если предыдущая версия библиотеки использовала logging-library-X, она не может легко переключиться на logging-library-Y (например, JUL), даже если последняя явно превосходна: любой пользователь этой библиотеки должен был бы изучить новая структура ведения журнала и (по крайней мере) перенастройка их ведения журнала. Это большое нет-нет, особенно когда это не приносит видимой выгоды большинству людей.

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


1
Спасибо Йоахим, я ценю ваш пост. Ваши (1) и (2) для меня просто история. Давным-давно. Ваш (4) является следствием этого и затем становится тем, что я называю циклическим аргументом. Ваш (3), однако, действительно интересный. Может быть, вы к чему-то? Но это затронет только тех, кто строит контейнеры приложений, которых в конце концов очень мало. Или что?
Петер

3
Ну, кто игнорирует историю, тот обречен повторять ее ;-) История очень важна для разработки программного обеспечения. Люди не двигаются слишком быстро, и замена существующих сторонних библиотек стандартными API работает хорошо, только если стандартные API работают по крайней мере так же хорошо, как сторонние библиотеки. И они не сделали изначально (и, возможно, до сих пор нет в некоторых случаях).
Иоахим Зауэр

Йоахим, меня интересуют те «в некоторых случаях, возможно, до сих пор нет», которые вы упоминаете. Вот где мясо должно быть. Замена библиотеки логгера в существующем коде довольно проста и может быть автоматизирована в наши дни. У SLF4J есть инструмент для этого, который как бы подтверждает мою точку зрения. Поэтому я думаю, что огромная библиотека, написанная в 2002 году с log4j, может быть преобразована в JUL за считанные минуты с помощью автоматизированного инструмента. (Хотя я не знаю, существует ли он). Так почему этого не происходит?
Петер

3
@ nolan6000: Я не знаю достаточно об особенностях, чтобы вдаваться в подробности об этой фразе, и это не совсем то, о чем я говорю. Даже если JUL сейчас находится на одном уровне со сторонними средами, инерция и существующая инфраструктура по-прежнему являются серьезной причиной, чтобы не переключаться. Например, если библиотека X использовала slf4j в Версии 1.1, переключение на JUL в 1.2 (или даже 2.0) было бы серьезной проблемой для многих пользователей (которые уже правильно настроили старую систему и должны были бы сделать это заново без видимой выгоды) ,
Иоахим Зауэр

@ nolan6000, даже если вы не заботитесь об истории, библиотеки, которые вы используете в своих приложениях, наверняка, имеют значение. Ничего страшного в том, чтобы отказаться от библиотеки просто потому, что она использовала другую среду ведения журналов, чем вы.
Турбьёрн Равн Андерсен

29

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

Возможно, он вложил время и деньги в Log4j или LogBack (специальные средства форматирования, приложения и т. Д.) И предпочитает продолжать использовать Log4j или LogBack, а не настраивать jul. Нет проблем: slf4j позволяет это. Это мудрый выбор, чтобы использовать Log4j над Джул? Может быть, а может и нет. Но тебе все равно. Позвольте конечному пользователю выбрать то, что он предпочитает.


Спасибо JB. Мой вопрос заключается в том, действительно ли я навязываю так много пользователю / исполнителю моей библиотеки, навязывая ему JUL? Если он недоволен, например, стандартными обработчиками вывода JUL, он может просто поменять их местами во время развертывания, как я это вижу. Я не вижу в JUL смирительной рубашки. Мне кажется, он такой же гибкий и расширяемый, как и все остальные.
Петер

12
За исключением того, что конечный пользователь мог уже выполнить эту настройку для своего собственного кода или другой библиотеки, которая использует log4j или LogBack. jul является расширяемым, но необходимость расширять LogBack, jul, log4j, и Бог знает только, какие другие каркасы журналирования, потому что он использует 4 библиотеки, которые используют 4 разные каркасы журналирования, громоздка. Используя slf4j, вы позволяете ему конфигурировать необходимые каркасы журналирования. не тот, который вы выбрали. Помните, что типичные проекты используют множество библиотек, а не только ваши.
JB Низет

6

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

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

Наше решение состояло в том, чтобы добавить фасад к библиотечному коду, который означал, что вызовы журнала библиотеки не изменились, а были динамически перенаправлены на любой доступный механизм ведения журнала. Когда они включены в инструмент POJO, они направляются в JUL, но при развертывании в виде веб-приложения они перенаправляются в LogBack.

Мы, конечно, сожалеем о том, что код библиотеки не использует параметризованное ведение журнала, но теперь его можно модифицировать по мере необходимости.

Мы использовали slf4j, чтобы построить фасад.


1
По какой причине вы не просто использовали пакет "redirect java.util.logging to slf4j" в дистрибутиве slf4j?
Турбьёрн Равн Андерсен

2
Мы сделали, но мало что стоило, потому что основное преимущество перехода на slf4j - это эффективная параметризованная регистрация. Если бы мы использовали это с самого начала, у нас не было бы никакой работы сейчас.
OldCurmudgeon

1
Я согласен, что это низко висящий фрукт slf4j.
Турбьёрн Равн Андерсен

3

Я запускал jul против slf4j-1.7.21 по logback-1.1.7, вывод на SSD, Java 1.8, Win64

Июль пробежал 48449 мс, выход из системы 27185 мс для цикла 1M.

Тем не менее, немного больше скорости и немного приятнее API не стоит 3 библиотеки и 800K для меня.

package log;

import java.util.logging.Level;
import java.util.logging.Logger;

public class LogJUL
{
    final static Logger logger = Logger.getLogger(LogJUL.class.getSimpleName());

    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            Object[] o = { lc };

            logger.log(Level.INFO,"Epoch time {0}", o);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }
}

а также

package log;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LogSLF
{
    static Logger logger = LoggerFactory.getLogger(LogSLF.class);


    public static void main(String[] args) 
    {
        int N = 1024*1024;

        long l = System.currentTimeMillis();

        for (int i = 0; i < N; i++)
        {
            Long lc = System.currentTimeMillis();

            logger.info("Epoch time {}", lc);
        }

        l = System.currentTimeMillis() - l;

        System.out.printf("time (ms) %d%n", l);
    }

}

3
Вы не сравниваете как для как. Почему вы явно создаете массив для jul? Я думаю, это потому, что в slf4j отсутствует перегрузка с одним аргументом logger.info(). Таким образом, вы намеренно наносите вред производительности jul, чтобы компенсировать недостаток интерфейса slf4j. Вместо этого вы должны кодировать оба метода так, как они идиоматически закодированы.
Klitos Kyriacou

2
Вы неправильно поняли это. Вам не нужно использовать дополнительные 800K. Согласие заключается в том, что очень тонкий API SLF4J стоит использовать, потому что тогда вы (или другие, кто может когда-нибудь повторно использовать ваш код!) Могут свободно переключаться между JUL, Logback, Log4j и т. Д. SLF4J составляет всего ~ 28K. Мост SLF4J-JUL (slf4j-jdk ... jar) составляет всего ~ 9K.
рископ
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.