Почему бы не использовать Java.утиль.лесозаготовки?


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

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

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

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

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

  2. "мне не нравятся названия уровней регистрации в июле". Хорошо, серьезно, это просто не достаточно оснований для введения новой зависимости.

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

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

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

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

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

  1. производительность. Некоторые говорят, что производительность в SLF4J превосходит остальные. Мне кажется, что это случай преждевременной оптимизации. Если вам нужно регистрировать сотни мегабайт в секунду, то я не уверен, что вы на правильном пути в любом случае. ИЮЛЬ также эволюционировал, и тесты, которые вы делали на Java 1.4, больше не могут быть истинными. Вы можете прочитать об этом здесь и это исправление превратило его в Java 7. Многие также говорят о накладных расходах на конкатенацию строк в методах ведения журнала. Однако ведение журнала на основе шаблона позволяет избежать этой стоимости, и она существует также В июле. Лично я никогда не пишу журнал на основе шаблонов. Слишком ленив для этого. Например, если я сделаю это с 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.

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

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

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

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

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

так что же мне делать с моим API? Я хочу, чтобы это стало успешный. Я могу, конечно, просто "пойти с потоком" и реализовать SLF4J (который кажется самым популярным в эти дни), но для меня самого мне все еще нужно понять, что именно не так с сегодняшним днем, который гарантирует весь пух? Буду ли я саботировать себя, выбирая Джул для своей библиотеки ?

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

(раздел добавлен nolan600 на 07-JUL-2012)

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

  • время выполнения SLF4J, backend Logback: 1515
  • время выполнения SLF4J, backend 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), так и более сложный тип данных (в данном случае строка). Не уверен, что это имеет значение, но там у вас есть.)

оператор log для SLF4J:

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

заявление в журнале Джул:

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 были перенаправлены на нулевое устройство.

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

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

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

(раздел добавлен nolan600 на 08-JUL-2012)

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

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

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

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

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

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

(раздел добавлен nolan600 на 08-JUL-2012)

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

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

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

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

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

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

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

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

5 277

5 ответов:

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

есть объективные причины предпочитая SLF4J. Например, это дает конечному пользователю возможность выбирать базовый лесозаготовки. Кроме того, более опытные пользователи, как правило, предпочитают logback, который предлагает возможности за пределами log4j, С J.у.я падаю далеко позади. Функциональный j. u. l может быть достаточным для некоторых пользователей, но для многих других это просто не так. в двух словах, если ведение журнала важно для вас, вы хотели бы использовать SLF4J с logback в качестве базовой реализации. Если ведение журнала не имеет значения, j.u.l в порядке.

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

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

таким образом, удержание "твердых фактов" над общественным мнением, хотя и кажется смелым, является логическим заблуждением в этом случай.

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

за исключением того, что конечный пользователь уже мог сделать эту настройку для своего собственный код или другая библиотека, которая использует log4j или logback. Дж.у.л расширяемый, но имеющий возможность расширять logback, j. u. l, log4j и только Бог знает, какая другая структура ведения журнала, потому что он использует четыре библиотеки, которые использование четырех различных структур ведения журнала является громоздким. С помощью SLF4J, вы позвольте ему настроить рамки ведения журнала, которые он хочет, а не тот вы выбрали. помните, что типичный проект использует множество библиотеки, и не только ваши.

Если по какой-то причине вы ненавидите SLF4J API и с его помощью будет гасить удовольствие от вашей работы, то во что бы то ни стало перейти на j.u.l. В конце концов, есть средства перенаправить j. u. l в SLF4J.

кстати, параметризация j.u.l как минимум в 10 раз медленнее чем SLF4J, который в конечном итоге делает заметную разницу.

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

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

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

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

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

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

IMHO, основное преимущество в использовании фасада ведения журнала, такого как 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

jul ran 48449 ms, logback 27185 ms для цикла 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);
    }

}