Console.log日志的改造之路

3,511 阅读4分钟

Console.log日志的改造之路

写在前面


  移动端调试大家都用什么呢?相信大家跟我们组选择的工具是一样的,Vconsole.js,使用简单,只需引入就可以在移动端网页内提供前端调试面板,展示log/network等信息,方便开发者定位问题。
  本可以愉快的玩耍,那为什么我们要改造log呢,因为谁也想不到,vconsole调试面板在低端机(性能拉跨,但是项目就要承载╮(╯▽╰)╭ )上因为前端的的打印日志过多导致程序卡死/崩溃,测试大佬忍无可忍,要一个好用的日志查看。好吧,我们的锅,我们来修。 o(╯□╰)o

分析现象


  vconsole在测试环境是跟随项目一起启动,所有的前端日志一直存在在调试面版上,正常的日志不会对程序有影响,但是在项目中,我们有签名或者人脸识别等功能,有些接口会有传base64或者html字符串这些数据,前端没做处理直接打印到面板上,这些base64或者一些字符串数据量大,机子性能不行,滑两三下直接就卡死页面上,而且这些长度十分长的字符串对于在定位分析上,也没有多大作用,是要对其做下处理。

解决思路


  1. 对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,我们要把危险扼杀到摇篮里。

  1. 对全属性对象做个长度限制   判断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
}
  1. 改写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远兮,吾将上下而求索。

感谢阅读,如果还有什么疑问或者建议,请多多交流,原创文章,文笔有限,文中若有不正之处,万望告知指正。