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

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

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

Поскольку я родом из JUL, я предвзято отношусь к этому. Мои познания в остальном не так велики.

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

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

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

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

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

  5. «Все остальные используют ведение журнала -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. Конфигурация из пути к классам . Из -из -поле -JUL не может загрузить файл конфигурации из пути к классам. Для этого нужно несколько строк кода . Я понимаю, почему это может раздражать, но решение короткое и простое.

  3. Наличие обработчиков вывода . JUL поставляется с 5 обработчиками вывода из -из -консоли -box :, файлового потока, сокета и памяти. Они могут быть расширены или могут быть написаны новые. Например, это может быть запись в системный журнал 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, бэкенд Logback :1515
  • Время выполнения SLF4J, серверная часть JUL :12938
  • Время выполнения JUL :16911

Цифры выше указаны в миллисекундах, поэтому чем меньше, тем лучше. Таким образом, разница в производительности в 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 была «прогрета» одним и тем же тестом, выполненным один раз, прежде чем было выполнено фактическое измерение. В Windows 7 использовалась Java 1.7.03. Использовались последние версии 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 (slow )и SLF4J+Logback (quick составляет примерно 10 раз. ). Как сказал Чеки.

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

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

(раздел добавлен nolan600 8 -июля -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 верно обратное... а в прошлом он был равен нулю, потому что его не существовало.

331
задан peterh 22 April 2019 в 10:50
поделиться