Впервые в жизни я нахожусь в положении, когда я пишу Java API, который будет с открытым исходным кодом. Надеюсь, будет включен во многие другие проекты.
Для ведения журнала я (да и те люди, с которыми я работаю) всегда использовал JUL (java.util.logging) и никогда не сталкивался с какими-либо проблемами. Однако теперь мне нужно более подробно понять, что я должен делать для разработки своего API. Я провел некоторое исследование по этому вопросу, и с информацией, которую я получил, я просто запутался. Отсюда и этот пост.
Так как я приехал из июля, я склонен к этому. Мои знания об отдыхе не так уж велики.
Из проведенного мною исследования я пришел к выводу, что людям не нравится JUL:
«Я начал разрабатывать на Java задолго до того, как Sun выпустила JUL, и мне было проще перейти на logging-framework-X, чем изучать что-то новое» . Хм. Я не шучу, это на самом деле то, что говорят люди. С этим аргументом мы все могли бы делать COBOL. (однако я, конечно, могу относиться к тому, что я ленивый чувак)
«Мне не нравятся названия уровней логирования в JUL» . Хорошо, серьезно, этого просто недостаточно для введения новой зависимости.
«Мне не нравится стандартный формат вывода из JUL» . Хм. Это просто конфигурация. Вам даже не нужно ничего делать по коду. (правда, в старые времена вам, возможно, приходилось создавать свой собственный класс Formatter, чтобы сделать это правильно).
«Я использую другие библиотеки, которые также используют logging-framework-X, поэтому я подумал, что проще использовать эту» . Это круговой аргумент, не так ли? Почему «все» используют logging-framework-X, а не JUL?
«Все остальные используют logging-framework-X» . Для меня это просто особый случай из вышеперечисленного. Большинство не всегда верно.
Так что настоящий большой вопрос - почему не JUL? , Что я пропустил? Основанием для регистрации фасадов (SLF4J, JCL) является то, что несколько реализаций регистрации существовали исторически, и причина этого действительно восходит к эпохе, предшествующей JUL, как я ее вижу. Если бы JUL был безупречен, то не было бы вырубки фасадов или как? Чтобы сделать вещи более запутанными, JUL - это в какой-то степени сам фасад, позволяющий менять местами обработчики, форматеры и даже LogManager.
Вместо того, чтобы использовать несколько способов сделать одно и то же (ведение журнала), не должны ли мы задаться вопросом, почему они были необходимы в первую очередь? (и посмотрите, если эти причины все еще существуют)
Итак, мои исследования до сих пор привели к нескольким вещам, которые, как я вижу, могут быть реальными проблемами с JUL:
Производительность . Некоторые говорят, что производительность в 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 не очень хорошая по сравнению с конкурентами.
Конфигурация из classpath . Готовый JUL не может загрузить файл конфигурации из пути к классам. Это несколько строк кода, чтобы сделать это. Я понимаю, почему это может раздражать, но решение является коротким и простым.
Наличие обработчиков вывода . JUL поставляется с 5 встроенными обработчиками вывода: консоль, файловый поток, сокет и память. Они могут быть расширены или могут быть написаны новые. Например, это может быть запись в системный журнал UNIX / Linux и журнал событий Windows. Лично у меня никогда не было этого требования, и я не видел, чтобы оно использовалось, но я определенно могу понять, почему оно может быть полезным. Logback поставляется с приложением для Syslog, например. Тем не менее я бы сказал, что
- 99,5% потребностей в конечных пунктах назначения покрываются за счет того, что имеется в JUL «из коробки».
- Особые потребности могут удовлетворяться пользовательскими обработчиками поверх 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. Это основано на ряде вещей и ваших данных:
Это дает гибкость выбора реализации журнала во время развертывания.
Проблемы с недостаточной гибкостью конфигурации JUL при запуске внутри сервера приложений.
SLF4J, конечно, намного быстрее, как описано выше, в частности, если вы соедините его с Logback. Даже если это был грубый тест, у меня есть основания полагать, что в SLF4J + Logback было затрачено гораздо больше усилий, чем в JUL.
Документация. Документация для SLF4J просто намного более полная и точная.
Гибкость рисунка. Выполняя тесты, я решил, что JUL будет имитировать шаблон по умолчанию из Logback. Этот шаблон включает в себя имя потока. Оказывается, JUL не может сделать это из коробки. Хорошо, я не пропустил это до сих пор, но я не думаю, что это должно быть упущено из каркаса журнала. Период!
Большинство (или многие) Java-проектов сегодня используют Maven, поэтому добавление зависимости не так уж и важно, особенно если эта зависимость довольно стабильна, то есть не постоянно меняет свой API. Похоже, что это верно для SLF4J. Также баночка и друзья SLF4J имеют небольшие размеры.
Так что странная вещь, которая произошла, состояла в том, что я на самом деле очень расстроился из-за JUL после того, как немного поработал с SLF4J. Я все еще сожалею, что так должно быть с JUL. JUL далеко не идеален, но отчасти делает свою работу. Просто не совсем хорошо. То же самое можно сказать Properties
в качестве примера, но мы не думаем об абстрагировании, чтобы люди могли подключить свою собственную библиотеку конфигурации и то, что у вас есть. Я думаю, что причина в том, что он Properties
находится чуть выше планки, в то время как для JUL сегодня все наоборот ... и в прошлом он пришел в ноль, потому что его не было.
InternalLoggerFactory.java
.java.lang.System.Logger
, представляющего собой интерфейс , который можно перенаправить на любую реальную инфраструктуру ведения журналов, которую вы хотите, при условии, что эта среда догоняет и обеспечивает реализацию этого интерфейса. В сочетании с модульностью вы можете даже развернуть приложение с JRE, не содержащим пакетjava.util.logging
, если вы предпочитаете другую среду.Ответы:
Отказ от ответственности : я являюсь основателем проектов log4j, SLF4J и logback.
Есть объективные причины для предпочтения SLF4J. Во- первых , SLF4J позволяет конечному пользователю свободно выбирать базовую структуру ведения журнала . Кроме того, более опытные пользователи, как правило, предпочитают logback, который предлагает возможности, выходящие за пределы log4j , а jul отстает. Для некоторых пользователей может быть достаточно функционального джула, но для многих других его просто нет. В двух словах, если ведение журнала важно для вас, вы можете использовать SLF4J с logback в качестве базовой реализации. Если регистрация не важна, с Джулем все в порядке.
Однако, как разработчик oss, вы должны учитывать предпочтения своих пользователей, а не только свои собственные. Из этого следует, что вы должны использовать SLF4J не потому, что вы убеждены, что SLF4J лучше, чем jul, а потому, что большинство разработчиков Java (в настоящее время (июль 2012 г.)) предпочитают SLF4J в качестве своего API для ведения журнала. Если в конечном итоге вы решили не заботиться о распространенном мнении, примите во внимание следующие факты:
Таким образом, удержание «неопровержимых фактов» над общественным мнением, хотя и кажется смелым, в данном случае является логической ошибкой.
Если все еще не убежден, JB Nizet приводит дополнительный и убедительный аргумент:
Если по какой-то причине вы ненавидите SLF4J API и используете его, вы потеряете удовольствие от своей работы, тогда непременно идите на июль. В конце концов, есть способы перенаправить джул на SLF4J .
Кстати, джул-параметризация, по крайней мере, в 10 раз медленнее, чем у SLF4J, что в итоге дает заметную разницу.
источник
java.util.logging
был введен в Java 1.4. До этого были случаи использования логирования, поэтому существует много других API логирования. Те API, которые интенсивно использовались до Java 1.4 и, следовательно, имели большой рыночный ресурс, который не просто упал до 0, когда был выпущен 1.4.JUL начинал не так уж и хорошо, многие из тех вещей, о которых вы упомянули, были намного хуже в 1.4 и стали лучше только в 1.5 (и я думаю, что и в 6, но я не слишком уверен).
JUL плохо подходит для нескольких приложений с разными конфигурациями в одной JVM (подумайте о нескольких веб-приложениях, которые не должны взаимодействовать). Tomcat должен перепрыгнуть через несколько обручей, чтобы заставить это работать (эффективно повторно внедряя JUL, если я правильно понял).
Вы не можете всегда влиять на то, какую среду ведения журналов используют ваши библиотеки. Поэтому использование SLF4J (который на самом деле является очень тонким слоем API по сравнению с другими библиотеками) помогает сохранить несколько непротиворечивую картину всего мира ведения журналов (так что вы можете определить базовую структуру ведения журналов, сохраняя при этом ведение журнала в той же системе).
Библиотеки не могут легко измениться. Если предыдущая версия библиотеки использовала logging-library-X, она не может легко переключиться на logging-library-Y (например, JUL), даже если последняя явно превосходна: любой пользователь этой библиотеки должен был бы изучить новая структура ведения журнала и (по крайней мере) перенастройка их ведения журнала. Это большое нет-нет, особенно когда это не приносит видимой выгоды большинству людей.
Сказав все это, я думаю, что JUL является, по крайней мере, допустимой альтернативой другим системам журналирования в наши дни.
источник
ИМХО, главное преимущество использования фасада ведения журнала, такого как slf4j, заключается в том, что вы позволяете конечному пользователю библиотеки выбирать, какую конкретную реализацию ведения журнала он хочет, вместо того, чтобы навязывать свой выбор конечному пользователю.
Возможно, он вложил время и деньги в Log4j или LogBack (специальные средства форматирования, приложения и т. Д.) И предпочитает продолжать использовать Log4j или LogBack, а не настраивать jul. Нет проблем: slf4j позволяет это. Это мудрый выбор, чтобы использовать Log4j над Джул? Может быть, а может и нет. Но тебе все равно. Позвольте конечному пользователю выбрать то, что он предпочитает.
источник
Я начал, как и вы, я подозреваю, использовать JUL, потому что это было легче всего начать немедленно. Однако за эти годы мне стало жаль, что я потратил немного больше времени на выбор.
Теперь моя главная проблема заключается в том, что у нас есть значительный объем библиотечного кода, который используется во многих приложениях, и все они используют JUL. Всякий раз, когда я использую эти инструменты в приложении типа веб-службы, регистрация просто исчезает или уходит в непредсказуемое или странное состояние.
Наше решение состояло в том, чтобы добавить фасад к библиотечному коду, который означал, что вызовы журнала библиотеки не изменились, а были динамически перенаправлены на любой доступный механизм ведения журнала. Когда они включены в инструмент POJO, они направляются в JUL, но при развертывании в виде веб-приложения они перенаправляются в LogBack.
Мы, конечно, сожалеем о том, что код библиотеки не использует параметризованное ведение журнала, но теперь его можно модифицировать по мере необходимости.
Мы использовали slf4j, чтобы построить фасад.
источник
Я запускал jul против slf4j-1.7.21 по logback-1.1.7, вывод на SSD, Java 1.8, Win64
Июль пробежал 48449 мс, выход из системы 27185 мс для цикла 1M.
Тем не менее, немного больше скорости и немного приятнее API не стоит 3 библиотеки и 800K для меня.
а также
источник
logger.info()
. Таким образом, вы намеренно наносите вред производительности jul, чтобы компенсировать недостаток интерфейса slf4j. Вместо этого вы должны кодировать оба метода так, как они идиоматически закодированы.