Речь пойдет не о различных вариантах ведения консольного журнала, а о попытке создать организованный способ работы с ним в различных средах в браузерном JavaScript-приложении, будь то Angular, Vue, React или BinCurse.
Почему именно консоль?
JavaScript устаревает, и все же лучший способ узнать, что происходит, — это простой console.log в нужном месте. Читайте на сайте Chrome Dev все, что вам нужно знать о консоли. Отказ от ответственности, я не нахожу реальной пользы от большинства из них.
Работая со сложными фреймворками, которые абстрагируют ванильный JavaScript, нам нужно записывать все в консоль во время разработки, и это главное «почему».
Что записывать в консоль?
Вот несколько примеров того, что нужно записывать в консоль:
-
Запрос и ответ на HTTP-вызов
-
Обновление состояния
-
Последовательность инициализации
-
Готовность конфигурации
-
Специальные сторонние события протоколирования.
-
Временные сообщения о внимании
Нам также нужен способ различать общие журналы, предупреждения и ошибки.
Куда в консоль?
Мы хотим отключать журналы при изменении окружения, особенно в продакшене. Поэтому console.log
не должен вызываться напрямую, а должен быть обернут в другую функцию, которая различает окружение.
Скрипт
Работая в обратном направлении, нам нужен способ сделать это:
ourFunction(object);
Мы также хотим вызывать сообщение, но сообщение необязательно:
ourFunction(object, message);
И мы хотим различать разные типы протоколирования:
ourFunction.log(object, message);
ourFunction.info(object, message);
Но мы не хотим связывать их слишком жестко, поэтому мы просто передадим тип как необязательный параметр.
ourFunction(object, message, type);
Итак, давайте напишем нашу функцию
function ourFunction(o, message, type) {
// switch type to have different flavors
console.log(message, o);
}
Первым улучшением, очевидно, является имя функции. Я назову ее _debug
. После многих лет использования различных имен, столкновений с третьими лицами и путаницы с журналом, это имя выдержало испытание временем.
Конечно, вы можете возразить, почему бы не поместить его в отдельный модуль? Вы можете. Я отказываюсь. Я хочу что-то такое же тупое, как console.log
, даже еще тупее.
Второе усовершенствование — это добавление формата к логам, чтобы они выглядели примерно так:
console.log("%c " + message, style);
Что касается типов, то основные типы, которые мы хотим записывать в журнал, это общая информация (мы можем добавлять новые стили по каждому запросу), ошибки и сбои, трассировки, результаты вызовов API и предупреждения.
Третье усовершенствование — это определение переменной перед записью в журнал, переменной, которая в продакшене имеет значение false. И нет, эта переменная не берется напрямую из окружения, позже я расскажу почему. Это может быть глобальная константа:
window._indebug
Putting it together:
function _debug(o, message, type) {
if (window && window._indebug) {
switch (type) {
case 'e': // error
console.log('%c ' + message, 'background: red; color: #fff', o);
break;
case 't': // trace error
console.trace('%c ' + message, 'background: red; color: #fff', o);
break;
case 'p': // http response
console.info('%c ' + message, 'background: #222; color: #bada55', o);
break;
case 'w': // warning
console.warn('%c ' + message, 'background: #4f560f; color: #e6ff07', o);
break;
case 'gtm': // gtm events, special
console.info('%cGTM: ' + message, 'background: #03dbfc; color: #000', o);
break;
default:
console.info('%c ' + message, 'background: #d9d9d9; color: #a82868; font-weight: bold;', o);
}
}
}
// set _indebug to true in your environment file, could be anywhere
window._inebug = !process.env.production
Пример настройки окружения в Angular, находится в файле /environment.ts
напрямую, а так как он в typescript и может работать на SSR, проверяем наличие окна, и обращаемся к нему косвенно.
window && (window['_indebug'] = true);
Последнее усовершенствование заключается в добавлении отдельной функции для «внимания», которая необходима во время разработки. Это выглядит по-другому, и может быть легко найдено и удалено из кода. Эти функции не должны существовать, они существуют для того, чтобы заставить нас отказаться от привычки постоянно использовать «console.logging».
function _attn(o, message) {
if (window && window._indebug) {
// use debug to filter in console window
console.debug('%c ' + message, 'background: orange; font-weight: bold; color: black;', o);
}
}
Typescript
Чтобы сделать его доступным в typescript, нам нужно объявить его в файле типизации, это может быть typings.d.ts
, который включен в tsconfig
. Скрипт по-прежнему написан на JavaScript и добавлен в файлы, включенные в сборку, потому что, как вы уже знаете, он должен оставаться немым!
// in typing.d.ts
declare function _debug(o: any, message?: string, type?: string): void;
declare function _attn(o: any, message?: string): void;
Использование
Самое популярное использование пользовательского консольного логирования — это перехват запросов на выборку. Ванильный способ перехватить все вызовы fetch — перезаписать fetch. Давайте сделаем это:
const oFetch = window.fetch;
window.fetch = async (...args) => {
const response = await oFetch(...args);
// let's console here, something like resource url and response
_debug(response, 'GET ' + args[0], 'p');
if (!response.ok) {
// log errors
_debug(response.statusText, 'Error', 'e');
return Promise.reject(response);
}
return response;
};
// when used in client
fetch('https://saphire.sekrab.com/api/cats')
.then((response) => response.json())
.then((json) => {
// use _attn for temporary logging
_attn(json, 'after json call');
});
При запуске в StackBlitz это выглядит следующим образом.
Другой пример — вызов события регистрации GTM:
function registerEvent(data){
// ... call dataLayer push, then log
_debug(data, 'register event', 'gtm');
}
Бэкдор
Почему мы установили глобальную переменную? В SPA, после первой загрузки страницы, мы можем установить глобальную переменную в true в консоли devTools, и получить доступ к этим сообщениям на продакшене. Это безопасно, потому что в лог записывается только уже имеющаяся информация.
В других случаях, кроме SPA, или для того, чтобы переменная устанавливалась в true при загрузке, мы можем получить доступ к бэкдору с помощью параметра URL, и это опять же безопасно и безвредно:
const _insearch = window.location.search.indexOf('debug=true') > -1;
if (_insearch) {
_indebug = true;
}
Итак, теперь вы можете поделиться ссылкой с ?debug=true
в URL со своей командой, чтобы получить доступ к зарегистрированным сообщениям для лучшего понимания.
Angular
Мы можем использовать эту функцию в Angular для протоколирования изменений состояния, перехвата HTTP, обработки ошибок и многого другого. Возвращайтесь на следующей неделе, а может и раньше. 😴
Я заставил вас посмотреть? Не существует такого понятия, как BinCurse.
РЕСУРСЫ
- Формат и стиль сообщений в консоли
- Справочник по API консоли
- Перехват запросов и ответов JavaScript Fetch API
- На StackBlitz