Приручение консоли

Продолжая нашу консольную оболочку, в этой статье я буду использовать ее специально в приложении 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

Серия консолей