Если вы хотите хорошо делать что -то, вы должны сначала заточить свои инструменты.
Многие программисты могут забыть, как важно записывать поведение приложений. При столкновении с одновременными ошибками, вызванными высоким давлением в многопоточных средах, вы можете понять важность записи журналов.
Некоторые люди были очень рады добавить это предложение в код:
log.info ("счастливая и беззаботная ведение журнала");
Он может даже не осознать важность журналов применения в обслуживании, настройке и идентификации неисправностей. Я думаю, что SLF4J - лучший API журналирования, главным образом потому, что он поддерживает отличный способ внедрить схему:
log.debug ("Найдил {} записи, соответствующий фильтр: '{}'", Records, Filter);
Для log4j вы можете сделать только это:
log.debug («найдено» + Records + »фильтр записей: '" + filter + "'");
Это написание - это не только более многословная и плохая читабельность, но также имеет сплайсинг строк, который влияет на эффективность (когда этот уровень не требует выхода).
SLF4J представляет функцию {} впрыска, и, поскольку сплайсинг строки избегается каждый раз, метод ToString не будет вызываться, и нет необходимости добавлять Isdebugenabled. SLF4J - это применение режима появления, это просто фасад. Для конкретной реализации я рекомендую фреймворк. Я уже рекламировал это один раз, а не уже завершенный log4j. У этого есть много интересных функций. В отличие от Log4J, он все еще находится в активном развитии и улучшении.
Еще один инструмент для рекомендации - perf4j:
Perf4j - System.currentTimeMillis () как log4j to System.out.println ()
Точно так же, как log4j - лучшая альтернатива System.out.println, perf4j больше похожа на замену System.currentTimeMillis (). Я представил Perf4j в проекте и наблюдал, как он работает под высокими нагрузками. Администраторы и бизнес -пользователи ошеломлены прекрасными графиками, предоставленными этим гаджетом. Мы можем просматривать проблемы с производительностью в любое время. Perf4j должен быть специальной статьей, о которой можно поговорить. Теперь вы можете сначала посмотреть на его руководство по разработчике. Существует также Ceki Gülcü (создатель Log4J, SLF4J и Research Project), который обеспечивает для нас простой способ устранения зависимостей от общего пользования.
Не забудьте уровень журнала
Каждый раз, когда вы хотите добавить линию журналов, вы подумаете, какой уровень журнала должен использоваться здесь? Около 90% программистов не уделяют особого внимания этой проблеме. Они используют один уровень для записи журналов, обычно либо информации, либо отладки. Почему?
Структура журнала имеет два основных преимущества по сравнению с System.out: классификация и уровень. Оба позволяют выборочно фильтровать журналы, навсегда или просто при ошибках устранения неполадок.
Ошибка: произошла серьезная ошибка, и должна быть рассмотрена немедленно. Этот уровень ошибки невыносим для любой системы. Например: исключение Null Pointer, база данных недоступна, а варианты использования критических путей не могут продолжаться.
Warn: Последующий процесс будет продолжаться, но его следует относиться серьезно. На самом деле, я надеюсь, что здесь есть два уровня: один из них - очевидная проблема с решением (например, «текущие данные недоступны, с использованием кэшированных данных»), а другая - потенциальная проблема и предложения (такие как «программа запускается в режиме разработки» или «пароль консоли управления недостаточно безопасен».
Отладка: о чем обеспокоены разработчиками. Позже я расскажу о том, что должно быть записано на этом уровне.
TRACE: более подробная информация, используемая только на этапе разработки. Возможно, вам все еще нужно обратить внимание на эту информацию в течение короткого периода времени после запуска продукта, но эти записи журнала являются лишь временными и должны быть отключены в конечном итоге. Трудно различить разницу между отладкой и трассировкой, но если вы добавите линию журналов и удалите ее после разработки и тестирования, журнал должен быть на уровне трассировки.
Список выше - это просто предложение, вы можете войти в систему в соответствии со своими собственными правилами, но лучше иметь определенные правила. Мой личный опыт: не фильтруйте журналы на уровне кода, но используйте правильный уровень журнала, чтобы быстро отфильтровать желаемую информацию, которая может сэкономить вам много времени.
Последнее, что можно сказать, это то, что это печально известное -*включено условное утверждение. Некоторым нравится добавлять это перед каждым журналом:
if (log.isdebugenabled ()) log.debug ("место для вашего рекламного ролика");Лично я думаю, что вы должны избегать добавления этой грязной вещи в код. Производительность, кажется, не значительно улучшилась (особенно после использования SLF4J), что больше похоже на преждевременную оптимизация. Кроме того, разве вы не находите это немного избыточным? Редко, это явное утверждение суждения явно необходимо, если мы не докажем, что построение сообщений журнала само по себе слишком дорого. В противном случае, вы можете запомнить столько, сколько и должны, и позволить фреймворту журнала беспокоиться об этом.
Вы знаете, что вы записываете?
Каждый раз, когда вы пишете строку журналов, уделите время, чтобы увидеть, что вы печатаете в файле журнала. Прочитайте свой журнал и узнайте, где исключение. Во -первых, по крайней мере избегайте исключений нулевого указателя:
log.debug ("Запрос на обработку с id: {}", request.getid ());
Вы подтвердили, что запрос не нулевой?
Записывающие коллекции также являются большой ямой. Если вы используете Hibernate для получения коллекции объектов домена из базы данных, вы случайно пишете его так: log.debug («Возвращение пользователей: {}», пользователи);
SLF4J будет вызывать метод ToString только тогда, когда это утверждение действительно напечатано, конечно, это круто. Однако, если переполнение памяти, проблемы выбора n+1, нить голодает до смерти, задержка исключения инициализации, то место хранения журнала заканчивается ... все это может произойти. Лучший способ - записать только идентификатор объекта (или только размер коллекции). Тем не менее, для сбора идентификаторов требуется вызов метода GetID для каждого объекта, что на самом деле нелегко в Java. Groovy имеет отличный оператор расширения (пользователи*.id). В Java мы можем использовать библиотеку Beanatils Commons для моделирования:
log.debug ("возвращение идентификаторов пользователей: {}", collect (users, "id"));
Вероятно, это то, как реализован метод сбора:
Общедоступная статическая коллекция Collection (Collection Collection, String PropertyName) {return Collection.collect (Collection, New Beantopropertyvaluetransformer (PropertyName));}Наконец, метод ToString может быть реализован неправильно или используется.
Во -первых, чтобы войти в систему, существует много способов создать Tostring для каждого класса. Лучше всего использовать ToStringBuilder для генерации (но не версии реализации отражения).
Во -вторых, обратите внимание на массивы и нетипичные наборы. Реализация массивов и некоторых альтернативных коллекций может не вызывать метод ToString каждого элемента один за другим. Можно использовать метод DeeptoString, предоставленный JDK. Проверьте журналы, которые вы напечатали на себе, чтобы увидеть, есть ли какая -либо информация об исключении формата.
Избегайте побочных эффектов
Печать журнала, как правило, не оказывает большого влияния на производительность программы. Недавно мой друг бросил исключение LazyInitialization Exception Exception в системе, работающей на некоторых специальных платформах. Как вы уже догадались, некоторые отпечатки журналов, которые приводят к загрузке задержки инициализации при подключении сеанса. В этом случае, если уровень журнала увеличен, коллекция больше не будет инициализироваться. Если вы не знаете эту контекстную информацию, сколько времени вам понадобится, чтобы открыть эту ошибку.
Другим побочным эффектом является то, что он влияет на скорость работы программы. Быстрый ответ на этот вопрос: если журналы печатаются слишком сильно или то, что ToString и String Splicing не используются правильно, журнальная печать окажет негативное влияние на производительность. Насколько это может быть? Ну, я видел перезагрузку программы каждые 15 минут, потому что слишком много журналов заставляют потоки голодать до смерти. Это побочный эффект! Исходя из моего опыта, печать сто мегабайтов за один час - почти верхний предел.
Конечно, если бизнес -процесс прерывается из -за исключений для печати журнала, этот побочный эффект будет отличным. Я часто вижу людей, пишущих это, чтобы избежать этого:
try {log.trace ("id =" + request.getUser (). getId () + "accesses" + Manager.getPage (). getUrl (). ToString ())} Catch (nullPointerException e) {}Это настоящий кусок кода, но для того, чтобы сделать мир более очищенным, пожалуйста, не пишите его так.
Опишите четко
Каждая запись журнала содержит данные и описание. Взгляните на этот пример:
log.debug ("обработка сообщения"); log.debug (message.getjmsmessageid ()); log.debug ("Сообщение с id '{}' обработан", message.getjmsmessageid ()); При ошибках устранения неполадок в незнакомых системе, какой журнал вы предпочитаете видеть? Поверьте мне, эти примеры распространены. Есть также отрицательный режим:
if (экземпляр сообщения TextMessage)
// ...
еще
log.warn ("Неизвестный тип сообщения");
Трудно ли добавить типы сообщений, идентификаторы сообщений и т. Д. В этот журнал предупреждения? Я знаю, что произошла ошибка, но что это? Что такое контекстная информация?
Третий отрицательный пример - «Волшебный журнал». Реальный пример: многие программисты в команде знают, что следуют 3 ≥ числа! Число, за которым следует номер #, за которым следует бревно псевдолупиточный номер, означает «сообщение с идентификатором XYZ было получено». Никто не хочет менять этот журнал. Если кто -то напечатал клавиатуру и выбрал уникальную строку "&&&!#", Он быстро найдет нужную информацию.
Результатом является то, что весь файл журнала выглядит как большая строка случайных символов. Некоторые люди не могут не задаться вопросом, является ли это программой Perl.
Файлы журнала должны быть читаемыми, чистыми и самопровозглашенными. Не используйте магические числа, записи значения, числа, идентификаторы и их контекст. Запишите обработанные данные и их значение. Запишите, что делает программа. Хороший журнал должен быть хорошим документом кода программы.
Я упоминал, чтобы не печатать пароль и иметь личную информацию? Я считаю, что нет такого глупого программиста.
Отрегулируйте свой формат
Формат журнала - очень полезный инструмент, который невидимо добавляет ценную контекстную информацию в журнал. Но вы должны четко думать о том, какая информация включена в ваш формат. Например, не имеет смысла записывать даты в журналах, написанных каждый почасовой цикл, потому что ваше имя журнала уже содержит эту информацию. Напротив, если вы не записываете имя потока, когда два потока работают параллельно, вы не сможете отслеживать потоки через журналы - журналы перекрывались вместе. В однопользовавшем приложении это нормально, но это уже ушло в прошлое.
Исходя из моего опыта, идеальный формат журнала должен включать (кроме самой информации журнала, конечно): текущее время (без даты, миллисекундная точность), уровень журнала, имя потока, простое имя журнала (не с полным именем) и сообщения. В заказах это будет выглядеть так:
<appender name = "stdout"> <concoder> <tlement>%d {hh: mm: ss.ss}%-5level [%inteh] [%logger {0}]%m%n </pattern> </encoder> </appender>Имена файлов, имена классов, номера строк, не нужно перечислять, хотя они кажутся полезными. Я также видел пустые журналы в коде:
log.info (""); Поскольку программист считает, что номер строки будет частью формата журнала, и он знает, что если пустое сообщение журнала появляется в строке 67 файла, это означает, что пользователь был аутентифицирован. Мало того, что записи имен имен имен класса или номера строк оказывают большое влияние на производительность.
Более продвинутой особенностью фреймворка регистрации является сопоставленный диагностический контекст. MDC - это просто карта, которая является локальной для потока. Вы можете поместить любые пары клавиш в эту карту, чтобы все записи журнала этого потока могли получить соответствующую информацию с этой карты как часть формата вывода.
Запишите параметры и возвращаемые значения метода
Если вы найдете ошибку на этапе разработки, вы обычно используете отладчик для отслеживания конкретной причины. Теперь допустим, вы больше не будете использовать отладчика. Например, поскольку эта ошибка появилась в среде пользователя несколько дней назад, все, что вы можете получить, это несколько журналов. Что вы можете узнать из этого?
Если вы следуете простому принципу печати и параметров для каждого метода, вам вообще не нужен отладчик. Конечно, каждый метод может получить доступ к внешним системам, блокировать, ждать и т. Д., И это следует учитывать. Просто обратитесь к следующему формату:
public String printDocument (документ doc, режим режима) {log.debug ("inving printdocument (doc = {}, mode = {})", doc, mode); String id = ...; // длинная операция печати log.debug ("оставить printdocument (): {}", id); возвратный идентификатор;}Поскольку вы регистрируете журналы в начале и в конце метода, вы можете вручную найти неэффективные коды и даже обнаруживать причины, которые могут вызвать тупики и голод - вам просто нужно посмотреть, не будет ли «ухода» после «входа». Если смысл имени вашего метода ясен, это будет приятно очистить журнал. Точно так же анализировать исключения проще, потому что вы знаете, что делаете на каждом шаге. Если в коде есть много методов, вы можете использовать разделы AOP для его завершения. Это уменьшает дублированный код, но вы должны быть очень осторожны при его использовании, и если вы не будете осторожны, это может привести к большому количеству выводов журнала.
Наиболее подходящими уровнями для этого вида журнала являются отладка и трассировки. Если вы обнаружите, что метод называется слишком часто, и запись его журнала может повлиять на производительность, вам просто нужно снизить уровень его журнала или напрямую удалить журнал (или только один из всех вызовов метода?) Однако слишком много журналов лучше, чем меньше. Думайте о журнале в качестве модульных тестов, ваш код должен быть покрыт журналами так же, как его модульные тесты находятся везде. Никакая часть системы вообще не требует журналов. Помните, что иногда вам нужно знать, правильно ли работает ваша система, и вы можете просматривать только журналы, которые постоянно затопляют экран.
Наблюдайте за внешними системами
Это предложение немного отличается от предыдущего: если вы общаетесь с внешней системой, не забудьте записать исходящие и чтение данных вашей системы. Системная интеграция - это рутина, и диагностика проблем между двумя приложениями (представьте себе разные компании, среда, технические команды) особенно сложна. Недавно мы обнаружили, что запись полного содержания сообщений, в том числе заголовки Apache CXF SOAP и HTTP, очень эффективна на этапе интеграции и тестирования системы.
Это дорого, и если это влияет на производительность, вы можете отключить только журнал. Но таким образом, ваша система может работать очень быстро и быстро повесить, так что вы ничего не можете с этим поделать? При интеграции с внешними системами вы можете быть только очень осторожны и быть готовыми пожертвовать накладными расходами. Если вам посчастливилось и интеграция системы обрабатывается ESB, лучше всего записать запрос и ответ на автобусе. Вы можете обратиться к этому компоненту журнала мула.
Иногда объем данных, обменяемых с внешними системами, определяет, что для вас невозможно записать все. С другой стороны, лучше всего сохранить все в журнале на этапе тестирования и ранних этапов высвобождения и быть готовым к жертвопринождению. Это можно сделать путем настройки уровня журнала. Взгляните на следующие советы:
Collection <Integer> requestIds = // ... if (log.isdebugenabled ()) log.debug ("Идентификаторы обработки: {}", requestids); else log.info ("Идентификаторы обработки: {}", requestIds.size ()); Если этот журнал настроен на уровень отладки, он печатает полную коллекцию идентификаторов запроса. Если она настроена на печати информацию, она выведет только размер набора. Вы можете спросить меня, забыл ли я условие ISINfoEnboding, пожалуйста, посмотрите второе предложение. Еще одна вещь, которую стоит отметить, это то, что набор идентификаторов не может быть нулевым. Хотя он может печатать обычно под отладкой как NULL, он является большим нулевым указателем при настройке как информация. Помните побочные эффекты, упомянутые в 4 -м предложении?
Правильные исключения для ведения журнала
Во -первых, не регистрируйте исключения, пусть структура или контейнер сделают это. Конечно, есть исключение: если вы бросаете исключения (RMI, EJB и т. Д.) Из удаленной службы, исключения сериализованы, чтобы убедиться, что они могут быть возвращены клиенту (часть API). В противном случае, клиент получит NoclassDeffoundError или другое странное исключение вместо реального сообщения об ошибке.
Запись исключений является одной из наиболее важных обязанностей по ведению журнала, но многие программисты, как правило, используют журнал как способ справиться с исключениями. Обычно они просто возвращают значение по умолчанию (обычно нулевая, 0 или пустая строка) и притворяются, что ничего не произошло. Иногда они сначала записывают исключение, затем завершают исключение, а затем выбрасывают его:
log.error ("IO Exception", E); бросить новое customexception (e);Таким образом, информация о стеке обычно напечатана дважды, потому что места, где завоевано исключение Mycustomexception, будет напечатано снова. Записи журнала или оберните и выбросьте, не используйте его одновременно, иначе ваши журналы будут выглядеть запутанными.
Что если мы действительно хотим войти в систему? По какой -то причине (предположительно не читать API и документацию?), Около половины журнала, я думаю, неверно. Небольшой тест, какое из следующих операторов журнала может правильно распечатать исключения из нулевого указателя?
попробуйте {integer x = null; ++ x;} catch (Exception e) {log.error (e); // a log.error (e, e); // b log.error ("" + e); // c log.error (e.toString ()); // d log.error (e.getMessage ()); // e log.error (null, e); // f log.error ("", e); // g log.error ("{}", e); // h log.error ("{}", e.getmessage ()); // i log.error ("Файл конфигурации ошибок:" + e); // j log.error ("Файл конфигурации ошибок:" + e.getMessage ()); // k log.error ("Файл конфигурации ошибок", E); // l}Странно, что только G и L (это лучше) правы! A и B вообще не составлены под SLF4J. Другие выбросят информацию о трассировке стека или печатают неверную информацию. Например, E не печатает ничего, потому что само исключение NULL Pointer не предоставляет никакой информации об исключении, а информация о стеке не распечатана. Помните, что первым параметром обычно является текстовая информация о самой ошибке. Не пишите в нем информацию об исключении. Он автоматически выйдет после печати журнала перед информацией о стеке. Но если вы хотите распечатать это, конечно, вы должны передать исключение во втором параметре.
Журналы должны быть читаемыми и легко проанализировать
Теперь есть две группы пользователей, которые заинтересованы в ваших журналах: мы, люди (независимо от того, согласны ли вы или нет, кодеры здесь) и компьютеры (обычно сценарии оболочки, написанные системными администраторами). Журналы должны быть подходящими для понимания обоих пользователей. Если кто -то смотрит на журнал вашей программы позади вас и видит это:
Тогда вы определенно не следовали моему совету. Журналы должны быть так же просто читать и понимать, как код.
С другой стороны, если ваша программа генерирует половину ГБ журналов каждый час, ни один или какой -либо графический текстовый редактор не может их закончить. В это время наши старые парни, Греп, Сед и Авк, пришли, когда они пришли на поле. Если возможно, лучше всего для журналов, которые вы записываете, чтобы прояснить оба компьютера. Не форматируйте числа, используйте некоторые форматы, которые делают регулярные соответствующие и т. Д. Если это невозможно, распечатайте данные в двух форматах:
log.debug ("запрос ttl set to: {} ({})", новая дата (ttl), ttl); // Запрос ttl set: wed 28 апреля 20:14:12 Cest 2010 (1272478452437) окончательная строка duduration = durationformatils.formatdureats (durountmillis, true, trueMinfire. {} MS ({}) ", DurationMillis, Duration); // Импорт взял: 123456789 мс (1 день 10 часов 17 минут 36 секунд)Компьютеры видят «MS после эпохи 1970 года». Такой формат времени поблагодарит вас, в то время как люди рады видеть что -то вроде «1 день 10 часов 17 минут 36 секунд».
Короче говоря, журнал также может быть написан как элегантный, как стихотворение, если вы готовы подумать об этом.
Выше приведено набор информации о формате регулировки журнала Java. Друзья, которые заинтересованы, могут ссылаться на это.