Console.log日志的改造之路
写在前面
移动端调试大家都用什么呢?相信大家跟我们组选择的工具是一样的,Vconsole.js,使用简单,只需引入就可以在移动端网页内提供前端调试面板,展示log/network等信息,方便开发者定位问题。
本可以愉快的玩耍,那为什么我们要改造log呢,因为谁也想不到,vconsole调试面板在低端机(性能拉跨,但是项目就要承载╮(╯▽╰)╭ )上因为前端的的打印日志过多导致程序卡死/崩溃,测试大佬忍无可忍,要一个好用的日志查看。好吧,我们的锅,我们来修。 o(╯□╰)o
分析现象
vconsole在测试环境是跟随项目一起启动,所有的前端日志一直存在在调试面版上,正常的日志不会对程序有影响,但是在项目中,我们有签名或者人脸识别等功能,有些接口会有传base64或者html字符串这些数据,前端没做处理直接打印到面板上,这些base64或者一些字符串数据量大,机子性能不行,滑两三下直接就卡死页面上,而且这些长度十分长的字符串对于在定位分析上,也没有多大作用,是要对其做下处理。
解决思路
- 对base64进行处理 项目中遇到调试面板遇到滑动卡顿得时候,一般都是打印base64字符串造成得,搞个函数解决他,让base64做下截取
/**
* 对base64进行处理
* @param {string ,object, array} data 处理数据
**/
export function base64Filter(data) {
if (Object.prototype.toString.call(data) === '[object Object]'
|| Object.prototype.toString.call(data) === '[object Array]') {
let applyObj
try {
applyObj = JSON.parse(JSON.stringify(data)) // 做个对象的复制 避免影响真实数据
} catch (error) {
return applyObj
}
for (const key in applyObj) { // 遍历数据
if (applyObj.hasOwnProperty(key)) {
if (Object.prototype.toString.call(applyObj[key]) === '[object Object]'
|| Object.prototype.toString.call(applyObj[key]) === '[object Array]') {
applyObj[key] = base64Filter(applyObj[key])// 递归匹配
} else { // 限制参数匹配
if (typeof applyObj[key] === 'string'
&& applyObj[key].indexOf('data:image') > -1) {
applyObj[key] = 'base64length' + applyObj[key].length // 将base64转为长度显示
}
}
}
}
return applyObj
} else if (Object.prototype.toString.call(data) === '[object String]') { // 字符串类型
let stringdata = data
const re = /{[\S\s]*}/
const match = stringdata.match(re) // 匹配对象
if (match) { // 匹配到可解析对象
try { // 尝试对 对象解析 过滤
let strobj = base64Filter(JSON.parse(match[0]))
strobj = JSON.stringify(strobj)
stringdata = stringdata.replace(re, strobj)
return stringdata
} catch (error) { // 异常则不处理数据
return data
}
} else { // 无法解析对象得字符串
stringdata = stringdata.indexOf('data:image') > -1
? stringdata.substr(0, stringdata.indexOf('data:image')) + '+base64length' + 、stringdata.length
: stringdata
return stringdata
}
} else { //其他类型不处理
return data
}
}
函数可以用,专门争对base64格式进行长度得限制。但是,先前说了,还有一些接口返回html字符串或者其他超长字符,这也是会可能导致卡顿,不能单单争对把bas64,我们要把危险扼杀到摇篮里。
- 对全属性对象做个长度限制 判断log类型,尝试去解析成对象,发现某个字段长度超过一定长度,做截取操作。
/**
* 对全属性对象进行处理
* @param {string ,object, array} data 处理数据
**/
export function objectValLimit(data) {
if (Object.prototype.toString.call(data) === '[object Object]'
|| Object.prototype.toString.call(data) === '[object Array]') { // 原始对象处理
let strobj
try {
strobj = JSON.parse(JSON.stringify(data))
} catch (e) {
return data
}
strobj = valueFilter(strobj)
return strobj
} else if (Object.prototype.toString.call(data) === '[object String]') { // 字符串处理
const re = /{[\S\s]*}/
let str = data
const match = str.match(re) // 匹配对象
if (match) {
try {
let strobj = match[0]
strobj = valueFilter(JSON.parse(strobj))
strobj = JSON.stringify(strobj)
str = str.replace(re, strobj)
return str
} catch (error) {
// 对象解析异常
str = str.length > 1000 ? str.substr(0, 1000) + '+length' + str.length : str
return str
}
} else { // 普通字符串
str = str.length > 200 ? str.substr(0, 200) + '+length' + str.length : str
return str
}
} else { // 其他类型 不处理
return data
}
}
/* 值长度限制 */
const valueFilter = function(data) { // 对象传入
for (const key in data) {
if (data.hasOwnProperty(key)) {
if (Object.prototype.toString.call(data[key]) === '[object Object]'
|| Object.prototype.toString.call(data[key]) === '[object Array]') {
data[key] = valueFilter(data[key])// 递归匹配
} else if (data[key] && data[key].length > 100) { // 限制参数长度
data[key] = data[key].substr(0, 100) + '+length' + data[key].length
}
}
}
return data
}
- 改写console.log 函数
let config = { // 配置项
isLog: true, // 是否打印日志
isallLimit: true //是否全部限制
}
console.log = (function(logFunc) {
return function() {
if(!config.isLog)return; // 不输出日志
try {
const arr = []
arr.push(...arguments)
arr.forEach((item, index) => {
arr[index] = config.isallLimit ? objectValLimit(item) : base64Filter(item)
})
logFunc.call(console, ...arr)
} catch (e) {
console.warn(e)
}
}
})(console.log)
事情到这,就该告一段落了,,对于前端输出得大部分日志都能良好响应,实现效果如下图
对象匹配:
字符串匹配:
不久后,测试大佬对log没意见,对调试面板得network提出意见了,要把那些接口入参和出参 也像log那样处理下长度,欣(nei)然(liu)应(man)允(mian)了。:-
对于network得处理,同理操作。拉下vconsole源码,关于网络得处理在network.js文件下,我们在window.XMLHttpRequest.prototype.send函数里对于post请求加上跟log一样得处理函数。然后打包就好
修改后send函数
// mock send()
window.XMLHttpRequest.prototype.send = function() {
let XMLReq = this;
let args = [].slice.call(arguments),
data = args[0];
let item = that.reqList[XMLReq._requestID] || {};
item.method = XMLReq._method.toUpperCase();
let query = XMLReq._url.split('?'); // a.php?b=c&d=?e => ['a.php', 'b=c&d=', '?e']
item.url = query.shift(); // => ['b=c&d=', '?e']
if (query.length > 0) {
item.getData = {};
query = query.join('?'); // => 'b=c&d=?e'
query = query.split('&'); // => ['b=c', 'd=?e']
for (let q of query) {
q = q.split('=');
item.getData[ q[0] ] = decodeURIComponent(q[1]);
}
}
if (item.method == 'POST') {
// save POST data
if (tool.isString(data)) {
let arr = data.split('&');
item.postData = {};
// 加上长度处理
item.postData[tool.objectValLimit(arr[0])] = undefined;
// for (let q of arr) {
// q = q.split('=');
// item.postData[ q[0] ] = q[1];
// }
} else if (tool.isPlainObject(data)) {
item.postData = data;
}
}
if (!XMLReq._noVConsole) {
that.updateRequest(XMLReq._requestID, item);
}
return _send.apply(XMLReq, args);
};
};
实际执行效果如图:
Log日志改造之路告一段落了,路漫漫其修bug远兮,吾将上下而求索。
感谢阅读,如果还有什么疑问或者建议,请多多交流,原创文章,文笔有限,文中若有不正之处,万望告知指正。