Впервые в жизни я оказался в положении, когда я пишу Java API, который будет открыт. Надеемся, что мы будем включены во многие другие проекты.
Для ведения журнала я (и действительно люди, с которыми я работаю) всегда использовал JUL (java.util.logging) и никогда не имел с ним никаких проблем. Однако теперь мне нужно более подробно понять, что я должен сделать для разработки моего API. Я провел некоторое исследование по этому вопросу и с информацией, которую у меня есть, я просто больше смущен. Отсюда этот пост.
Поскольку я родом из JUL, я склонен к этому. Мои знания об остальном не так уж велики.
Из исследования, которое я сделал, я придумал эти причины, почему люди не любят JUL:
-
"Я начал развиваться в Java задолго до того, как Sun выпустил JUL, и мне было проще продолжить работу с logging-framework-X, а не изучать что-то новое". Хм. Я не шучу, это на самом деле то, что люди говорят. С этим аргументом мы все могли бы сделать COBOL. (однако я, безусловно, могу сказать, что это тоже ленивый чувак)
-
"Мне не нравятся имена уровней ведения журнала в JUL". Хорошо, серьезно, этого просто недостаточно, чтобы представить новую зависимость.
-
"Мне не нравится стандартный формат вывода из JUL". Хм. Это просто конфигурация. Вы даже не должны делать что-то по коду. (правда, еще в старые времена вам, возможно, пришлось создать свой собственный класс Formatter, чтобы получить его право).
-
"Я использую другие библиотеки, которые также используют logging-framework-X, поэтому я подумал, что просто просто использовать этот". Это циклический аргумент, не так ли? Почему "все" используют logging-framework-X, а не JUL?
-
"Все остальные используют logging-framework-X". Это для меня просто особый случай из вышеперечисленного. Большинство не всегда правильно.
Итак, реальный большой вопрос: почему не JUL?. Что я пропустил? Raison d'être для протоколирования фасадов (SLF4J, JCL) заключается в том, что множество реализаций регистрации существовали исторически, и причина этого действительно восходит к эпохе до JUL, как я ее вижу. Если бы JUL был идеален, то каротажные фасады не существовали бы или что? Вместо того, чтобы обнимать их, мы не должны ставить под вопрос, почему они необходимы в первую очередь? (и посмотрите, сохранились ли эти причины)
Хорошо, мои исследования до сих пор привели к двум вещам, которые, как я могу видеть, могут быть реальными проблемами с помощью 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 не соответствует по сравнению с конкурентами.
-
Конфигурация из пути к классам. Из-за коробки JUL не удается загрузить файл конфигурации из пути к классам. Это несколько строк кода, чтобы сделать это. Я могу понять, почему это может раздражать, но решение короткое и простое.
-
Доступность обработчиков вывода. JUL поставляется с 5 выходными обработчиками из коробки: консоль, файловый поток, сокет и память. Они могут быть расширены или могут быть записаны новые. Это может быть, например, запись в Syslog UNIX/Linux и журнал событий Windows. У меня лично никогда не было этого требования, и я не видел его, но я, безусловно, могу сказать, почему это может быть полезной функцией. Например, в журнале регистрации есть приложение для Syslog. Тем не менее я бы сказал, что
- 99,5% потребностей в адресах вывода покрываются тем, что находится в JUL из коробки.
- Особые потребности могут обслуживаться пользовательскими обработчиками поверх JUL, а не поверх чего-то другого. Мне ничего не кажется, что требуется больше времени для написания обработчика вывода Syslog для JUL, чем для другой структуры ведения журнала.
Я действительно обеспокоен тем, что кое-что я забыл. Использование журнальных фасадов и реализаций каротажа, кроме JUL, настолько широко распространено, что я должен прийти к выводу, что это я, который просто не понимает. Боюсь, это будет не первый раз.: -)
Итак, что мне делать с моим API? Я хочу, чтобы он стал успешным. Я могу, конечно, просто "пойти с потоком" и реализовать SLF4J (который кажется самым популярным в наши дни), но для меня самого я все равно должен точно понять, что не так с JUL сегодняшнего дня, который гарантирует весь пух? Смогу ли я саботировать себя, выбрав JUL для своей библиотеки?
Производительность тестирования
(раздел, добавленный nolan600 07-JUL-2012)
Здесь приведена ссылка ниже от Ceki о параметризации SLF4J в 10 раз или быстрее, чем у JUL. Поэтому я начал делать простые тесты. На первый взгляд претензия, безусловно, правильная. Вот предварительные результаты (но читайте дальше!):
- Время выполнения SLF4J, backend Logback: 1515
- Время выполнения SLF4J, backend JUL: 12938
- Время выполнения JUL: 16911
Цифры выше - это msecs, поэтому лучше. Таким образом, разница в производительности в 10 раз поначалу на самом деле довольно близка. Моя первоначальная реакция: Это очень много!
Вот ядро теста. Как видно, целое число и строка строятся в цикле, который затем используется в операторе журнала:
for (int i = 0; i < noOfExecutions; i++) {
for (char x=32; x<88; x++) {
String someString = Character.toString(x);
// here we 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 все равно будет отображаться сверху, но далеко не так, как указано выше. Оставайтесь с нами.
Btw: Тест был первый раз, когда я работал с SLF4J или Logback. Приятный опыт. JUL, безусловно, гораздо менее приветствуется, когда вы начинаете.
Производительность тестирования (часть 2)
(раздел, добавленный nolan600 от 08-JUL-2012)
Как оказалось, для производительности не имеет большого значения, как вы настраиваете свой шаблон в JUL, то есть, включает ли оно имя источника или нет. Я пробовал с очень простым шаблоном:
java.util.logging.SimpleFormatter.format="%4$s: %5$s [%1$tc]%n"
и это не изменило приведенные выше тайминги. Мой профилировщик показал, что регистратор все еще проводил много времени при вызовах getSourceClassName()
, даже если это не было частью моего шаблона. Шаблон не имеет значения.
Поэтому я делаю вывод о производительности, которая, по крайней мере, для тестового заявления на основе шаблонов на основе шаблонов, по-видимому, составляет примерно 10 раз в реальной разнице в производительности между JUL (медленным) и SLF4J + Logback (быстрый). Так же, как сказал Чеки.
Я также вижу, что вызов SLF4J getLogger()
намного дороже, чем JUL. (95 мс против 0,3 мс, если мой профилировщик является точным). Это имеет смысл. SLF4J должен сделать некоторое время на привязке основной реализации каротажа. Это меня не пугает. Эти вызовы должны быть несколько редкими в течение срока действия приложения. Быстрота должна быть в фактических вызовах журнала.
Окончательное заключение
(раздел, добавленный nolan600 от 08-JUL-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 сегодняшнего дня... и в прошлом он пришел на ноль, потому что его не было.