【问题标题】:How to handle logging of HttpClient requests?如何处理 HttpClient 请求的日志记录?
【发布时间】:2018-07-31 07:16:25
【问题描述】:

我正在使用 Angular 和 rxjs 5.x.x。对于每个 http 请求,我必须将响应记录在一个集中的位置 - 无论是成功还是失败。

我面临的问题是引用了 catch 子句。

例如:

  callRemote() {
    return this.http.get("http://aaa.dom")
                    .catch(f=>Rx.Observable.of(f))
                    .do(f => console.log("logging response both bad and ok..."))
  }

这是一个失败的http 请求。但我必须调用do 运算符,因为我需要记录它。 但是,如果调用了 .catch,则不会调用 .do。这就是我这样做的原因:

  .catch(f=>Rx.Observable.of(f))

所以我基本上是在捕捉错误并将其包装为一个新的 observable 并继续使用 .do 函数。

到目前为止一切顺利。

现在的问题是,最终,在订阅时,我想知道请求是失败还是正常。

所以基本上这就是我订阅的方式:

 this._http.callRemote().subscribe(
      (x => console.log('OK')),
      (x => console.log('ERROR')),
      (() => console.log('COMPLETE'))
    );

问题是一切都被重定向到sucess 回调函数。甚至失败。我明白为什么会发生 - 这是因为我将 catch 包装为新的 Observables 失败。

问题

包装错误是否有可能转到 error 回调,而不是 success 回调?

Online demo

【问题讨论】:

  • 如果你想让它继续出错,你为什么不Observable.throw它而不是把它变成一个成功的流?只需将其记录在您捕获它的位置即可。
  • @jonrsharpe 你的意思是.catch(f=>Rx.Observable.throw (f)) ?如果我这样做 - 则不会调用 do
  • 是的,完全正确。否则你可以例如将其包裹在某种对象中,例如{ error, result },然后再打开它,这似乎有点尴尬。
  • 捕获不应该首先吞下错误,这是肯定的。最终,您可以在捕获中记录然后重新抛出,但这对于仅记录副作用来说有点矫枉过正。最简单的选择是在 do 运算符中使用错误回调。
  • @Jota.Toledo 在do操作符中使用错误回调是什么意思?

标签: angular logging rxjs error-logging angular-httpclient


【解决方案1】:

正如在 cmets 中已经提到的,catch 不应该首先吞下错误,这是肯定的。通过这样做,您只是阻止了 rxjs 链中的错误信号。

最终,您可以在 catch 中进行日志记录,然后重新抛出,但这对于仅日志副作用来说有点矫枉过正。

最简单的选择是在do 运算符中使用错误回调参数。

根据this tutorial page,do操作符可以带3个参数(callback fns),基本匹配订阅签名:

1s 参数:下一个回调

第二个参数:错误回调

第三个参数:完成回调

所以你可以重构如下:

callRemote() {
    return this.http.get("http://aaa.dom")
                    .do(
                      response => console.log("logging response both bad and ok..."), 
                      error => console.log("Something exploded, call 911");
  }

因此,基本上您可以将该操作附加到基本代码中的每个 HttpClient 调用。很整洁,不是吗?

等等等等!乍一看,这可能看起来很简洁,但它会适得其反:

  • 修改日志记录行为
  • 以任何方式重构

为什么?

您将基本上使用该 do() 操作对每个可能的后端调用进行猴子修补。如果对该逻辑进行 1 次更改意味着在 3 个以上的地方更改代码,那么就会发生一些臭味。

更好的方法

随着 HttpClient 的引入,添加了另一个 API:HttpInterceptor API。

基本上,您可以一次拦截所有传出请求。

怎么样?如下:

第一步,创建一个可以用来封装日志逻辑的可注入服务;

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { AuthService } from './auth/auth.service';
import { Observable } from 'rxjs/Observable';
import { tap } from 'rxjs/operators'; // fancy pipe-able operators

@Injectable()
export class LoggingInterceptor implements HttpInterceptor {
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
       tap(
           response => console.log("Oh boy we got an answer"), 
           error => console.log("Something might be burning back there")
       ));
  }
}

第二步,通过令牌提供LoggingInterceptor,让angular知道它的存在:

import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { NgModule, ClassProvider } from '@angular/core';

import { LoggingInterceptor } from '../some-place';

const LOGGING_INTERCEPTOR_PROVIDER: ClassProvider = {
   provide: HTTP_INTERCEPTORS ,
   useClass: LoggingInterceptor,
   multi: true
};

@NgModule({
   ...
   providers: [
      LOGGING_INTERCEPTOR_PROVIDER
   ]
   ...
})
export class AppModule {}

就是这样!现在您可以记录所有发出的请求,并在必要时以真正集中的方式使用这些日志做一些其他很酷的事情。

【讨论】:

  • 等一下,我正在更新答案
  • BTW it works perfectly。现在我可以同时登录.do ok .do fail 并且它仍然重定向到订阅的错误回调——就像我想要的那样。 (从来不知道do 有错误回调:-) 谢谢)
  • 有一个错字
  • 谢谢)。 ,控制台没有显示。 非常非常感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 2020-10-27
  • 1970-01-01
  • 2011-06-22
相关资源
最近更新 更多