RxJS调试第2部分:用日志调出问题
2023-10-08 21:37:48
日志没什么可兴奋的。然而,日志是获取足够信息以开始推断问题的直接方式,它不是靠猜的,而且它通常用于调试 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