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

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

Не в статье которая по большому счету заточена для начинающих про syslog я писать поостерегусь. Ибо все таки "/dev/log" есть не у всех.
За статью спасибо!
Я раньше такие статьи заставлял junior-ов писать :-)
Тема не раскрыта, ИМХО. Больше всего недопонимания у народа, которым я объяснял логгинг в таких фремворках — вызывает матрица логгеров и аппендеров. И это как раз самая базовая и самая мощная фича. Про это надо в первую очередь рассказывать.

Фразы 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 и на самом деле рассказанное в данной статье с минимальными
И поверьте писать код вполне можно не понимая матрицу логеров и апендеров. Достаточно чтобы хотя бы один человек в команде это понимал.
сорри, промазал с ответом. См. чуть ниже.
Я это видел, конечно. И код писать можно. Но на best practices оно не тянет — максимум на beginners tutorial, чтобы научиться делать хоть как-нибудь, а потом уже переучиваться делать правильно (так почему сразу не учить, как делать правильно?).

Хотя, пожалуй, я слишком придираюсь.

В общем, статья написана очень неплохо. Если добавить пару вещей — (1) ту самую матрицу логгеров/аппендеров, и (2) объяснить, что хотя для примеров используется JUL из коробки, в реальной жизни его применять не стоит по ряду причин (мешанина логгеров в сторонних библиотеках требует использования обертки над логгером и перенаправления части логгинг фреймворков в один реально используемый; ограниченность возможностей JUL; неудобство использования в ряде мест — довольно критично, на самом деле) — тогда будет ок.
Не флейма ради. Я достаточно давно (фактически с момента выхода 1.4) использую JUL. Вроде пока больших проблем на замечал, ну кроме иногда необходимой некоторой магии с настройкой логеров сторонних библиотек.
Тут хочется ехидно спросить «что я делаю не так». Но на самом деле действительно интересно, что я упускаю? Может быть в моих приложениях есть какая-то засада о которой я не знаю?
В принципе то и Tomcat на JUL перешел.
Не флейма ради, а токмо пользы для :-)

Например, с 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,
вот блин… ну почему нельзя комменты редактировать?!

нужно читать как

Но если объект myObject вернет null
Спасибо, я понял. Это да. Перегруженных методов у стандартного логера не хватает.

Возможно то, что меня устраивает 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-ом «ради стандартизации»), меня раздражает то, что они не развиваются.

Спасибо за подсказку с конкатенированием строк — тут меня С/С++ бэкграунд подводит — у меня фобия насчет нулевых указателей :-).
logbag нужен вот для чего — например (почти реальная ситуация) у меня приходят пакеты по сети, я их сложным образом потрошу и раздают другим частям системы. Каждый пакет может порождать скажем 500 потенциально полезных сообщений. Пакеты валятся со скоростью 10 штук в сек. Общий максимальный объем текста логов на один пакет скажем 50-100 кб.
Система mission critical и при взрыве неприятностях с одной из 200 установок у меня будут спрашивать каждую мелочь, когда получил, что было в полученном пакете, когда обработал, какой пакет (в байтах) по сети отдал и когда, когда получил подтверждение о том, что дошло и какие байты были в подтверждении.
Контекст безусловно рулит позволяет отфильтровать относящееся к конкретной установке (по позывному), но блин за сутки это ~20Гб текста, а историю надо хранить хотя бы за три месяца.
Конечно есть log rotate + gzip и все такое, но это безумство логов тоже как-то хочется ограничить

В этой ситуации очень удобна система при которой нормально пишутся только info сообщения и все-все сообщения если при обработке пакета был хотя бы один warning.

Но, надо сказать, что проект сильно не типичный, обычно такого геморроя нет и хватает обычных средств :-)
да, интересный сценарий — запись всего только в случае ошибки — это очень полезно
Хорошая вводная статья. Хотелось бы обратить внимание на еще один, архи-важный момент: логирование в библиотеках отличается коренным образом от логирования в конечном приложении.

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

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

Пожалуйста, избегайте подобные фокусы. Помните, каждый раз, когда вы компилируете такой код, где-то умирает маленький котенок.
Согласен, момент весьма тонкий. К счастью джуниоры (основная аудитория статьи) не часто пишут библиотеки. Да и чего уже там греха таить, в нашей коммерческой практике очень редко (к моему сожалению) приходится писать библиотеки для широкого использования. Честно говоря я вообще ни одного такого проекта не помню.
Да, делать jar для своего же проекта — это часто. А вот так чтобы «для кого-то не знаю для кого и какой у него будет логер» — даже не слышал чтобы у нас кто-то такое заказывал. Увы!
Но согласитесь, даже для проектов внутри Вашей организации: сегодня вы знаете, что везде используется java.util.logging, но что будет завтра — этого не знает никто. Вот захочется вам через пару лет 10 кратного перформанса или особой реализации с учетом специфики Java EE или OSGi или что-то еще… а у вас везде JUL.
Ну у нас не in-home а outsoursing, так что все достаточно стабильное и относительно короткоживущее. Ну и опять же исходники всегда есть.
Опять же если смотреть правде в глаза в 50% случаев logging framework нам дан как свыше заказчиком. В оставшихся 50% продиктован религиозными предпочтениями тим лида.
Я например всегда исповедовал принцип «делай как проще, все равно рано или поздно жизнь изменится и придется переделывать, а простое переделывать проще и не так обидно».
The Simple Logging Facade for Java or (SLF4J) -> s/SL4J/SLF4j/g
Статья неплохая, но увы! — в ней разбираются только простейшие практики организации и настройки логирования. При этом за кадром остались, возможно, самые принципиально важные вещи: что, когда и как логировать.
Было бы очень интересно, например, узнать опыт DataArt в части
— шаблонов строк лога (чтобы grep-апь потом было легче)
— правил, регламентирующих обязательность логирования того или иного (например, исключения должны попадать в лог всегда или только при каких-то определенных обстоятельствах)
— взаимоотношения try-catch-throw и практик ведения логов (например, логировать ли что-нибудь при перевыбросе исключений)
— подходов к разруливанию «вложенного логирования» (когда часто используемый метод-утилита что-то логирует, и это иногда помогает, а иногда мешает)
— одновременного ведения нескольких логов (разбиваемых, например, по уровню) — хорошо это или плохо, помогает или нет и т.п.
— вопросов синхронизации лога на клиентской и серверной сторонах приложения

ну и других подобных аспектов.
Таки да, это такая специальная статья в которой разбираются только основы. Ибо такой специальной статьи от других авторов я найти не смог, все норовят матрицу логеров разобрать или еще что-то такое замороченное.
Нам была нужна простая статья про простые вещи. Вот она.
Возможно то, о чем я упомянул, — это не самые «простые вещи», но определенно необходимые любому новичку (да и не только), потому что с ними он столкнется ровно в тот самый момент, как только начнет использовать логгер.
Понимаю, что такую готовую статью найти трудно — потому и поинтересовался опытом DataArt. :)
см ниже, вопрос имеет много ответов и писать на эту тему статью на хабре не хочется. Может быть в корпоративном блоге.
Странно что никто ещё не написал. Сообщение об ошибке всегда должно быть осмысленным. Нельзя писать
} 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

Сразу понятно какая из строк относится к проблеме «всё сломалось, сайт не работает», не так ли?
Ну в оригинале было
log.log(Level.SEVERE, «Bla-bla-bla: », ex);

Так что ничего удивительного :-)

Боюсь, что если я напишу что и как надо логировать и какой текст лучше писать, это будет очередной holy war. Не уверен, что конструктивная дискуссия на эту тему получится.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории