返回

RxJS调试第2部分:用日志调出问题

前端

日志没什么可兴奋的。然而,日志是获取足够信息以开始推断问题的直接方式,它不是靠猜的,而且它通常用于调试 RxJS 代码。本文是调试 RxJS 系列文章的第二篇,继调试 RxJS 第1部分: 工具篇之后,侧重于使用日志来解决实际问题。

在本文中,我将展示如何以一种不唐突的方式记录流值、错误和调试消息,使用工具来格式化日志并以一种有意义的方式显示它们。然后,我将详细介绍可以在 RxJS 中找到的各种日志记录选项,包括使用内置的日志记录操作符和行为主题,以及第三方库。

使用日志进行调试

日志通常以文本格式显示调试消息、警告和错误。尽管这通常足以满足开发人员的需求,但有时你可能还需要包含其他信息,例如可帮助你理解问题的源的元数据。

这正是 RxJS 中的 tap 操作符的用武之地。它允许你将观察函数附加到流中,以执行某些操作,例如记录流值或错误。

import { tap } from 'rxjs/operators';

const observable$ = rxjs.of(1, 2, 3).pipe(
  tap(value => {
    console.log(`The current value is ${value}`);
  }),
  tap({
    next: value => {
      console.log(`The next value is ${value}`);
    },
    error: err => {
      console.error(`The error is ${err}`);
    },
  })
);

observable$.subscribe({
  next: value => {
    console.log(`The final value is ${value}`);
  },
  error: err => {
    console.error(`The final error is ${err}`);
  },
});

如你所见,tap 操作符允许你分别指定针对正常值、错误和完成事件的不同观察函数。

格式化日志

直接将值和错误记录到控制台可能看起来很方便,但它很快就会变得难以阅读,尤其是在你的流产生大量数据的情况下。

RxJS 提供了一些内置的格式化工具,可以帮助你以更美观的方式呈现你的日志。例如,你可以使用 pretty 操作符来格式化一个流的值:

import { pretty } from 'rxjs/operators';

const observable$ = rxjs.of(1, 2, 3).pipe(
  pretty()
);

observable$.subscribe({
  next: value => {
    console.log(value);
  },
  error: err => {
    console.error(err);
  },
});

这将以以下格式输出日志:

{ value: 1 }
{ value: 2 }
{ value: 3 }

你还可以使用 timestamp 操作符来记录每个事件发生的时间:

import { timestamp } from 'rxjs/operators';

const observable$ = rxjs.of(1, 2, 3).pipe(
  timestamp()
);

observable$.subscribe({
  next: value => {
    console.log(value);
  },
  error: err => {
    console.error(err);
  },
});

这将以以下格式输出日志:

{ timestamp: 1651191475181, value: 1 }
{ timestamp: 1651191475182, value: 2 }
{ timestamp: 1651191475183, value: 3 }

显示日志

一旦你记录了你的日志,你当然需要能够看到它们。有几种方法可以做到这一点。

一种方法是使用浏览器的控制台。你可以在 Chrome 中通过按 Ctrl + Shift + J 来打开它,在 Firefox 中通过按 Ctrl + Shift + K 来打开它。

另一种方法是使用日志记录库。有很多这样的库可供选择,但我个人推荐 bunyan。它是一个高度可定制的日志记录库,允许你将日志发送到控制台、文件或其他目的地。

RxJS 中的日志记录选项

除了使用 tap 操作符和第三方库之外,你还可以使用 RxJS 中内置的日志记录选项。

日志记录操作符

RxJS 提供了一组日志记录操作符,可以用来以非侵入性方式记录流值、错误和调试消息。这些操作符包括:

  • auditTime:以给定的时间间隔记录流值。
  • buffer:将流值缓冲到一个数组中,然后以给定的时间间隔记录该数组。
  • catchError:捕获流中的错误并将其记录到控制台。
  • count:记录流中事件的数量。
  • debounceTime:在给定的时间间隔内抑制流值,然后记录最新值。
  • distinctUntilChanged:仅记录流中的唯一值。
  • do:执行给定的副作用,例如记录流值或错误。
  • exhaustMap:将流中的每个值映射到一个新的流,然后记录每个新流中的值。
  • filter:仅记录流中满足给定条件的值。
  • first:仅记录流中的第一个值。
  • groupBy:将流中的值分组并记录每个组。
  • ignoreElements:忽略流中的所有值并仅记录完成事件。
  • last:仅记录流中的最后一个值。
  • mapTo:将流中的每个值映射到一个给定值,然后记录该值。
  • mergeMap:将流中的每个值映射到一个新的流,然后记录每个新流中的值。
  • observeOn:将流调度到一个新的调度程序,然后记录流值。
  • partition:将流分成两个流,一个流包含满足给定条件的值,另一个流包含不满足给定条件的值,然后记录每个流中的值。
  • reduce:将流中的所有值减少为一个单一值,然后记录该值。
  • retry:在发生错误时重新订阅流,然后记录流值。
  • sample:在给定的时间间隔内记录流中的最新值。
  • scan:将流中的所有值累积为一个单一值,然后记录该值。
  • skip:跳过流中的给定数量的值,然后记录其余的值。
  • skipUntil:跳过流中的值,直到满足给定条件,然后记录其余的值。
  • startWith:在流开始时插入一个给定值,然后记录流值。
  • subscribeOn:将流订阅到一个新的调度程序,然后记录流值。
  • take:仅记录流中的给定数量的值。
  • takeUntil:仅记录流中的值,直到满足给定条件。
  • throttleTime:在给定的时间间隔内仅记录流中的最新值。
  • timeout:如果流在给定的时间间隔内不发出任何值,则记录一个错误。
  • window:将流分成多个窗口,然后记录每个窗口中的值。
  • withLatestFrom:将流中的每个值与另一个流中的最新值组合起来,然后记录组合后的值。

行为主题

行为主题是一个特殊的主题,它除了具有普通主题的所有功能外,还具有存储最新值的功能。这意味着你可以随时订阅行为主题并获取其当前值。

这使得行为主题非常适合于记录流值。你可以创建一个行为主题来存储流的最新值,然后随时订阅该主题以获取最新值。

import { BehaviorSubject } from 'rxjs';

const observable$ = rxjs.of(1, 2, 3);
const subject = new BehaviorSubject(0);

observable$.subscribe(subject);

subject.subscribe({
  next: value => {
    console.log(`The current value is ${value}`);
  },
  error: err => {
    console.error(`The error is ${err}`);
  },
});

这将以以下格式输出日志:

The