Comments 26
> Выводим лог сообщения куда-то еще
Здесь еще можно добавить, что возможно логирование в syslog. Что довольно удобно в некоторых случаях.
Например вот так:
blog.dutchworks.nl/2010/01/14/logging-to-the-syslog-from-a-java-application/
Здесь еще можно добавить, что возможно логирование в syslog. Что довольно удобно в некоторых случаях.
Например вот так:
blog.dutchworks.nl/2010/01/14/logging-to-the-syslog-from-a-java-application/
За статью спасибо!
Я раньше такие статьи заставлял junior-ов писать :-)
Я раньше такие статьи заставлял junior-ов писать :-)
Тема не раскрыта, ИМХО. Больше всего недопонимания у народа, которым я объяснял логгинг в таких фремворках — вызывает матрица логгеров и аппендеров. И это как раз самая базовая и самая мощная фича. Про это надо в первую очередь рассказывать.
Фразы java logging best practices и java.util.logging framework не сочетаются — опять ИМХО. java.util.logging framework не рекомендуется к использованию (по многим причинам), а вместо нее нужна обертка типа slf4j, и реализация поприличнее, чем JUL — тогда можно говорить о best practices. Иначе в реальной жизни оно так-же применимо, как Hello World.
Фразы java logging best practices и java.util.logging framework не сочетаются — опять ИМХО. java.util.logging framework не рекомендуется к использованию (по многим причинам), а вместо нее нужна обертка типа slf4j, и реализация поприличнее, чем JUL — тогда можно говорить о best practices. Иначе в реальной жизни оно так-же применимо, как Hello World.
См начало статьи:
Весь код примеров использует java.util.logging framework. Вопрос «Какой из фреймворков логирования ниболее кошерен» я оставлю за кадром. Скажу только что до java.util.logging проще всего дотянуться ибо он уже идет вместе с JRE и на самом деле рассказанное в данной статье с минимальными
И поверьте писать код вполне можно не понимая матрицу логеров и апендеров. Достаточно чтобы хотя бы один человек в команде это понимал.
Весь код примеров использует java.util.logging framework. Вопрос «Какой из фреймворков логирования ниболее кошерен» я оставлю за кадром. Скажу только что до java.util.logging проще всего дотянуться ибо он уже идет вместе с JRE и на самом деле рассказанное в данной статье с минимальными
И поверьте писать код вполне можно не понимая матрицу логеров и апендеров. Достаточно чтобы хотя бы один человек в команде это понимал.
Я это видел, конечно. И код писать можно. Но на best practices оно не тянет — максимум на beginners tutorial, чтобы научиться делать хоть как-нибудь, а потом уже переучиваться делать правильно (так почему сразу не учить, как делать правильно?).
Хотя, пожалуй, я слишком придираюсь.
В общем, статья написана очень неплохо. Если добавить пару вещей — (1) ту самую матрицу логгеров/аппендеров, и (2) объяснить, что хотя для примеров используется JUL из коробки, в реальной жизни его применять не стоит по ряду причин (мешанина логгеров в сторонних библиотеках требует использования обертки над логгером и перенаправления части логгинг фреймворков в один реально используемый; ограниченность возможностей JUL; неудобство использования в ряде мест — довольно критично, на самом деле) — тогда будет ок.
Хотя, пожалуй, я слишком придираюсь.
В общем, статья написана очень неплохо. Если добавить пару вещей — (1) ту самую матрицу логгеров/аппендеров, и (2) объяснить, что хотя для примеров используется JUL из коробки, в реальной жизни его применять не стоит по ряду причин (мешанина логгеров в сторонних библиотеках требует использования обертки над логгером и перенаправления части логгинг фреймворков в один реально используемый; ограниченность возможностей JUL; неудобство использования в ряде мест — довольно критично, на самом деле) — тогда будет ок.
Не флейма ради. Я достаточно давно (фактически с момента выхода 1.4) использую JUL. Вроде пока больших проблем на замечал, ну кроме иногда необходимой некоторой магии с настройкой логеров сторонних библиотек.
Тут хочется ехидно спросить «что я делаю не так». Но на самом деле действительно интересно, что я упускаю? Может быть в моих приложениях есть какая-то засада о которой я не знаю?
В принципе то и Tomcat на JUL перешел.
Тут хочется ехидно спросить «что я делаю не так». Но на самом деле действительно интересно, что я упускаю? Может быть в моих приложениях есть какая-то засада о которой я не знаю?
В принципе то и Tomcat на JUL перешел.
Не флейма ради, а токмо пользы для :-)
Например, с JUL мы пишем:
Но конкатенация строк при выключенных отладочных логах может убить производительность, поэтому мы пишем так:
Но если функция prepareCPUConsumingLogMessage() вернет null, всем будет плохо. Поэтому мы пишем так:
Теперь сравним с SLF4J:
Профит:
1. Значительно более компактно и понятно.
2. Проверка logLevel может быть опущена в большинстве мест, кроме самых критичных по быстродействию, и тех, где вызывается prepareCPUConsumingLogMessage().
3. Проверка нулей выполняется за вас — это очень важно, так как зачастую из-за этих нулей включение логгирования может прервать выполнение функции (которая сама по себе может и была защищена от нулей, но не в отладочном сообшении).
Например, с JUL мы пишем:
log.fine( "Let's display some object in debug: " + myObject );
Но конкатенация строк при выключенных отладочных логах может убить производительность, поэтому мы пишем так:
if( log.isLoggable( Level.FINE ) ) {
log.fine( "Let's display some object in debug: " + myObject );
}
Но если функция prepareCPUConsumingLogMessage() вернет null, всем будет плохо. Поэтому мы пишем так:
if( log.isLoggable( Level.FINE ) ) {
log.fine( "Let's display some object in debug: " + (myObject != null ? myObject.toString() : "null") );
}
Теперь сравним с SLF4J:
log.debug( "Let's display some object in debug: {}", myObject );
Профит:
1. Значительно более компактно и понятно.
2. Проверка logLevel может быть опущена в большинстве мест, кроме самых критичных по быстродействию, и тех, где вызывается prepareCPUConsumingLogMessage().
3. Проверка нулей выполняется за вас — это очень важно, так как зачастую из-за этих нулей включение логгирования может прервать выполнение функции (которая сама по себе может и была защищена от нулей, но не в отладочном сообшении).
Поторопился —
Но если функция prepareCPUConsumingLogMessage() вернет null,
нужно читать как
Но если объект вернет myObject,
Но если функция prepareCPUConsumingLogMessage() вернет null,
нужно читать как
Но если объект вернет myObject,
вот блин… ну почему нельзя комменты редактировать?!
нужно читать как
Но если объект myObject вернет null
нужно читать как
Но если объект myObject вернет null
Спасибо, я понял. Это да. Перегруженных методов у стандартного логера не хватает.
Возможно то, что меня устраивает JUL является следствием приверженности бритве Оккама и моего стиля написания лог сообщений.
На конкатенацию двух-трех строк я обычно не обращаю внимания, особенно если где-то рядом есть обращение к БД или сетевой обмен.
По поводу null
выведет
В целом согласен — сочетания Formatter и логера в одном флаконе весьма удобно.
На самом деле к JUL у меня есть две серьезные претензии
1. отсутствие контекстного логирования например /псевдокод/
делаем так, а потом собираем по всем логерам срез относящийся только к данному хосту. Как мне помнится Log4J такое позволяет
2. LogBags — это когда мы выделяем одну условную единицу обработки например запроса и пишем туда все подряд с разными уровнями. Но в лог файл это попадает только если максимальный уровень в LogBag не меньше указанного, например warning. Так мы имеем не очень большой файл логов, но зато все-все сообщения включая уровень finest.
По молодости я пытался что-то такое использовать, но потом плюнул ибо заставить всю команду ровно и консистентно писать в лог не реально. А если даже один джуниор начинает писать как попало (например забьет на контекст), все эти стройные штуки ломаются нафиг. И сейчас остановился на следующей позиции. Да JUL не идеален, иногда он громоздок, но он стандартен и проще один раз научить всех им пользоваться, чем мучиться с солянкой из разных логеров в головах разработчиков.
Мне к сожалению довелось видеть проект в котором 3 разных разработчика, каждый использовали свой и только свой, единственно рассово-верный логер, периодически рефакторя код друг друга :-)
Возможно то, что меня устраивает JUL является следствием приверженности бритве Оккама и моего стиля написания лог сообщений.
На конкатенацию двух-трех строк я обычно не обращаю внимания, особенно если где-то рядом есть обращение к БД или сетевой обмен.
По поводу null
public class TestNull {
public static void main(String[] args) {
String s = null;
System.out.println("!"+s);
}
}
выведет
!null
В целом согласен — сочетания Formatter и логера в одном флаконе весьма удобно.
На самом деле к JUL у меня есть две серьезные претензии
1. отсутствие контекстного логирования например /псевдокод/
log.context(remoteHostAddress).log("Transport packet received:\n" + Util.dumpBytes(packetBytes));
делаем так, а потом собираем по всем логерам срез относящийся только к данному хосту. Как мне помнится Log4J такое позволяет
2. LogBags — это когда мы выделяем одну условную единицу обработки например запроса и пишем туда все подряд с разными уровнями. Но в лог файл это попадает только если максимальный уровень в LogBag не меньше указанного, например warning. Так мы имеем не очень большой файл логов, но зато все-все сообщения включая уровень finest.
По молодости я пытался что-то такое использовать, но потом плюнул ибо заставить всю команду ровно и консистентно писать в лог не реально. А если даже один джуниор начинает писать как попало (например забьет на контекст), все эти стройные штуки ломаются нафиг. И сейчас остановился на следующей позиции. Да JUL не идеален, иногда он громоздок, но он стандартен и проще один раз научить всех им пользоваться, чем мучиться с солянкой из разных логеров в головах разработчиков.
Мне к сожалению довелось видеть проект в котором 3 разных разработчика, каждый использовали свой и только свой, единственно рассово-верный логер, периодически рефакторя код друг друга :-)
logbags на самом деле не обязательно. Если таки использовать context (по крайней мере, в критических участках), то например, logback позволяет с помощью MDCFilter-a логгировать отладочные сообщения только от одного залогиненного пользователя (или по какому другому критерию), оставляя всех остальных пользователей в INFO.
Можно даже прикрутить такую штуку без использования контекста, если есть на что опереться в thread local storage, чтобы получить ассоциацию с конкретной сессией.
В JUL (и ему подобных вещах, в свое время полу-скопированных Sun-ом «ради стандартизации»), меня раздражает то, что они не развиваются.
Спасибо за подсказку с конкатенированием строк — тут меня С/С++ бэкграунд подводит — у меня фобия насчет нулевых указателей :-).
Можно даже прикрутить такую штуку без использования контекста, если есть на что опереться в thread local storage, чтобы получить ассоциацию с конкретной сессией.
В JUL (и ему подобных вещах, в свое время полу-скопированных Sun-ом «ради стандартизации»), меня раздражает то, что они не развиваются.
Спасибо за подсказку с конкатенированием строк — тут меня С/С++ бэкграунд подводит — у меня фобия насчет нулевых указателей :-).
logbag нужен вот для чего — например (почти реальная ситуация) у меня приходят пакеты по сети, я их сложным образом потрошу и раздают другим частям системы. Каждый пакет может порождать скажем 500 потенциально полезных сообщений. Пакеты валятся со скоростью 10 штук в сек. Общий максимальный объем текста логов на один пакет скажем 50-100 кб.
Система mission critical и привзрыве неприятностях с одной из 200 установок у меня будут спрашивать каждую мелочь, когда получил, что было в полученном пакете, когда обработал, какой пакет (в байтах) по сети отдал и когда, когда получил подтверждение о том, что дошло и какие байты были в подтверждении.
Контекст безусловно рулит позволяет отфильтровать относящееся к конкретной установке (по позывному), но блин за сутки это ~20Гб текста, а историю надо хранить хотя бы за три месяца.
Конечно есть log rotate + gzip и все такое, но это безумство логов тоже как-то хочется ограничить
В этой ситуации очень удобна система при которой нормально пишутся только info сообщения и все-все сообщения если при обработке пакета был хотя бы один warning.
Но, надо сказать, что проект сильно не типичный, обычно такого геморроя нет и хватает обычных средств :-)
Система mission critical и при
Контекст безусловно рулит позволяет отфильтровать относящееся к конкретной установке (по позывному), но блин за сутки это ~20Гб текста, а историю надо хранить хотя бы за три месяца.
Конечно есть log rotate + gzip и все такое, но это безумство логов тоже как-то хочется ограничить
В этой ситуации очень удобна система при которой нормально пишутся только info сообщения и все-все сообщения если при обработке пакета был хотя бы один warning.
Но, надо сказать, что проект сильно не типичный, обычно такого геморроя нет и хватает обычных средств :-)
Хорошая вводная статья. Хотелось бы обратить внимание на еще один, архи-важный момент: логирование в библиотеках отличается коренным образом от логирования в конечном приложении.
В идеале библиотека (коих в приложении может быть больше чем одна штука) не навязывает мне использование какой-либо конкретной реализации логирования, а пользуется нейтральным интерфейсом, оставляя выбор реализации за конечным приложением (именно эту возможность дают SLF4J и динозавр commons logging).
И уж совсем печально видеть порой библиотеки, которые не только берут ответственность в выборе реализации логирования на себя, но вдобавок еще и перенимают на себя всю конфигурацию.
Пожалуйста, избегайте подобные фокусы. Помните, каждый раз, когда вы компилируете такой код, где-то умирает маленький котенок.
В идеале библиотека (коих в приложении может быть больше чем одна штука) не навязывает мне использование какой-либо конкретной реализации логирования, а пользуется нейтральным интерфейсом, оставляя выбор реализации за конечным приложением (именно эту возможность дают SLF4J и динозавр commons logging).
И уж совсем печально видеть порой библиотеки, которые не только берут ответственность в выборе реализации логирования на себя, но вдобавок еще и перенимают на себя всю конфигурацию.
Пожалуйста, избегайте подобные фокусы. Помните, каждый раз, когда вы компилируете такой код, где-то умирает маленький котенок.
Согласен, момент весьма тонкий. К счастью джуниоры (основная аудитория статьи) не часто пишут библиотеки. Да и чего уже там греха таить, в нашей коммерческой практике очень редко (к моему сожалению) приходится писать библиотеки для широкого использования. Честно говоря я вообще ни одного такого проекта не помню.
Да, делать jar для своего же проекта — это часто. А вот так чтобы «для кого-то не знаю для кого и какой у него будет логер» — даже не слышал чтобы у нас кто-то такое заказывал. Увы!
Да, делать jar для своего же проекта — это часто. А вот так чтобы «для кого-то не знаю для кого и какой у него будет логер» — даже не слышал чтобы у нас кто-то такое заказывал. Увы!
Но согласитесь, даже для проектов внутри Вашей организации: сегодня вы знаете, что везде используется java.util.logging, но что будет завтра — этого не знает никто. Вот захочется вам через пару лет 10 кратного перформанса или особой реализации с учетом специфики Java EE или OSGi или что-то еще… а у вас везде JUL.
Ну у нас не in-home а outsoursing, так что все достаточно стабильное и относительно короткоживущее. Ну и опять же исходники всегда есть.
Опять же если смотреть правде в глаза в 50% случаев logging framework нам дан как свыше заказчиком. В оставшихся 50% продиктован религиозными предпочтениями тим лида.
Я например всегда исповедовал принцип «делай как проще, все равно рано или поздно жизнь изменится и придется переделывать, а простое переделывать проще и не так обидно».
Опять же если смотреть правде в глаза в 50% случаев logging framework нам дан как свыше заказчиком. В оставшихся 50% продиктован религиозными предпочтениями тим лида.
Я например всегда исповедовал принцип «делай как проще, все равно рано или поздно жизнь изменится и придется переделывать, а простое переделывать проще и не так обидно».
The Simple Logging Facade for Java or (SLF4J) -> s/SL4J/SLF4j/g
Статья неплохая, но увы! — в ней разбираются только простейшие практики организации и настройки логирования. При этом за кадром остались, возможно, самые принципиально важные вещи: что, когда и как логировать.
Было бы очень интересно, например, узнать опыт DataArt в части
— шаблонов строк лога (чтобы grep-апь потом было легче)
— правил, регламентирующих обязательность логирования того или иного (например, исключения должны попадать в лог всегда или только при каких-то определенных обстоятельствах)
— взаимоотношения try-catch-throw и практик ведения логов (например, логировать ли что-нибудь при перевыбросе исключений)
— подходов к разруливанию «вложенного логирования» (когда часто используемый метод-утилита что-то логирует, и это иногда помогает, а иногда мешает)
— одновременного ведения нескольких логов (разбиваемых, например, по уровню) — хорошо это или плохо, помогает или нет и т.п.
— вопросов синхронизации лога на клиентской и серверной сторонах приложения
ну и других подобных аспектов.
Было бы очень интересно, например, узнать опыт DataArt в части
— шаблонов строк лога (чтобы grep-апь потом было легче)
— правил, регламентирующих обязательность логирования того или иного (например, исключения должны попадать в лог всегда или только при каких-то определенных обстоятельствах)
— взаимоотношения try-catch-throw и практик ведения логов (например, логировать ли что-нибудь при перевыбросе исключений)
— подходов к разруливанию «вложенного логирования» (когда часто используемый метод-утилита что-то логирует, и это иногда помогает, а иногда мешает)
— одновременного ведения нескольких логов (разбиваемых, например, по уровню) — хорошо это или плохо, помогает или нет и т.п.
— вопросов синхронизации лога на клиентской и серверной сторонах приложения
ну и других подобных аспектов.
Таки да, это такая специальная статья в которой разбираются только основы. Ибо такой специальной статьи от других авторов я найти не смог, все норовят матрицу логеров разобрать или еще что-то такое замороченное.
Нам была нужна простая статья про простые вещи. Вот она.
Нам была нужна простая статья про простые вещи. Вот она.
Возможно то, о чем я упомянул, — это не самые «простые вещи», но определенно необходимые любому новичку (да и не только), потому что с ними он столкнется ровно в тот самый момент, как только начнет использовать логгер.
Понимаю, что такую готовую статью найти трудно — потому и поинтересовался опытом DataArt. :)
Понимаю, что такую готовую статью найти трудно — потому и поинтересовался опытом DataArt. :)
Странно что никто ещё не написал. Сообщение об ошибке всегда должно быть осмысленным. Нельзя писать
Это то же самое что писать комментарии в стиле
То что это Exception и так видно, что именно сломалось-то не понятно! Реальность такова, что в проектах всегда есть Exception'ы и когда что-то сломалось нужно уметь отделить в логе те, которые имеют отношение к поломке. Сравните, Вы видите в логе
Относится ли это к проблеме, ради которой Вас разбудили в 4 утра? И сравните с таким логом:
Сразу понятно какая из строк относится к проблеме «всё сломалось, сайт не работает», не так ли?
} catch (Exception ex) {
log.log(Level.SEVERE, "Exception: ", ex);
}
Это то же самое что писать комментарии в стиле
// Create new long varibale and assign current timestamp to it
Long time = new Long(System.currentTimeMillis());
То что это Exception и так видно, что именно сломалось-то не понятно! Реальность такова, что в проектах всегда есть Exception'ы и когда что-то сломалось нужно уметь отделить в логе те, которые имеют отношение к поломке. Сравните, Вы видите в логе
ERROR 2011-10-13 16:43:56,643 [ProxyUtils] Exception: NumberFormatException
Относится ли это к проблеме, ради которой Вас разбудили в 4 утра? И сравните с таким логом:
ERROR 2011-10-13 16:43:56,643 [ProxyUtils] Failed to parse user's input: NumberFormatException
ERROR 2011-10-13 16:43:56,648 [ProxyUtils] Failed to load proxy configuration: NullPointerException
Сразу понятно какая из строк относится к проблеме «всё сломалось, сайт не работает», не так ли?
Sign up to leave a comment.
Логирование в Java / quick start