Приручение консоли
Продолжая нашу консольную оболочку, в этой статье я буду использовать ее специально в приложении Angular. Регистрация может быть полезна при RxJS
конвейерной обработке и регистрации ошибок.
Во-первых, давайте добавим скрипт в assets и объявим его в typings.d.ts
(как упоминалось в предыдущем посте).
{
"projects": {
"main": {
"architect": {
"build": {
"options": {
"scripts": [
// add the script however you wish
{
"input": "src/assets/js/console.js",
"bundleName": "script"
}
]
}
}
}
}
}
}
Это при запуске SSR приведет к ошибке на стороне сервера, мы исправим ее позже.
Финальный проект находится на StackBlitz
Угловая обработка ошибок
Обработчик ошибок по умолчанию в Angular регистрирует необработанную ошибку. Мы можем переопределить его с помощью нашего собственного ErrorHandler (предоставленного в корневом модуле приложения):
@Injectable()
export class OurErrorHandler implements ErrorHandler {
handleError(error: any) {
_debug(error, 'Unhandled Error', 'e');
}
}
Используйте его с RxJS
Мы можем создать пользовательский оператор, который выводит сообщение в канал:
export const debug = (message: string, type?: string): MonoTypeOperatorFunction<any> => { return pipe( tap(nextValue => { _debug(nextValue, message, type); }) ); };
// this is used with observables like this obs$.pipe( debug('obs value') );
В предыдущей статье про Управление состоянием на основе RxJS в Angular у нас был базовый класс для состояния. Мы можем обновить его с помощью оператора отладки, чтобы регистрировать все изменения состояния. В StateService
:
export class StateService<T> {
protected stateList: BehaviorSubject<T[]> = new BehaviorSubject([]);
stateList$: Observable<T[]> = this.stateList
.asObservable()
// pipe to debug the constructor used, like ParamState, or TransactionState
.pipe(debug(this.constructor.name));
}
Теперь любые обновления состояния будут регистрироваться в консоли.
HTTP-перехватчик
В отладке devTools неудачные попытки сети регистрируются, показывая полный URL-адрес, но не успешные попытки. Давайте добавим оператор отладки в HttpInterceptor
, чтобы регистрировать все данные так, как нам нравится, так и должно быть 😉
[метод] [URL] [текст ответа]
@Injectable() export class OurInterceptor implements HttpInterceptor { intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// adjust req if needed return next .handle(req) .pipe( // pipe with req information, method, and url with params debug(`${req.method} ${req.urlWithParams}`, 'p') ) } }
Давайте перепишем оператор отладки, чтобы он обрабатывал следующее:
- если
nextValue
имеет типHttpResponse
, нам нужно зарегистрировать тело. - нам также нужно отфильтровать бесполезное значение типа
Sent
. Это событие устанавливается перед каждым малозначительным http-ответом, и оно равноundefined
в других наблюдаемых.
export const debug = (message: string, type?: string): MonoTypeOperatorFunction<any> => { return pipe( tap(nextValue => { let value = nextValue;
if (nextValue instanceof HttpResponse) { // value is the body value = nextValue.body; } // just filter out the sent event if (nextValue && <any>nextValue.type !== HttpEventType.Sent){ _debug(value, message, type); }
}) ); };
Теперь мой лог выглядит так
Мы также можем зарегистрировать запрос body
в случае событий PUT
или POST
в перехватчике HTTP:
@Injectable() export class OurInterceptor implements HttpInterceptor { intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> {
// log request body, prefix to distinguish or create a new console type if (req.body) { _debug(req.body, `Request: ${req.method} ${req.urlWithParams}`, 'p'); }
return next.handle(adjustedReq).pipe( //... ); } }
Лог выглядит так
Давайте поднимем его на ступеньку выше. Позвольте мне также регистрировать ошибки Http, используя расширенный обратный вызов tap
, например:
export const debug = (message: string, type?: string): MonoTypeOperatorFunction<any> => { return pipe( tap({ next: (nextValue) => { let value = nextValue;
if (nextValue instanceof HttpResponse) { // value is the body value = nextValue.body; } // just filter out the sent event if (nextValue && <any>nextValue.type !== HttpEventType.Sent) { _debug(value, message, type); } }, error: (error) => { // in error, log erros, check for pecific type of http response errors let value = error; if (error instanceof HttpErrorResponse) { value = `${error.status} ${error.message}`; } _debug(value, message, 'e'); }, }) ); };
Прежде чем показать вам журнал, я вернулся к GTM Angular service, который мы создали на прошлой неделе, и также добавил оператор _debug
:
// GTM service updated from a previous post export class GtmTracking { // ... public static RegisterEvent(track: IGtmTrack, extra?: any): void { let data = { event: track.event, gr_track: { source: track.source, ...extra } }; // add a special type of log here _debug(data, 'register event', 'gtm'); this.Push(data) }
public static SetValues(values: any): void { let data = { gr_values: { ...values } }; // and here: _debug(data, 'Set GA value', 'gtm'); this.Push(data); } }
В ErrorService
мы вызвали событие регистрации GTM, так что теперь у нас есть хорошо выглядящий консольный журнал:
Объект ошибки можно улучшить, но это уже другая тема и другой пост.
Отфильтрованное представление консоли
Есть причина, по которой я выдаю console.log
ошибки вместо console.error
, а именно; плохая привычка фильтровать консоль в информационные сообщения и забывать вернуться ко всем сообщениям. Концентрация внимания постоянно сокращается. Таким образом, мы никогда не упускаем из виду ошибки и получаем некоторое здравомыслие на пенсии.
_attn
был создан с целью заменить обычный console.log, выходные данные вызовов _attn
отображаются в консольных подробных журналах, и они выглядят достаточно яркими, чтобы не забыть удалить их, прежде чем мы построим. Однако, если мы не сможем их удалить, ничего страшного, они все равно не будут отображаться в рабочей среде.
Это влияет на производительность? Не в 99% процентов приложений (99% — это как сказать полмира, а не реальная статистика).
Логи серверной платформы
Используемый скрипт представляет собой JavaScript, добавленный в сборку и внедренный в index.html
. Это сделано специально, потому что я не хочу import
каждый раз, когда использую. Есть еще одно преимущество такого подхода. Во-первых, давайте исправим серверную платформу, которая не работает, потому что эти методы не существуют в NodeJS. Это делается путем определения этих функций в объекте NodeJS global
. Экспресс-сервер NodeJS должен содержать следующее:
// fix NodeJs server running SSR
global._debug = function (o, message, type) {
if(process.env.NODE_ENV !== 'production') {
console.log(message, o);
}
};
global._attn = function (o, message) {
if(process.env.NODE_ENV !== 'production') {
console.log(message, o);
}
}
Теперь мы можем отображать сообщения на сервере после сборки, но до развертывания. Включить или выключить его без перестройки. Контекст — король, разделение — новая королева.
Спасибо, что дочитали до этого места. Дайте мне знать, если я разыграл какие-то неправильные руки.
РЕСУРСЫ
Также опубликовано на Sekrab Garage
Серия консолей
- Написание оболочки для console.log для лучшего управления в JavaScript, часть I
- Написание оболочки для console.log для лучшего контроля в Angular, часть II
- Отлов и обработка ошибок в Angular
- Отлов и отображение ошибок пользовательского интерфейса с всплывающими сообщениями в Angular
- Автоматическое скрытие всплывающего сообщения в Angular