Вперше в своєму житті я опиняюся в положенні, коли я пишу API Java, який буде відкритий. Сподіваємось, що буде включено до багатьох інших проектів.
Для ведення журналу я (і справді люди, з якими я працюю) завжди використовував JUL (java.util.logging) і ніколи не мав з цим жодних проблем. Однак зараз мені потрібно більш детально зрозуміти, що мені робити для розробки API. Я провів кілька досліджень з цього приводу, і отриману інформацію я просто заплутався. Звідси ця посада.
Оскільки я родом з JUL, я упереджений цим. Мої знання про решту не такі великі.
З результатів проведених досліджень я прийшов до таких причин, чому люди не люблять JUL:
"Я почав розвиватися на Java задовго до того, як Sun випустила JUL, і мені було просто легше продовжувати лог-фреймворк-X, а не вивчати щось нове" . Хм. Я не жартую, це насправді те, що кажуть люди. З цим аргументом ми все могли б робити COBOL. (однак я, безумовно, можу ставитись до того, що я сам лінивий чувак)
"Мені не подобаються назви рівнів журналу в JUL" . Гаразд, серйозно, це просто недостатньо підстав для введення нової залежності.
"Мені не подобається стандартний формат виводу з JUL" . Хм. Це лише конфігурація. Вам навіть не потрібно робити щось кодове. (правда, за старих часів вам, можливо, довелося створити свій власний клас Форматтер, щоб правильно його встановити).
"Я використовую інші бібліотеки, які також використовують logging-Framework-X, тому я вважав, що простіше просто використовувати цю" . Це круговий аргумент, чи не так? Чому "всі" використовують logging-Framework-X, а не JUL?
"Усі інші використовують logging-Framework-X" . Це для мене лише окремий випадок із вищезазначеного. Більшість не завжди права.
Тож справді велике питання - чому б не JUL? . Що це я пропустив? Основна причина для фасадів журналів (SLF4J, JCL) полягає в тому, що кілька реалізацій журналів існували історично, і причина цього насправді повертається до епохи до ЮЛ, як я бачу. Якби 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 не може завантажити конфігураційний файл з classpath. Це зробити кілька рядків коду . Я бачу, чому це може дратувати, але рішення коротке та просте.
Наявність вихідних обробників . JUL поставляється з 5 вихідними обробниками поза коробкою: консоль, потік файлів, сокет і пам'ять. Вони можуть бути розширені або записані нові. Наприклад, це написання в UNIX / Linux Syslog та Журнал подій Windows. У мене особисто ніколи не було цієї вимоги, ні я не бачив, щоб вона використовувалася, але я, безумовно, можу сказати, чому це може бути корисною функцією. Зворотний зв'язок постачається разом із додатком для Syslog. Все-таки я б це заперечував
- 99,5% потреб у вихідних напрямках покриваються тим, що знаходиться в JUL поза коробкою.
- Спеціальні потреби можуть задовольняти спеціальні обробники на JUL, а не щось інше. Мені нічого не говорить про те, що для написання вихідного обробника Syslog для JUL потрібно більше часу, ніж це стосується іншої структури журналу.
Я дуже стурбований тим, що є щось, що я не помітив. Використання фасадів ведення журналів та інших журналів, крім JUL, настільки поширене, що я мушу прийти до висновку, що це я просто не розумію. Боюся, це було б не перший раз. :-)
То що мені робити зі своїм API? Я хочу, щоб це стало успішним. Звичайно, я можу просто "піти з потоку" та впровадити SLF4J (який здається найпопулярнішим в наші дні), але заради мене все одно потрібно зрозуміти, що не так з JUL сьогодні, що вимагає всіх нечітких дій? Чи буду я саботажу, вибравши JUL для своєї бібліотеки?
Тестування продуктивності
(розділ додано nolan600 07 липня 2012 р.)
Нижче наведено посилання від Ceki про параметризацію SLF4J, яка в 10 разів і швидше, ніж у JUL. Тому я почав робити кілька простих тестів. На перший погляд твердження, безумовно, правильне. Ось попередні результати (але читайте далі!):
- Час виконання SLF4J, резервний зворотний зв'язок: 1515
- Час виконання SLF4J, бекенд 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 + Зворотний зв'язок все одно вийде на вершину, але далеко від початкових цифр, як зазначено вище. Слідкуйте за налаштуваннями.
Btw: Тест був вперше, коли я фактично працював зі 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
знаходиться трохи вище планки, в той час як навпаки справедливо для сьогоднішнього липня ..., а в минулому він входив до нуля, оскільки його не існувало.
java.lang.System.Logger
, що є інтерфейсом , який можна перенаправляти на будь-яку фактичну рамку реєстрації, яку ви хочете, доки ця рамка наздогнала і забезпечила реалізацію цього інтерфейсу. У поєднанні з модулярізацією ви навіть можете розгорнути додаток із пакетним JRE, що не містить java.util.logging
, якщо ви віддаєте перевагу інший фреймворк.