Как стать автором
Обновить

Комментарии 89

Спасибо, качественный разбор.
В log4net (порте log4j) нет проблемы в вызовами IsDebugEnabled. Странно, что они до сих пор не реализовали DebugFormat и в log4j
Обратная совместимость же, не могут (в 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 разными языками программирования.
Ну у нас один программист попался, который писал с нуля отправку mail по спецификациям, свой http клиент по спецификациям… о системе логирования уже молчу. Есть и человек, который не признавал ничего готового а все писал сам, потому что запоминать свое проще. У него есть либа метров на 10, которая ходит по его проектам.
А вообще, любой программист, при поверхностном изучении почти любого фреймворка, выбирает ряд типовых задач, которыми он будет пользоваться и старается написать оберточку вокруг этих задач, чтобы все делалось желательно одним методом и без ошибок вылетающих дальше по стеку.
Каждый программист за свою жизнь должен написать тетрис, систему логирования и XML парсер :)
А ещё систему веб-шаблонов
0 из 3 :-) видимо, жить мне еще предстоит до-олго )
Тут бы еще упомянуть довольно корявую реализацию логгирования в 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.

И не делайте из еды культа :) С таким подходом мне как минимум придется писать следующее:
myObj1 = new MyClass(Logger.getLogger("MyClass"));
myObj2 = new MyClass2(Logger.getLogger("MyClass2"));


Это может быть и гибко, но делать это ВО ВСЕМ приложении весьма утомительно.

Кстати, мне интересно, что нам предложит DI, если мы захотим logger в статическом методе.
НЛО прилетело и опубликовало эту надпись здесь
>что бы было гибко и не утомительно
Правильно так: Чтобы было гибко И утомительно
НЛО прилетело и опубликовало эту надпись здесь
я уже написал, что не все классы используют DI.
если для того, что написать строчку в лог, я должен включать мой объект в контейнер DI и перестать использовать конструктор — это называется «утомительно».
НЛО прилетело и опубликовало эту надпись здесь
Мда. Хабр — это, оказывается, так весело ))))))))
Слушайте, ну возьмите вы исходники какой-нибудь реальной библиотеки, например, Spring или HttpClient и посмотрите, использует ли там кто-нибудь DI. А также обратите внимание на статические методы.
НЛО прилетело и опубликовало эту надпись здесь
а зачем мне их сравнивать? у меня вот приложение содержит и низкоуровневые компоненты, и бизнес-логику, и статические методы.

вот вы бы сами попробовали написать реальную DI-конфигурацию для логгинга ну хотя бы для 5-10 классов, сами все сразу поймете
НЛО прилетело и опубликовало эту надпись здесь
Да нет, не аномалия )

Иногда появляются утилитные бизнес-методы этак на 2-7 строчек, и их не привяжешь к какому-либо объекту, т.к. это будет противоречить принципам ООП.

Вот и выделяются в static-методы утилитных классов. Их, конечно, можно пихать в DI, но делать из утилитного класса singleton только для пропихивания logger'а — ИМХО, некрасиво.

А насчет AOP… Ну, не все используют AOP. Из разных соображвений, но иногда полномочий не хватает, чтобы внедрить технологию, приходится использовать то, что есть, с полной отдачей.
НЛО прилетело и опубликовало эту надпись здесь
Logger.getLogger(MyApp.class);

Имеет неприятный эффект его использование под некоторыми версиями tomcat может вести к тому что у вас не будут выгружаться из памяти классы старого war при горячем редеплое. т.к. логгер будет завязан на этот класс. Лучше писать

Logger.getLogger(MyApp.class.getName());
Спасибо, побольше бы таких статей.
НЛО прилетело и опубликовало эту надпись здесь
Статья интересная, но вот что меня интересует — а почему JUL то умирает. Успешно использовал в куче проектов ни разу проблем не было. Чем он плох? Я совершенно серьезно спрашиваю.
там целый комплекс проблем.
одна из них навскиду — очень сложно апгрейдить, ведь JUL является частью платформы. В то время как log4j апгрейдится заменой JAR-файла.
м-м-м а зачем апгрейдить? 99% он вполне покрывает.
Мне в голову приходит пожалуй только невозможность сделать
if (log.isEnabled(DEBUG))
{
String txt = composeTimeConsumingLogInfo();
log.debug(txt);
}
может быть миллион причин апгрейдить… наличие бага или уязвимости или какая-то новая фича.
это может быть и пустяк… но в целом слабую функциональность JUL мало что вылечит.

Например, попробуйте там найти Handler для записи в syslog. Его до сих пор нет. А в log4j он присутствует где-то с 2000 года.
Ну так получилось что в syslog мне писать не надо. А если надо то я пожалуй сам могу с log4J содрать.
Я тут наоборот всех своих практикантов ругаю, говорю берите JUL
1. Он уже есть, не увеличивайте сложность сверх необходимого
2. Он простой, у вас будет меньше шансов наколбасить что-то сложное.
ну для практикантов может быть JUL действительно в чем-то лучше.
вообще то, что он «уже есть» наверное, единственный аргумент для его использования
Ну основной point (см пост ниже) в 99% случаев сложное не надо а JUL для простых случаев более чем подходит.
Самое прикольное для 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)});
… чё сказать то хотел...:

