Впервые в жизни я нахожусь в положении, когда я пишу 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 сегодня все наоборот ... и в прошлом он пришел в ноль, потому что его не было.
java.lang.System.Logger
, представляющего собой интерфейс , который можно перенаправить на любую реальную инфраструктуру ведения журналов, которую вы хотите, при условии, что эта среда догоняет и обеспечивает реализацию этого интерфейса. В сочетании с модульностью вы можете даже развернуть приложение с JRE, не содержащим пакет java.util.logging
, если вы предпочитаете другую среду.