Инженерная практика: как правильно печатать журналы программы?
Давным-давно друг спросил меня, если бы вас попросили взять на себя старый проект для последующего обслуживания, с чего бы вы начали в первую очередь и позволили бы себе начать проект быстрее? В то время я не стал прямо отвечать на вопрос этого друга, я сказал: прост ли старый проект в использовании, очень важным моментом является то, достаточно ли хорош лог этого проекта. Потому что вообще говоря, старый проект относительно стабилен, и велика вероятность того, что в доработке серьезных изменений и изменений не будет, поэтому для такого проекта ядром является «поддержание стабильности». Однако никто не может гарантировать, что не будет онлайн-сбоев при работе проекта онлайн.Когда есть онлайн-проблемы или сбои, то, как быстро остановить потери, является первоочередной задачей, и лог играет очень важную роль в этом процессе. прекращения потерь. Журнал достаточно четкий, чтобы помочь разработчикам и персоналу по эксплуатации и техническому обслуживанию быстро определить проблему, а затем решить, какой план предпринять, чтобы остановить потерю.
Сегодня поговорим о том, как сделать журнал программы проекта «ОК». Ниже приводится план этой статьи:
1. Зачем нужно стандартизированно распечатывать журнал программы?
2. Как правильно распечатать логи программы?
Если есть какие-то неточности, прошу меня простить, и приветствуются критика и исправления.
Просим уважать результаты труда автора, а при перепечатке указывать ссылку на оригинал:
https://www.cnblogs.com/dolphin0520/p/10396894.html
1. Зачем нужно стандартизированно распечатывать журнал программы?
В процессе написания программного кода мы обычно сосредотачиваемся на реализации функции, и часто игнорируем важность лога, однако лог крайне важен после выхода системы в онлайн, потому что после выхода системы в онлайн только через лог можно мы понимаем текущую ситуацию.Оперативное состояние системы, в случае онлайн-сбоя, достаточно ли ясен журнал, определяет, можно ли быстро найти решение стоп-лосса. Мы можем взглянуть на следующий код:
public class HttpClient {
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
private static int CONNECT_TIMEOUT = 5000; // unit ms
private static int READ_TIMEOUT = 10000; // unit ms
public static String sendPost(String url, String param) {
OutputStream out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("charset", "UTF-8");
out = new PrintWriter(conn.getOutputStream());
out.print(parm);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception ex) {
LOG.error("post request error!!!");
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
LOG.error("close stream error!!!");
}
return result;
}
}
}Большое количество HTTP-запросов внезапно терпят неудачу на определенной антенне, а затем проверяют журнал и находят большое количество ошибок «Ошибка запроса публикации!!!». Если вы видите такой журнал в это время, вы можете понятия не иметь какова причина. Продолжайте определять конкретную причину с помощью других средств.
Если распечатанный журнал ошибок выглядит следующим образом:
post request error!!!, url:[http://www.123.test.com], param:[name=jack]
java.net.ConnectException: Connection refused
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)Затем можно быстро сделать вывод, что проблема связана с нисходящей службой http, а доменное имя нисходящей службы http — www.123.test.com (отказ в подключении обычно вызван тем, что нижестоящий служебный порт не активирован), и вы можете быстро найдите соответствующий персонал для проведения стоп-лосса, чтобы не тратить много времени на этап локализации неисправности.
Приведенный выше пример является лишь очень небольшим примером.Онлайн-проблемы, с которыми можно столкнуться в реальной повседневной разработке, более сложны и каверзны, чем этот.Вкратце, основные функции журнала заключаются в следующем:
1) Журнал - это система, работающая «волшебное зеркало», по его возможности отражать рабочее состояние в режиме реального времени системы;
Как показано на рисунке выше, производитель в системе A непрерывно генерирует данные и помещает их в очередь данных, а отправитель непрерывно извлекает данные из очереди данных и отправляет их получателю нижестоящей системы B, затем для системы A количество данных для отправки в очереди данных Это очень ключевой показатель. Он может реально отражать текущее рабочее состояние системы со стороны. Если количество элементов в очереди данных превышает 90% емкости, это означает, что в это время система может не работать нормально, и будут очереди.Риск блокировки, если количество элементов в очереди данных меньше 10% от вместимости, значит, система в это время работает нормально времени, а риск блокировки очереди низкий.
Если этот индикатор не выводится в журнал, разработчики и эксплуатационный и обслуживающий персонал не могут точно знать текущее рабочее состояние системы А (конечно, есть и другие способы получить этот индикатор, например, выставить его через http-интерфейс — это один из пути).
2) Хороший лог удобен для последующей эксплуатации и обслуживания, а разработчикам позволяет быстро локализовать онлайн-проблемы, ускорить установку стоп-лосса и снизить убытки, вызванные системными сбоями;
3) Другая функция журнала заключается в том, что его можно легко интегрировать с системой мониторинга, собирать журналы через систему мониторинга и получать соответствующие показатели производительности работы системы, что способствует анализу узких мест производительности системы и избеганию риски заранее;
Например:
Если есть система молла, то на начальном этапе БД предоставляет услуги через 2 сервера (1 мастер и 1 слейв), в это время большинство интерфейсов могут отвечать на запросы пользователей в течении секунд. С течением времени количество пользователей системы торгового центра постепенно увеличивалось, количество одновременных запросов и объем записи в определенной степени увеличивались, а также постепенно увеличивался объем данных в базе данных, что приводило к все более и более медленным запросам. для некоторых операторов SQL.Внезапно однажды ведомая машина базы данных была отключена из-за слишком большого количества медленных запросов и полностью отключена, что привело к недоступности службы торгового центра.
Если система торгового центра фиксирует в журнале время, затрачиваемое на каждый HTTP-запрос, настраивает сбор журналов через систему мониторинга и настраивает соответствующие сигналы тревоги, то узкое место в производительности системы, вызванное ростом бизнеса, может быть обнаружено заранее, и оптимизация системы может быть выполнена. выполняется заранее (например, расширение машины, оптимизация операторов SQL, подтаблица подбазы данных и т. д.), чтобы избежать рисков.
4) Удобно подсчитывать данные индекса, связанные с бизнесом, а также проводить соответствующий бизнес-анализ и оптимизацию функций.
Например:
Например, поисковая система хочет подсчитать долю использования поиска в разных регионах (таких как северный и южный регионы) за прошедшую неделю.Если IP-адрес каждого поискового запроса печатается в самом журнале, его легко В противном случае необходимо снова подключиться к сети и добавить журналы для подсчета.
Поэтому каждый должен обратить внимание на стандартизацию записи лога в процессе ежедневного написания кода, чтобы он мог сыграть свою должную роль, при этом помогая обеспечить стабильную работу наших сервисов, он мог эффективно повысить эффективность последующего обслуживания системы. .
2. Как правильно распечатать логи программы?
Далее поговорим о том, как правильно печатать логи из следующих аспектов.
2.1 Именование файла журнала
Вообще говоря, имена файлов журналов могут включать следующую ключевую информацию:
类型标识(logTypeName)
日志级别(logLevel)
日志生成时间(logCreateTime)
日志备份编号(logBackupNum)Идентификатор типа: Относится к функции или назначению этого файла журнала, например веб-службы, журнал, в котором записан http-запрос, обычно называется request.log или access.log, запрос и доступ — это идентификаторы типа, а журнал java gc — обычно называется gc.log, это понятно с первого взгляда, а журналы, которые обычно используются для записи общей работы службы, обычно называются по имени службы (serviceName, appKey) или имени компьютера (hostName), например nginx. бревно;
уровень журнала: это рекомендуемый способ непосредственно различать уровень по файлу при печати журнала.Если вы печатаете все уровни журналов в один и тот же файл журнала, вам необходимо выполнить поиск в файле при обнаружении проблемы, что относительно громоздко. Уровни журнала обычно включают пять уровней: DEBUG, INFO, WARN, ERROR и FATAL. При написании кода может быть выбран режим строгого соответствия или режим нестрогого соответствия. Режим строгого соответствия означает, что в журнале печатаются только журналы INFO и журналы ERROR. файл журнала INFO. Файл печатает только журнал ERROR; файл журнала INFO может печатать журнал INFO, журнал WARN, журнал ERROR и журнал FATAL в режиме нестрогого соответствия, а файл журнала WARN может печатать журнал WARN, журнал ERROR, журнал FATAL и так далее.
время генерации журнала: то есть к имени лог-файла добавляется время создания лог-файла, что удобно для сортировки при поиске лог-файлов;
резервный номер журнала: при резке бревна, если прокрутка основана на размере файла, вы можете добавить цифру в конец имени файла журнала;
2.2 Перекатывание бревен
Хотя в журнале может сохраняться ключевая информация во время работы системы, из-за ограниченного дискового пространства мы не можем хранить журнал без ограничений, поэтому должна существовать стратегия прокрутки журнала. Прокатка бревен обычно имеет следующие режимы:
Первый: прокрутка по времени
Второй: прокрутка в соответствии с размером одного файла журнала
Третий тип: прокрутка по времени и размеру одного лог-файла одновременно.
- Прокрутка по времени, то есть создание нового файла журнала в определенное время, обычно можно прокручивать на уровне часов или дней, какой метод зависит от объема печати системного журнала. Если системный журнал относительно невелик, можно использовать прокрутку на уровне дня; если дневной объем системы относительно велик, рекомендуется использовать прокрутку на уровне часа.
- Прокрутка в соответствии с размером одного файла журнала, то есть новый файл журнала создается каждый раз, когда файл журнала достигает определенного размера.Как правило, рекомендуется, чтобы размер одного файла журнала не превышал 500 МБ. файл журнала слишком велик, это может вызвать проблемы при мониторинге журнала или устранении неполадок.
- Прокрутка по времени и размеру одного файла журнала Этот режим обычно подходит для сценариев, когда вы хотите хранить журналы в течение определенного периода времени, но не хотите, чтобы один файл журнала был слишком большим. Например, logback обеспечивает этот режим конфигурации, вы можете обратиться к:войти обратно.QoS.eat/manual/app E…
Для стратегии чередования журналов есть два ключевых параметра: максимальное количество зарезервированных журналов и максимальное дисковое пространство. Не забудьте установить эти два параметра, если они не установлены, велика вероятность, что диск онлайн-машины будет заполнен.
2.3 Уровни журнала
Уровни журнала обычно следующие:
отладка/трассировка, информация, предупреждение, ошибка, фатальная
Программы серьезности этих уровней журнала увеличиваются в следующем порядке:
debug/trace: журналы на уровне отладки и трассировки обычно не подходят для производственных онлайн-сред из-за большого объема печатного содержимого, но обычно используются для ранней отладки автономной среды. Даже если будет использоваться онлайн-среда, ее необходимо контролировать с помощью переключателя, и она включается только при обнаружении и отслеживании онлайн-проблем;
info: информационный журнал обычно используется для записи состояния ключа, ключевой бизнес-логики или ключевых исполнительных узлов работы системы. Но имейте в виду, что нельзя злоупотреблять информационным журналом.Если информационный журнал используется неправильно, он мало чем отличается от журнала отладки/трассировки.
предупреждение:Журнал предупреждений обычно используется для записи некоторых непредвиденных ситуаций во время работы системы.Как следует из названия, он используется в качестве предупреждения, чтобы напомнить разработчикам и эксплуатационному и обслуживающему персоналу, что им нужно обратить внимание, но с ними можно справиться немедленно. без вмешательства человека.
error: журнал ошибок обычно используется для записи некоторых распространенных ошибок во время работы системы.После возникновения этих ошибок это означает, что нормальный доступ или использование пользователя нарушены, что обычно означает, что требуется вмешательство человека. Однако во многих случаях в производственной среде не обязательно, чтобы журнал ошибок требовал ручного вмешательства и немедленной обработки.Как правило, всестороннее суждение делается на основе количества и продолжительности журнала ошибок.
fatal: Это фатальная ошибка системы, обычно означает, что система фактически зависает и требует немедленного ручного вмешательства.
Давайте рассмотрим простой пример для иллюстрации.Если у нас есть такой сценарий, у нас есть система расчета заработной платы.1-го числа каждого месяца нам нужно получить данные о посещаемости всех сотрудников компании из системы посещаемости сотрудников, а затем рассчитать фонд заработной платы за последний месяц на основе данных о посещаемости.Зарплата, тогда должна быть функция для получения данных о посещаемости сотрудников из системы посещаемости:
public Map<Long, Double> getEmployeeWorkDaysFromAttendance(int year, int month, Set<Long> employeeList) throws BusiessException {
// 入口关键日志,需要打印关键的参数,因为employeeList可能数量较大,所以次数没有直接打印employeeList列表内容,只打印了size
logger.info("get employee work days, year:{}, month:{}, employeeList.size:{}", year, month, employeeList.size());
// 如果需要临时检验员工列表,可以把debug日志开关打开
if (debugOpen()) {
logger.debug("employ list content:{}", JSON.toJsonString(employeeList));
}
int retry = 1;
while (retry <= MAX_RETRY_TIMES) {
try {
Map<Long, Double> employeeWorkDays = employeeAttendanceRPC.getEmployeeWorkDays(year, month, employeeList);
logger.info("get employee work days success, year:{}, month:{}, employeeList.size:{}, employeeWorkDays.size:{}", year, month, employeeList.size(), employeeWorkDays.size());
return employeeWorkDays;
} catch (Exception ex) {
logger.warning("rpc invoke failed(employeeAttendanceRPC.getEmployeeWorkDays), retry times:{}, year:{}, month:{}, employeeList.size:{}", retry, year, month, employeeList.size(), ex);
// 连续重试失败之后,向上跑出异常
// 对于没有异常机制的语言,此处应该打印error日志
if (retry == MAX_RETRY_TIMES) {
throw new BusiessException(ex, "rpc invoke failed(employeeAttendanceRPC.getEmployeeWorkDays)");
}
}
retry++;
}
}2.4 Выбор времени печати журнала
Поскольку журнал должен помочь нам понять текущее рабочее состояние системы и найти онлайн-проблемы, время печати журнала очень важно.Если журнал используется неправильно, это приведет к слишком большому количеству содержимого журнала и повлияет на эффективность решения проблемы. местоположение; легко привести к отсутствию ключевых журналов, так что корень проблемы не может быть найден при поиске проблемы в Интернете. Поэтому очень важно понимать время печати журнала.Следующее является общим временем, подходящим для печати журналов:
1) вызов http или вызов интерфейса rpc
Когда программа вызывает другие службы или системы, ей необходимо распечатать параметры вызова интерфейса и результаты вызова (успех/неудача).
2) Программное исключение
Когда в программе возникает исключение, вы можете либо выдать исключение вверх, либо вы должны распечатать информацию о стеке исключений в блоке catch. Однако следует отметить, что повторно лог исключений лучше не печатать, например, в блоке catch вверх выбрасывается исключение, а печатается лог ошибок (кроме входа функции внешнего интерфейса rpc) .
3) Специальная условная ветвь
Когда программа входит в некоторые специальные условные ветви, такие как специальные ветви или переключатели. Например, мы рассчитываем зарплату на основе стажировки:
public double calSalaryByWorkingAge(int age) {
if (age < 0) {
logger.error("wrong age value, age:{}", age);
return 0;
}
// ..
}Теоретически продолжительность обслуживания не может быть меньше 0, поэтому необходимо распечатать эту непредвиденную ситуацию.Конечно, можно также создать исключение.
4) Критические пути выполнения и промежуточные состояния
Информация о ключевом журнале также должна быть записана в некоторых ключевых путях выполнения и промежуточных состояниях.Например, алгоритм может быть разделен на множество шагов, и необходимо записать промежуточный результат каждого шага, чтобы облегчить последующее позиционирование и отслеживание состояние выполнения алгоритма.
5) Запрос входа и выхода
Журнал входа/выхода необходимо распечатывать при входе/выходе функции или внешнего интерфейса, что удобно для последующей статистики журнала и более удобно для контроля состояния работы системы.
2.5 Содержание и формат журнала
Время — это журнал печати, позволяющий найти проблему в соответствии с журналом, а содержимое журнала определяет, следует ли быстро определить причину проблемы в соответствии с журналом, поэтому содержимое журнала имеет решающее значение. Вообще говоря, строка журнала должна включать как минимум следующие компоненты:
logTag, параметр, exceptionStacktrace
logTag — это тег журнала, который используется для определения сценария или причины вывода журнала, param — параметр вызова функции, а exceptionStacktrace — стек исключений. Например:
- good case
public class HttpClient {
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
private static int CONNECT_TIMEOUT = 5000; // unit ms
private static int READ_TIMEOUT = 10000; // unit ms
public static String sendPost(String url, String param) {
OutputStream out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("charset", "UTF-8");
out = new PrintWriter(conn.getOutputStream());
out.print(parm);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception ex) {
// 有关键logTag,有参数信息,有错误堆栈
LOG.error("post request error!!!, url:[[}], param:[{}]", url, param, ex);
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
LOG.error("close stream error!!!, url:[[}], param:[{}]", url, param, ex);
}
return result;
}
}
}
- bad case
public class HttpClient {
private static final Logger LOG = LoggerFactory.getLogger(HttpClient.class);
private static int CONNECT_TIMEOUT = 5000; // unit ms
private static int READ_TIMEOUT = 10000; // unit ms
public static String sendPost(String url, String param) {
OutputStream out = null;
BufferedReader in = null;
String result = "";
try {
URL realUrl = new URL(url);
URLConnection conn = realUrl.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("charset", "UTF-8");
out = new PrintWriter(conn.getOutputStream());
out.print(parm);
out.flush();
in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (Exception ex) {
// 没有任何错误信息
LOG.error("post request error!!!");
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
LOG.error("close stream error!!!");
}
return result;
}
}
}
Кроме того, для внешнего интерфейса http или интерфейса rpcЖелательно иметь requestId для каждого запроса, чтобы отслеживать все последующие пути выполнения каждого запроса.
Справочная статья:
blog.CSDN.net/ZOL Forum has/Aretti…
ву ву Краткое описание.com/Fear/59Chengdu61Retribution 9…
у-у-у. Краткое описание.com/afraid/6149463 ах ах…
woo woo woo.cn blog on.com/KOF learning method/afraid/37…
git book.can /books/5 ах ах 68…
呜呜.CN блог на .com / хочу ребенка / страх / 79 ...
, Блог Brother Space.com/ Программист - почему - как это ...
Woohoo. Видите лазейки. Способность /digest/Java…
blog.CSDN.net/eight o’clock_with/areti…