Comments 89
Спасибо, качественный разбор.
Обратная совместимость же, не могут (в 1.2.х во всяком случае)
Обратная совместимость здесь с Java 1.4 в которой нет varargs.
А если мне надо 20 параметров передать? 20 методов делать? Нафиг нафиг. Лучше уж что-нибудь типа info(String format, Object[] args) — но всё равно неудобно.
Да тут пол статьи о том, что вышло в результате принятия неудачного API, которое вошло в стандарт и породило кошмар. Пять методов(а на самом деле это ещё надо помножить на кол-во уровней логирования) делающих одно и то же только с разным кол-вом параметров — это костыль, а не решение. И в публичной библиотеке такое недопустимо.
Спорить совсем не хочется(хотя хотелось, но литр жигулевского сделал своё дело :) ). IMHO, самое главное в библиотеке — это её интерфейс, а для библиотек которыми пользуются тысячи разработчиков по всему миру тем более. Хороший API — библиотека имеет шансы. Плохой API — библиотека плоха, независимо от её реализации. А добавление кучи методов делающих по сути одно и тоже никак нельзя назвать хорошим интерфейсом.
Вполне можно, по-моему, если таким образом использовать ее становится удобно. Не вижу проблемы. Двадцать аргументы передать — во-первых, тут уж будьте добры, воспользуйтесь массивом, а во-вторых, вам правдо случалось двадцать аргументов в лог-строку передавать? Я имею в виду, не «а что если», а вот на самом деле, по-настоящему?
кстати slf4j так и делает
Жалко, что loj4j не развивается. По мне он достаточно удобный и простой. Да и как уже было сказано antelle, его портированная версия для .NET уже достаточно хорошо развилась (а еще и NLog, как его аналог). Поэтому мне хотелось бы дальнейшего развития log4j, вместо того, чтобы переходить на что-то другое.
Логгирование — «ахилесова пята» более половины проектов на Java. Зачастую некоторые IDE — типа Netbeans при генерации блоков (try/catch и т.п.) автоматически пуляют туда логгирование с помощью JUL и так оно и остается в коде, при не внимательности.
Да бросьте вы про невнимательность, вы что код совсем не читаете который среда генерит?
А Вы в команде не работали никогда? Когда потом подчищать код по всем классам.
Я не против авто генерации разных блоков, конструкторов и т.д., но не люблю когда среда навязывает какое то поведение.
И да, я это отключаю в в шаблонах генерации.
Я не против авто генерации разных блоков, конструкторов и т.д., но не люблю когда среда навязывает какое то поведение.
И да, я это отключаю в в шаблонах генерации.
Спасибо. Очень интересно было прочитать историю. В целом, любая компания, где разработчики не являются цельной командой эта история в миниатюре повторяется. В нашей компании в результате несколько лет назад все-таки признали log4j стандартом de-facto, и споры хотя бы относительно этой библиотеки прекратились. Да, есть масса ньюансов использования. Шишки набивались долго. Но в целом удобно и юзабельно
А я помню жуткие времена, когда все писали свои обертки. У каждой компании была своя обертка и свой log viewer. По-моему, это был где-то 2001 год.
Практически каждый разработчик участвовал в «изобретении велосипеда» :). Наиболее частые примеры — это своя реализация списка, вектора и системы логирования. На прежнем месте работы я попробовал «заменить» систему логирования разработанную и развиваемую одним из ведущих разработчиков на log4cxx.
Предложенный вариант снисходительно игнорировался и то, что в нем уже давно было реализовано и легко настраивалось через log4cxx.properties, изобреталось в собственной разработке снова и снова. Поэтому я, чтобы не создавать зоопарк разных реализаций библиотек логирования, отказался там от использования log4cxx. Но в остальных проектах где я принимаю архитектурные решения — использую log4cxx, log4j, log4net. Схожая реализация упрощает понимание применения библиотек логирования c разными языками программирования.
Предложенный вариант снисходительно игнорировался и то, что в нем уже давно было реализовано и легко настраивалось через log4cxx.properties, изобреталось в собственной разработке снова и снова. Поэтому я, чтобы не создавать зоопарк разных реализаций библиотек логирования, отказался там от использования log4cxx. Но в остальных проектах где я принимаю архитектурные решения — использую log4cxx, log4j, log4net. Схожая реализация упрощает понимание применения библиотек логирования c разными языками программирования.
Ну у нас один программист попался, который писал с нуля отправку mail по спецификациям, свой http клиент по спецификациям… о системе логирования уже молчу. Есть и человек, который не признавал ничего готового а все писал сам, потому что запоминать свое проще. У него есть либа метров на 10, которая ходит по его проектам.
А вообще, любой программист, при поверхностном изучении почти любого фреймворка, выбирает ряд типовых задач, которыми он будет пользоваться и старается написать оберточку вокруг этих задач, чтобы все делалось желательно одним методом и без ошибок вылетающих дальше по стеку.
А вообще, любой программист, при поверхностном изучении почти любого фреймворка, выбирает ряд типовых задач, которыми он будет пользоваться и старается написать оберточку вокруг этих задач, чтобы все делалось желательно одним методом и без ошибок вылетающих дальше по стеку.
Каждый программист за свою жизнь должен написать тетрис, систему логирования и XML парсер :)
торт!
Тут бы еще упомянуть довольно корявую реализацию логгирования в Eclipse (при разработке plugins или RCP Application). C одной стороны — вроде как есть уже что-то готовое и тянуть еще-одну-либу log4j нет смылса, с другой — ILog уж больно страшен.
Думаю, что зря. Что делать, если класс не управляется Guice?
я просто хочу использовать new MyObject(), в таком случае зачем мне сдался весь этот DI?
OMG!
Скорее это Factory. Описывается в документации log4j:
и, кстати, это работает ВЕЗДЕ.
public class MyApp {
// Define a static logger variable so that it references the
// Logger instance named "MyApp".
static Logger logger = Logger.getLogger(MyApp.class);
и, кстати, это работает ВЕЗДЕ.
По времени log4j был создан когда уже почти загибался проект Avalon с его дурацкими Loggable/LogEnabled.
И не делайте из еды культа :) С таким подходом мне как минимум придется писать следующее:
Это может быть и гибко, но делать это ВО ВСЕМ приложении весьма утомительно.
Кстати, мне интересно, что нам предложит DI, если мы захотим logger в статическом методе.
И не делайте из еды культа :) С таким подходом мне как минимум придется писать следующее:
myObj1 = new MyClass(Logger.getLogger("MyClass"));
myObj2 = new MyClass2(Logger.getLogger("MyClass2"));
Это может быть и гибко, но делать это ВО ВСЕМ приложении весьма утомительно.
Кстати, мне интересно, что нам предложит DI, если мы захотим logger в статическом методе.
>что бы было гибко и не утомительно
Правильно так: Чтобы было гибко И утомительно
Правильно так: Чтобы было гибко И утомительно
я уже написал, что не все классы используют DI.
если для того, что написать строчку в лог, я должен включать мой объект в контейнер DI и перестать использовать конструктор — это называется «утомительно».
если для того, что написать строчку в лог, я должен включать мой объект в контейнер DI и перестать использовать конструктор — это называется «утомительно».
Мда. Хабр — это, оказывается, так весело ))))))))
Слушайте, ну возьмите вы исходники какой-нибудь реальной библиотеки, например, Spring или HttpClient и посмотрите, использует ли там кто-нибудь DI. А также обратите внимание на статические методы.
Слушайте, ну возьмите вы исходники какой-нибудь реальной библиотеки, например, Spring или HttpClient и посмотрите, использует ли там кто-нибудь DI. А также обратите внимание на статические методы.
а зачем мне их сравнивать? у меня вот приложение содержит и низкоуровневые компоненты, и бизнес-логику, и статические методы.
вот вы бы сами попробовали написать реальную DI-конфигурацию для логгинга ну хотя бы для 5-10 классов, сами все сразу поймете
вот вы бы сами попробовали написать реальную DI-конфигурацию для логгинга ну хотя бы для 5-10 классов, сами все сразу поймете
Да нет, не аномалия )
Иногда появляются утилитные бизнес-методы этак на 2-7 строчек, и их не привяжешь к какому-либо объекту, т.к. это будет противоречить принципам ООП.
Вот и выделяются в static-методы утилитных классов. Их, конечно, можно пихать в DI, но делать из утилитного класса singleton только для пропихивания logger'а — ИМХО, некрасиво.
А насчет AOP… Ну, не все используют AOP. Из разных соображвений, но иногда полномочий не хватает, чтобы внедрить технологию, приходится использовать то, что есть, с полной отдачей.
Иногда появляются утилитные бизнес-методы этак на 2-7 строчек, и их не привяжешь к какому-либо объекту, т.к. это будет противоречить принципам ООП.
Вот и выделяются в static-методы утилитных классов. Их, конечно, можно пихать в DI, но делать из утилитного класса singleton только для пропихивания logger'а — ИМХО, некрасиво.
А насчет AOP… Ну, не все используют AOP. Из разных соображвений, но иногда полномочий не хватает, чтобы внедрить технологию, приходится использовать то, что есть, с полной отдачей.
Logger.getLogger(MyApp.class);
Имеет неприятный эффект его использование под некоторыми версиями tomcat может вести к тому что у вас не будут выгружаться из памяти классы старого war при горячем редеплое. т.к. логгер будет завязан на этот класс. Лучше писать
Logger.getLogger(MyApp.class.getName());
Имеет неприятный эффект его использование под некоторыми версиями tomcat может вести к тому что у вас не будут выгружаться из памяти классы старого war при горячем редеплое. т.к. логгер будет завязан на этот класс. Лучше писать
Logger.getLogger(MyApp.class.getName());
Спасибо, побольше бы таких статей.
Отлично написано!
Статья интересная, но вот что меня интересует — а почему JUL то умирает. Успешно использовал в куче проектов ни разу проблем не было. Чем он плох? Я совершенно серьезно спрашиваю.
там целый комплекс проблем.
одна из них навскиду — очень сложно апгрейдить, ведь JUL является частью платформы. В то время как log4j апгрейдится заменой JAR-файла.
одна из них навскиду — очень сложно апгрейдить, ведь JUL является частью платформы. В то время как log4j апгрейдится заменой JAR-файла.
м-м-м а зачем апгрейдить? 99% он вполне покрывает.
Мне в голову приходит пожалуй только невозможность сделать
if (log.isEnabled(DEBUG))
{
String txt = composeTimeConsumingLogInfo();
log.debug(txt);
}
Мне в голову приходит пожалуй только невозможность сделать
if (log.isEnabled(DEBUG))
{
String txt = composeTimeConsumingLogInfo();
log.debug(txt);
}
может быть миллион причин апгрейдить… наличие бага или уязвимости или какая-то новая фича.
это может быть и пустяк… но в целом слабую функциональность JUL мало что вылечит.
Например, попробуйте там найти Handler для записи в syslog. Его до сих пор нет. А в log4j он присутствует где-то с 2000 года.
это может быть и пустяк… но в целом слабую функциональность JUL мало что вылечит.
Например, попробуйте там найти Handler для записи в syslog. Его до сих пор нет. А в log4j он присутствует где-то с 2000 года.
Ну так получилось что в syslog мне писать не надо. А если надо то я пожалуй сам могу с log4J содрать.
Я тут наоборот всех своих практикантов ругаю, говорю берите JUL
1. Он уже есть, не увеличивайте сложность сверх необходимого
2. Он простой, у вас будет меньше шансов наколбасить что-то сложное.
Я тут наоборот всех своих практикантов ругаю, говорю берите JUL
1. Он уже есть, не увеличивайте сложность сверх необходимого
2. Он простой, у вас будет меньше шансов наколбасить что-то сложное.
ну для практикантов может быть JUL действительно в чем-то лучше.
вообще то, что он «уже есть» наверное, единственный аргумент для его использования
вообще то, что он «уже есть» наверное, единственный аргумент для его использования
Ну основной point (см пост ниже) в 99% случаев сложное не надо а JUL для простых случаев более чем подходит.
Самое прикольное для serverside быстрота тоже особенно не важна. т.к. на 100% загрузить процессор все равно не реально.
Из продвинутых вещей мне бы было нужен logbag но тащить его если честно не хочется ибо игра не будет стоить свеч
(http://www.theserverside.com/news/thread.tss?thread_id=42243 logBag thread)
Самое прикольное для serverside быстрота тоже особенно не важна. т.к. на 100% загрузить процессор все равно не реально.
Из продвинутых вещей мне бы было нужен logbag но тащить его если честно не хочется ибо игра не будет стоить свеч
(http://www.theserverside.com/news/thread.tss?thread_id=42243 logBag thread)
Самое прикольное для serverside быстрота тоже особенно не важна. т.к. на 100% загрузить процессор все равно не реально.
logger.info(«x = {}», new Object[]{String.valueOf(x)});
logger.info(«x = {}», new Object[]{String.valueOf(x)});
… чё сказать то хотел...:
— Самое прикольное для serverside быстрота тоже особенно не важна. т.к. на 100% загрузить процессор все равно не реально.
—
использование логинга из примера
— logger.info(«x = {}», new Object[]{String.valueOf(x)});
—
может не только процессор на 100% но и память ВСЮ отожрать за несколько минут работы сервера, рождая миллионы обьектов.
ЗЫ ..............................+________________
*а что у меня форматирование/html-теги не работают совсем !?
* FireFox + Ubuntu >8-E
*ASCII-artom как то не круто получается
..............................+___________________
— Самое прикольное для serverside быстрота тоже особенно не важна. т.к. на 100% загрузить процессор все равно не реально.
—
использование логинга из примера
— logger.info(«x = {}», new Object[]{String.valueOf(x)});
—
может не только процессор на 100% но и память ВСЮ отожрать за несколько минут работы сервера, рождая миллионы обьектов.
ЗЫ ..............................+________________
*а что у меня форматирование/html-теги не работают совсем !?
* FireFox + Ubuntu >8-E
*ASCII-artom как то не круто получается
..............................+___________________
public abstract class Debug
{
public static final void _trace ( final String text )
{
System.out.println ( "Trace: " + text );
}
}
На этапе сборки релизной версии препроцессор (писанный на коленке) попросту выкидывает из кода куски от «Debug._trace (» и до соответствующей закрывающей скобки. Я не претендую на то, чтобы это было эталонным подходом к логированю, но мне этого (чуть понавороченнее, я тут только суть указал) пока хватает за глаза.Коллеги а никто не помнит как назывался логгер который умел logBags. Было это примерно так
Message msg = receiveMessage
LogBag bag = log.getBag(msg.phoneNumber);
дальше я препарирую message логируя с уровнем debug и соответственно в лог это не пишется
log.debug(bag,«message body\n»+msg.dumpBytes());
и так далее
НО если возникла ошибка
log.error(bag,«фигня случилась»);
то уже логируется все что относилось к этому bag включая debug
Message msg = receiveMessage
LogBag bag = log.getBag(msg.phoneNumber);
дальше я препарирую message логируя с уровнем debug и соответственно в лог это не пишется
log.debug(bag,«message body\n»+msg.dumpBytes());
и так далее
НО если возникла ошибка
log.error(bag,«фигня случилась»);
то уже логируется все что относилось к этому bag включая debug
на codegeeks тоже была интересная статья про логирование
А еще есть хороший обзор с примерами у skipy.
Большое спасибо. Замечательная статья. Жаль, нет кармы, плюсанул бы.
C SLF4J тоже не все сладко. Одно время я практиковал встраивание Jetty в собственное приложение. Приложение могло работать под управлением внешнего сервера Jetty/Resin/Tomcat, а могло и само обслуживать HTTP-запросы. Это очень удобно в некоторых случаях.
Но тут выяснилось, что если моё приложение использует SLF4J, а при этом еще из приложения запускается Jetty, который тоже использует SLF4J, то я лишаюсь журналирования. Даже если я не использую SLF4J напрямую, он может быть задействован в одной из библиотек, например Hibernate, и я таки опять лишаюсь журналирования.
Причину, помнится, я более-менее понял, но так и не понял, кого именно винить: Jetty за его ClassLoader-ы или SLF4J, который оказался недостаточно гибким. Обе точки зрения имеют право на жизнь, а я на всякий случай откатился в стан почитателей Log4J.
Но тут выяснилось, что если моё приложение использует SLF4J, а при этом еще из приложения запускается Jetty, который тоже использует SLF4J, то я лишаюсь журналирования. Даже если я не использую SLF4J напрямую, он может быть задействован в одной из библиотек, например Hibernate, и я таки опять лишаюсь журналирования.
Причину, помнится, я более-менее понял, но так и не понял, кого именно винить: Jetty за его ClassLoader-ы или SLF4J, который оказался недостаточно гибким. Обе точки зрения имеют право на жизнь, а я на всякий случай откатился в стан почитателей Log4J.
да, есть такое — если кто-то подложил свой implementation jar, то он перекрывает все остальное
видимо в jetty не очень разобрались с зависимостями
видимо в jetty не очень разобрались с зависимостями
Там проблема в том, что Jetty строит для запускаемого контекста classpath, в который адаптер SLF4J-Log4J включён дважды. А архитектура SLF4J не позволяет такие трюки, в результате «Class path contains multiple SLF4J bindings». С одной стороный, косяк на стороне Jetty, с другой — SLF4J мог бы быть и подружественнее к такого рода проблемам.
Сегодня с удивлением наткнулся еще на одну проблему. Приложение использует Hibernate, а тот использует SLF4J. При попытке запуска приложения под Resin 4.0.14 сервер на смог подхватить SLF4J и Log4J из каталога WEB-INF. При переносе этих библиотек в [Resin]/lib все запустилось. В Resin 3.1.10 все запускается без проблем. Детально разбираться не стал. По всей видимости, косяк в Resin, в котором SLF4J то добавляют, то убирают.
Сегодня с удивлением наткнулся еще на одну проблему. Приложение использует Hibernate, а тот использует SLF4J. При попытке запуска приложения под Resin 4.0.14 сервер на смог подхватить SLF4J и Log4J из каталога WEB-INF. При переносе этих библиотек в [Resin]/lib все запустилось. В Resin 3.1.10 все запускается без проблем. Детально разбираться не стал. По всей видимости, косяк в Resin, в котором SLF4J то добавляют, то убирают.
jetty разве использует log4j? мне казалось, что там logback
что касается Resin, то видимо где-то в рутовых classloader-ах лежит slf4j. что вообще необычно, т.к. Resin очень любит JUL.
что касается Resin, то видимо где-то в рутовых classloader-ах лежит slf4j. что вообще необычно, т.к. Resin очень любит JUL.
Что касается Jetty, я наверное не очень ясно выразился. Подробно проблема описана здесь:
www.rsdn.ru/forum/java/3934217.flat.aspx
А по поводу Resin в одном из issue, касающемся Resin 4.0.12 я нашел такой коммент:
slf4j removed from distribution, since Resin does not depend on it.
Из чего и сделал вывод, что Resin пытались скрестить c SLF4J.
Надо ли говорить, что всё это очень сильно действует на нервы. Платформа Java имеет некоторое количество проблем, из которых Logging — самая серьёзная. Так что правильная тема в статье поднята.
www.rsdn.ru/forum/java/3934217.flat.aspx
А по поводу Resin в одном из issue, касающемся Resin 4.0.12 я нашел такой коммент:
slf4j removed from distribution, since Resin does not depend on it.
Из чего и сделал вывод, что Resin пытались скрестить c SLF4J.
Надо ли говорить, что всё это очень сильно действует на нервы. Платформа Java имеет некоторое количество проблем, из которых Logging — самая серьёзная. Так что правильная тема в статье поднята.
непонятно причем тут опенсорс сообщества, упоминаемое на каждом шагу
отлично, всё здорово разобрано.
Огромное спасибо за статью. Уже очень долгое время:
— использую Log4j,
— матерю java-util-logging,
— тихо ненавижу commons-logging
— побаиваюсь SLF4J.
После прочтения статьи появилось желание попробовать logback. Что и сделаю в ближайшее время.
— использую Log4j,
— матерю java-util-logging,
— тихо ненавижу commons-logging
— побаиваюсь SLF4J.
После прочтения статьи появилось желание попробовать logback. Что и сделаю в ближайшее время.
Про logback не указана, на мой взгляд, важная особенность. Он нативно реализует SLF4J API (команда/спонсор судя по всему одна и та же — QOS.ch). А это значит, что используя «православную» связку SLF4J + logback, обёрток как бы и нет.
Согласен с WFrog, кроме того SLF4J — просто кучка интерфейсов, Logback — просто реализация (нативная), поэтому ставить их в один ряд немного неправильно. Вообще мне больше всего нравится через slf4j и его адаптеры перенаправлять все в Logback, и в своем проекте использовать slf4j. Самый народный вариант и все зависимости логируют свой злам единообразно.
Спасибо за отличную статью!
бесконечный «reinvent the wheel». То есть из двух вариантов «доработать существующее» и «сделать свое» всегда выбирался второй.«Существующее» нередко имеется в виде, уже распухшем почти до уровня мини-ОС, так что в таких случаях «сделать своё» — это ещё и тупо быстрее. К тому же «дорабатывать» — это не брать готовое, по трудозатратам вполне сопоставимо со «сделать своё», и при этом зависеть от выпуска новых версий (и изменения политики лицензирования) чужого продукта.
Нельзя сказать, чтобы JSR47 проигрывал в производительности. Местами он обгонял log4j за счет поддержания в памяти специального представления своей конфигурации (что, кстати, одновременно усложняло эту самую конфигурацию). Однако, как выяснилось, JSR47 в обязательном порядке собирал так называемую Caller Information, то бишь «откуда логгируется данное сообщение». Получение Caller Information — операция довольно дорогостоящая, протекает она с использованием Native-кода. Опытные дяди из log4j это знали, поэтому предоставляли эту возможность с оговоркой «лучше не включайте».
Может я не понимаю, но разве это нельзя получить через new Exception().getStackTrace()?
1. На мой взгляд наиболее важное преимущество любой сторонней библиотеки логгирования перед JUL- возможность для каждого задеплоенного модуля в контейнере иметь свой собственный лог-файл, а не валить все в одну кучу.
2. Пользую slf4j с адаптерами. При этом если модуль подтягивает зашаренную библиотеку контейнера, то эти (общие) классы или не имеют реализации slf4j- тогда весь логгинг у них теряется, либо, если подложить реализацию в общие библиотеки, будет двойной биндинг. В любом случае логгинг зашаренных классов по модулям уже корректно не растащить. Может кто подскажет решение? (не считая решением тащить все библиотеки с собой)
3. Пользую таки свою обертку с такими вспомогательными вещами как:
2. Пользую slf4j с адаптерами. При этом если модуль подтягивает зашаренную библиотеку контейнера, то эти (общие) классы или не имеют реализации slf4j- тогда весь логгинг у них теряется, либо, если подложить реализацию в общие библиотеки, будет двойной биндинг. В любом случае логгинг зашаренных классов по модулям уже корректно не растащить. Может кто подскажет решение? (не считая решением тащить все библиотеки с собой)
3. Пользую таки свою обертку с такими вспомогательными вещами как:
public IllegalStateException getIllegalStateException(String msg, @Nullable Throwable e) {...
logger.error(msg, e);
return new IllegalStateException(msg, e);
}
public IllegalArgumentException getIllegalArgumentException(...
...
public UserSecurityException getSecurityException(String msg, String user) {
Прекрасная статья, спасибо!
Sign up to leave a comment.
Java Logging: история кошмара