重写console.log
完整代码见底部
阅读开源库的时候,发现别人会设计自己内置的 log 方法,比如 vue-cli 工具会有自己的 logger,这样就可以定制 console.log,形成自己的风格。
基于此,我想到了两个功能点,并查找了相关博客,形成了这篇笔记。功能需求是:
- 在每次 log 时,输出当前时间,这样可以比较前后 log 之间的时间差别
- 时间 log 标记为特殊样式,以示区分
但是,我不想要改动原来项目中已有的 log,即不需要调用方额外引入一个工具函数,并调用。答案是在 console.log 使用前,劫持 console.log,时间及其样式均在此完成。
劫持 console.log
一般认为是重写原型,这里不是,而是直接重写该方法。通过调用自执行函数来完成,并在该函数中做处理,如下:
console.log = (function(logFunc) { return function() { logFunc.call(console, 'this is my log', ...arguments) } })(console.log)
1
2
3
4
5输出为
console.log(123) // this is my log 123
1加上时间
由于自执行函数返回了函数,所以内部的函数不会立即执行,在这里可以获取当前时间。
console.log = (function(logFunc) { return function() { const now = new Date() const time = now.toLocaleString() + '.' + now.getMilliseconds() logFunc.call(console, time, ...arguments) } })(console.log) // 2022/11/27 16:11:05.285 123
1
2
3
4
5
6
7
8看上去有点长,并且我们输出时间一般是为了比较前后 log 的时间(比如页面进入到页面离开,或者接口请求到接口返回),所以这里日期可以去掉。
- const time = now.toLocaleString() + '.' + now.getMilliseconds() + const time = now.toLocaleTimeString() + '.' + now.getMilliseconds() // 16:14:04.708 123
1
2
3在实践中发现,有时候毫秒的位数不一样,看起来不整齐。
16:17:38.95 123 16:17:38.397 123
1
2做下优化:
const now = new Date() let ms = now.getMilliseconds() + '' let len = ms.length while (len < 3) { ms = '0' + ms len++ } const time = now.toLocaleTimeString() + '.' + ms
1
2
3
4
5
6
7
8看起来就整齐了,切口一致。
16:19:43.036 123 16:19:43.335 123
1
2深拷贝
前端调试时经常会打印前后的对象,比较他们的值和内部属性,但经常发现要么由于页面切换等原因,该变量早就销毁,要么前后该变量内的值一模一样。是因为 js 引用指向同 个地址,前后输出的实际是同一个对象,这里可以坐下深拷贝来规避。举例:
const obj = { a: 1, } console.log(obj) // obj.a = 2 obj.a = 2 console.log(obj) // obj.a = 2
1
2
3
4
5
6这里简单做下深拷贝:
const arr = [...arguments] arr.forEach((item, index) => { if (Object.prototype.toString.call(item) === '[object Object]' || Object.prototype.toString.call(item) === '[object Array]') { arr[index] = JSON.parse(JSON.stringify(item)) } }) logFunc.call(console, time, ...arr)
1
2
3
4
5
6
7这下就是完全不同的值了。
美化
这里的时间和正常的 log 样式完全一样,log 多了可能会视觉疲劳,想看自己的 log 还要费一点劲,这里做下美化,以示区分。
我们打开
baidu.com
的控制台,发现它就是做了美化,实际就是将 css 样式往上造。const style = `background:#2d8cf0; padding: 2px; border-radius: 4px;color: #fff;` logFunc.call(console, `%c ${time}`, style, ...arr)
1
2加了样式
这个颜色太深了,容易吸引开发者的注意,实际上时间只是少数场景下会关注 的。换个样式。
const style = 'color:#909399;
1这样既不会影响到阅读,也可以很好的区分。
最后
改造 console.log 到这里就算完成了,但是还有点不完美的地方,是原来的堆栈信息丢失了,所有的堆栈都会定位到封装的自执行函数内部,造成调试的不方便。临时的 hack 方 案是,紧接着输出该日志的堆栈信息,但这有会作为冗余,所以默认不开启。完整代码如下:
console.log = (function(logFunc, debug = false) { return function() { const now = new Date() let ms = now.getMilliseconds() + '' let len = ms.length while (len < 3) { ms = '0' + ms len++ } const time = now.toLocaleTimeString() + '.' + ms const arr = [...arguments] arr.forEach((item, index) => { if (Object.prototype.toString.call(item) === '[object Object]' || Object.prototype.toString.call(item) === '[object Array]') { arr[index] = JSON.parse(JSON.stringify(item)) } }) const style = 'color:#909399;' logFunc.call(console, `%c ${time}`, style, ...arr) debug && console.trace() } })(console.log)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22