— Самое прикольное для 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 (» и до соответствующей закрывающей скобки. Я не претендую на то, чтобы это было эталонным подходом к логированю, но мне этого (чуть понавороченнее, я тут только суть указал) пока хватает за глаза.
Увы при разработки логи мне например нужны в занчительно меньшей степени чем в production.
да и возможность раскидывать по нескольким файлам очень важна
Коллеги а никто не помнит как назывался логгер который умел 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
на codegeeks тоже была интересная статья про логирование
Большое спасибо. Замечательная статья. Жаль, нет кармы, плюсанул бы.
C SLF4J тоже не все сладко. Одно время я практиковал встраивание Jetty в собственное приложение. Приложение могло работать под управлением внешнего сервера Jetty/Resin/Tomcat, а могло и само обслуживать HTTP-запросы. Это очень удобно в некоторых случаях.

Но тут выяснилось, что если моё приложение использует SLF4J, а при этом еще из приложения запускается Jetty, который тоже использует SLF4J, то я лишаюсь журналирования. Даже если я не использую SLF4J напрямую, он может быть задействован в одной из библиотек, например Hibernate, и я таки опять лишаюсь журналирования.

Причину, помнится, я более-менее понял, но так и не понял, кого именно винить: Jetty за его ClassLoader-ы или SLF4J, который оказался недостаточно гибким. Обе точки зрения имеют право на жизнь, а я на всякий случай откатился в стан почитателей Log4J.
да, есть такое — если кто-то подложил свой implementation jar, то он перекрывает все остальное
видимо в 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 то добавляют, то убирают.
jetty разве использует log4j? мне казалось, что там logback
что касается 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 — самая серьёзная. Так что правильная тема в статье поднята.
непонятно причем тут опенсорс сообщества, упоминаемое на каждом шагу
Упоминания относятся к Java-open source-сообществу.
статья ведется, словно опенсорсное сообщество прям пользует и определяет что хорошо, а что плохо — хотя это шокапец не верно
Это откуда такое? Там другие механизмы действуют. Но вообще это оффтопик.
отлично, всё здорово разобрано.
Огромное спасибо за статью. Уже очень долгое время:
— использую Log4j,
— матерю java-util-logging,
— тихо ненавижу commons-logging
— побаиваюсь SLF4J.
После прочтения статьи появилось желание попробовать logback. Что и сделаю в ближайшее время.
Про logback не указана, на мой взгляд, важная особенность. Он нативно реализует SLF4J API (команда/спонсор судя по всему одна и та же — QOS.ch). А это значит, что используя «православную» связку SLF4J + logback, обёрток как бы и нет.
Согласен с WFrog, кроме того SLF4J — просто кучка интерфейсов, Logback — просто реализация (нативная), поэтому ставить их в один ряд немного неправильно. Вообще мне больше всего нравится через slf4j и его адаптеры перенаправлять все в Logback, и в своем проекте использовать slf4j. Самый народный вариант и все зависимости логируют свой злам единообразно.
Спасибо за отличную статью!
бесконечный «reinvent the wheel». То есть из двух вариантов «доработать существующее» и «сделать свое» всегда выбирался второй.
«Существующее» нередко имеется в виде, уже распухшем почти до уровня мини-ОС, так что в таких случаях «сделать своё» — это ещё и тупо быстрее. К тому же «дорабатывать» — это не брать готовое, по трудозатратам вполне сопоставимо со «сделать своё», и при этом зависеть от выпуска новых версий (и изменения политики лицензирования) чужого продукта.
Ответ на вопрос «подружиться с разработчиками продукта с миллионом пользователей или писать свой, с 0 пользователями» не так однозначен и не только трудозатраты решают. Хотя ссоры в оперсорцовых командах не редкость — тот же ffmpeg распилили вдоль и поперек.
Нельзя сказать, чтобы JSR47 проигрывал в производительности. Местами он обгонял log4j за счет поддержания в памяти специального представления своей конфигурации (что, кстати, одновременно усложняло эту самую конфигурацию). Однако, как выяснилось, JSR47 в обязательном порядке собирал так называемую Caller Information, то бишь «откуда логгируется данное сообщение». Получение Caller Information — операция довольно дорогостоящая, протекает она с использованием Native-кода. Опытные дяди из log4j это знали, поэтому предоставляли эту возможность с оговоркой «лучше не включайте».

Может я не понимаю, но разве это нельзя получить через new Exception().getStackTrace()?
Может я не понимаю, но разве это нельзя получить через new Exception().getStackTrace()?

Можно. Только это метод появился в Java 1.4 :)
Раньше наиболее быстрым был вызов Reflection.getCallerClass(...), но он все равно слишком медленный для логгирования.
Спасибо.
1. На мой взгляд наиболее важное преимущество любой сторонней библиотеки логгирования перед JUL- возможность для каждого задеплоенного модуля в контейнере иметь свой собственный лог-файл, а не валить все в одну кучу.

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) {
Прекрасная статья, спасибо!
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории