Настало время серьёзно поговорить про отладку. На протяжении многих курсов вы постоянно занимались отладкой, иногда часами, а иногда и днями не могли понять, почему всё работает не так, как должно работать. Это норма! (c) В повседневной практике такое случается не редко.
Обычно всё ещё хуже. Бага произошла на продакшене, и у вас уже нет возможности воспроизвести ситуацию. Чем сложнее система, тем меньше шансов что это возможно. Каким образом отлаживать код в такой ситуации? Здесь мы приходим к главной идее отладки. Правильная отладка — это хорошо настроенное логирование.
Есть общие практики, принятые во всех экосистемах, о которых мы сейчас поговорим. В этой истории немного особняком стоит именно js
. Основной способ логирования, принятый в js
, идет своим путём, который, на мой взгляд, достаточно неплох.
Классическое логирование выглядит так. Подключается специальная библиотека со следующим интерфейсом:
logger.debug("I'm a debug message!");
logger.info("OMG! Check this window out!");
logger.error("HOLY SHI... no carrier.");
Как правило, вызовы приводят к одному и тому же результату за исключением одного. У них разный уровень логирования. В самом логе вывод будет скорее всего таким:
[debug] I'm a debug message!
[info] OMG! Check this window out!
[error] HOLY SHI... no carrier.
Уровни логирования имеют следующую семантику:
- TRACE - дебаговые сообщения для определения flow
- DEBUG - дебаговые сообщения для определения conditions
- INFO - этапы нормального flow
- WARN - некритичные ошибки (но результат должен быть верным)
- ERR - ошибки, которые могут привести к неверному результату
- FATAL - "ща грохнусь!"
Чем ближе к началу списка, тем более подробный (говорят "verbose") лог. Важно, что в коде присутствуют все уровни, и вызовы проставляются самим программистом на основе его понимания работы программы. А вот дальше, уже во время эксплуатации программы, на уровне конфигурации, задаётся, какой уровень логирования необходимо поддерживать для данного запуска. Предположим, что стоит уровень info
, что типично для production
систем. Это значит, что в лог будут выводиться только те строчки, которые предназначены для уровней с info
по fatal
. Во время разработки уровень обычно debug
, на этом уровне выводится много отладочной информации, позволяющей понять как идут и преобразуются данные. А также по каким частям кода идет flow.
Логи — настолько важная часть всего программного продукта, что существует множество решений для сбора, агрегации, анализа логов, в том числе облачных.
Debug
А теперь забудьте всё, что я вам говорил до этого :D. Шутка. Но суровая правда в том, что в js
доминирует немного другой подход, что не отрицает возможности комбинирования.
Подход js
по сути завязан на конкретную библиотеку, которая предложила нестандартный способ работы.
import debug from 'debug';
const log = debug('http');
const name = 'App';
log('booting %s', name);
// http booting App
Первое, что бросается в глаза, это отсутствие уровней логирования. Второе: перед использованием логера импортируется специальная функция, которая вызывается так debug('http')
. Передаваемая строчка представляет из себя namespace
. Имя, которое, подобно уровню логирования, отделяет логи друг от друга. В отличие от уровней логирования, неймспейсы не имеют никаких ограничений, их может быть столько, сколько нужно, и называть их можно как угодно. Неймспейсы, по сути, отвечают за некоторую подсистему, по которой мы хотим логировать. На практике это оказывается крайне удобным подходом. Обычно мы примерно понимаем на каком уровне и в какой подсистеме произошла ошибка, и хочется выводить лог (подробный) только по этой подсистеме. В случае использования уровней логирования это сделать невозможно, и приходится выискивать нужные строчки в массе других. С использованием неймспейсов это естественный способ работы.
import debug from 'debug';
const httpRequestLog = debug('http:request');
const rpcLog = debug('rpc');
httpRequestLog('request');
rpcLog('action');
Но это ещё не всё. Неймспейсы могут быть вложенными. Это тоже крайне полезная фича. Обычно на верхнем уровне каждая библиотека определяет неймспейсом свое имя, а внутри уже идет разделение на конкретные подсистемы. Кроме того, в любой файл можно наимпортировать сколько угодно логов и использовать их в любых комбинациях.
А теперь самое главное. Так сложилось, что 99% самых популярных библиотек на js уже поставляются со встроенным логированием через debug
. По сути, нам не очень-то и оставили выбор. На картинке выше как раз видно кусок лога Express.
Последний вопрос касается того, как управлять этим выводом. По умолчанию debug
ничего не печатает. Чтобы это изменить, нужно передать переменную окружения DEBUG
следующим образом:
DEBUG=* bin/server.js
DEBUG=http:* bin/server.js
DEBUG=*,-not-this bin/server.js
Пример демонстрирует три разных варианта использования:
- Вывести все логи (это коснется не только вашего приложения, будут выведены логи всех библиотек входящих в ваше приложение)
- Вывести все логи внутри неймспейса
http
- Вывести все логи за исключением
not-this
неймспейса
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты