Сбор данных об ошибках внешнего интерфейса (Vue.js, апплет WeChat)

внешний интерфейс Апплет WeChat Vue.js

предисловие

С развитием бизнеса компании появляется все больше фронтенд-проектов. Иногда требуется много времени, чтобы исследовать проблему, о которой говорят клиенты. Если неверная информация не может быть получена с первого раза, это в какой-то степени принесет компании убытки. В настоящее время нам нужен набор механизмов сбора ошибок, чтобы заблаговременно обнаруживать проблемы в коде и решать проблемы заранее, до получения отзывов от клиентов. Или, получая отзывы клиентов, вы можете найти соответствующий стек ошибок, чтобы помочь нам быстро найти и решить проблему. Далее в основном представлен метод сбора ошибок апплетов Vue и WeChat.

метод сбора ошибок

Коллекция ошибок Vue

Vue предоставляет глобальную конфигурацию errorHandler для сбора ошибок, возникающих при работе Vue. подробно APITalent.v UE JS.org/V2/API/#two…

Применение:

Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  //`err`是js错误栈信息,可以获取到具体的js报错位置。
  //`vm` vue实例
  //`info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
}

Конечно, после получения экземпляра, соответствующего Vue, мы можем легко получить имя компонента и пользовательские атрибуты, соответствующие Vue (вы можете свободно играть здесь).

Вот как получить имя компонента: (источник: fundebug)

function formatComponentName(vm) {
    if (vm.$root === vm) return 'root';
    var name = vm._isVue
        ? (vm.$options && vm.$options.name) ||
        (vm.$options && vm.$options._componentTag)
        : vm.name;
    return (
        (name ? 'component <' + name + '>' : 'anonymous component') +
        (vm._isVue && vm.$options && vm.$options.__file
            ? ' at ' + (vm.$options && vm.$options.__file)
            : '')
    );
}

На данный момент наш код можно переписать так:

Vue.config.errorHandler = function(err, vm, info) {
    if (vm) {
        var componentName = formatComponentName(vm);
        //调用错误日志收集接口
    } else {
        //调用错误日志收集接口
    }
};

Зная, как собирать информацию об ошибках, вам достаточно просто обернуть ее и применить к проекту. (Конечно, вам также нужен интерфейс для сохранения информации об ошибках и интерфейс для отображения информации об ошибках. В Интернете также есть несколько зрелых фреймворков для обработки журналов, например: ELK)

создать новый файлdebug.js

let debugConfig = {
    Vue: null,
    //项目名称
    entryName: 'entryName',
    //脚本版本
    scriptVersion: '1.0',
    // 环境
    releaseStage: 'pro'
},
debug = {
    notifyWarn({ message, metaData }) {
        let type = 'caught',
            severity = 'warn';
        
        _logReport({ type, severity, message, metaData });
    },
    notifyError({ type = 'caught', error, message, metaData, lineNumber, columnNumber, fileName }){
        let severity = 'error';

        _logReport({ type, severity, error, metaData, message, lineNumber, columnNumber, fileName });
    }
};

// 日志上报
function _logReport({ type, severity, error, metaData, message, lineNumber, columnNumber, fileName }) {

    let { silentDev, Vue } = debugConfig;

    message = message || error && error.message || '';

    //这里可以做一个灰度控制

    let { entryName, releaseStage, severity, scriptVersion } = debugConfig,
        name = error && error.name || 'error',
        stacktrace = error && error.stack || '',
        time = Date.now(),
        title = document.title,
        url = window.location.href,
        client = {
            userAgent: window.navigator.userAgent,
            height: window.screen.height,
            width: window.screen.width,
            referrer: window.document.referrer
        },
        pageLevel = 'p4';

    //此处可以给你的页面进行分级
    pageLevel = 'p0';//getPageLevel();

    //此处http请求使用的是vue-resource,可以根据各自的情况进行调整
    Vue.http.post(logReportUrl, {
        entryName,
        scriptVersion,
        message,
        metaData,
        name,
        releaseStage,
        severity,
        stacktrace,
        time,
        title,
        type,
        url,
        client,
        lineNumber,
        columnNumber,
        fileName,
        pageLevel//页面等级
    });

}

export default function(Vue, option = {}){

    debugConfig = Object.assign(debugConfig, { Vue, ...option });
    
    //如果你想在开发环境不去捕获错误信息 可以在此处加上环境判断

    function formatComponentName(vm) {
        if (vm.$root === vm) return 'root';
        let name = vm._isVue
            ? (vm.$options && vm.$options.name) ||
            (vm.$options && vm.$options._componentTag)
            : vm.name;
        return (
            (name ? 'component <' + name + '>' : 'anonymous component') +
            (vm._isVue && vm.$options && vm.$options.__file
                ? ' at ' + (vm.$options && vm.$options.__file)
                : '')
        );
    }

    Vue.config.errorHandler = function(err, vm, info) {
        if (vm) {
            let componentName = formatComponentName(vm);
            let propsData = vm.$options && vm.$options.propsData;
            debug.notifyError({
                error: err,
                metaData: {
                    componentName,
                    propsData,
                    info,
                    userToken: { userId: 1 }//metaData可以存一些额外数据,比如:用户信息等
                }
            });
        } else {
            debug.notifyError({
                error: err,
                metaData: {
                    userToken: { userId: 1 }//metaData可以存一些额外数据,比如:用户信息等
                }
            });
        }
    };
    
    window.onerror = function(msg, url, lineNo, columnNo, error) {
        debug.notifyError({
            type: 'uncaught', 
            error, 
            metaData: {
                userToken: { userId: 1 }//metaData可以存一些额外数据,比如:用户信息等
            }, 
            message: msg, 
            lineNumber: lineNo, 
            columnNumber: columnNo, 
            fileName: url
        });
    }

}

//最后我们把debug抛到外面供其他地方调用
export { debug }

Конечно, вы также можете перехватывать исключения, такие как промисы, сетевые запросы, изображения и т. д. Здесь рекомендуется относительно полная статья, можете проверить самиnuggets.capable/post/684490…

инициализация:

//错误日志收集
import debug from './debug';
//初始化错误处理
Vue.use(ngmmdebug, { entryName: 'webmall' });

Если вы хотите сообщить об ошибке самостоятельно, вы можете пройти через:

import { debug } from './debug';
debug.notifyError({ messag: '发生错误了' });

Коллекция ошибок программы WeChat Mini

Апплету WeChat также удобно собирать информацию об ошибках, нужно лишь реализовать метод onError в объекте, передаваемом при вызове функции App. Адрес документа:Developers.wechat.qq.com/mini Программа ...

Применение:

App({
  onError (msg) {
    console.log(msg);//msg就是报错信息
  }
})

Если вы хотите сделать код более переносимым, вы можете сделать это:

//将App暂存起来
let _App = App;

function HookParams(_appParams, eventName, eventFn) {
  if (_appParams[eventName]) {
    let _eventFn = _appParams[eventName];
    //如果参数中已经存在onError函数,则保留并且添加错误收集
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName);
      return _eventFn.call.apply(_eventFn, [this].concat(Array.prototype.slice.call(arguments)))
    }
  } else {
    //如果参数中不存在onError函数,那比较简单直接定义一个,并且加入错误收集
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName)
    }
  }
}

function onErrorFn(error, eventName) {
  //收集错误
}

App = function (_appParams) {
  HookParams(_appParams, "onError", onErrorFn);
  _App(_appParams);
};

Принцип на самом деле очень простой, нужно переписать функцию App, а затем обработать входящие параметры с помощью Hook.

Опять же, теперь, когда мы знаем, как собирать ошибки, нам просто нужно обернуть наш код.

создать новый файлdebug.js

function HookParams(_appParams, eventName, eventFn) {
  if (_appParams[eventName]) {
    let _eventFn = _appParams[eventName];
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName);
      return _eventFn.call.apply(_eventFn, [this].concat(Array.prototype.slice.call(arguments)))
    }
  } else {
    _appParams[eventName] = function (error) {
      eventFn.call(this, error, eventName)
    }
  }
}

function objToParam(options = {}) {
    let params = '';
    for (let key in options) {
        params += '&' + key + '=' + options[key];
    }
    return params.substring(1);
}

function onErrorFn(error, eventName) {
    _logReport(error);
}

// 将App暂存起来
let _App = App;

App = function (_appParams) {
  HookParams(_appParams, "onError", onErrorFn);
  _App(_appParams);
};

//config
let debugConfig = {
  entryName: 'entryName',
  releaseStage: 'pro',
  scriptVersion: '1.0',
  client: {}
}

//获取设备信息
wx.getSystemInfo({
  success: function (res) {
    debugConfig.client = res;
  }
});

//拼装postData
function getPostData(error = '') {
  let {
    entryName,
    releaseStage,
    scriptVersion,
    client
  } = debugConfig,
  curPage = getCurrentPages()[getCurrentPages().length - 1],
    url = '',
    urlParams = '',
    name = 'error',
    postData = "postData",
    metaData = {},
    pageLevel = 'p0';

  //处理url
  if (curPage) {
    url = curPage.route;

    //此处自己根据实际项目给页面定级
    pageLevel = 'p0'; //getPageLevel(url);

    urlParams = objToParam(curPage.options);
    if (urlParams) {
      url += '?' + urlParams;
    }
  }

  name = error.split('\n')[0] || 'error';
  metaData = {
    userToken: getHeaders()
  }

  try {
    postData = {
      data: JSON.stringify({
        entryName,
        type: 'caught',
        scriptVersion,
        releaseStage,
        name,
        stacktrace: error,
        time: Date.now(),
        client,
        url,
        metaData,
        pageLevel,
        serviceLevel
      })
    };
  } catch (e) {
    console.error(e);
  }

  return postData;
}

//控制错误的发送
function _logReport(error) {
    //灰度控制自行加
    wx.request({
        header: {
        'content-type': 'application/x-www-form-urlencoded'
        },
        method: 'POST',
        url: logReportUrl,
        data: getPostData(error)
    });
}

let debug = {
  init(option = {}) {
    debugConfig = Object.assign({}, debugConfig, option);
  },
  notifyError(error) {
    _logReport(error)
  }
}

module.exports = debug;

Измените app.js:

import ngmmdebug from './utils/ngmmdebug.js';
//初始化
ngmmdebug.init({
  entryName: 'mall-wxapp',
  scriptVersion: '2.6.3'
})

Сообщать об ошибках вручную

import ngmmdebug from './utils/ngmmdebug.js';
ngmmdebug.notifyError('发生错误了~');

Есть несколько особых моментов апплета, на которые вы можете обратить внимание:

1. Пройтиwx.getSystemInfoПолучить информацию об устройстве, конечно, вы также можете передатьwx.getUserInfoПолучить информацию о пользователе и т. д.

2. Полную ссылку апплета нужно собрать самому через атрибут page options.

Ссылаться на

Соберите ссылку на поле

{
    "entryName":"项目名称",
    "scriptVersion":"1.0",//脚本版本
    "message":"aler is not defined",//错误描述
    "metaData":{//自定义字段
        "componentName":"anonymous component at /Users/taoxinhua/git/webmall/src/components/app.vue",//组件名称
        "info":"created hook",//vue报错信息
        "userToken":{//用户登录信息
            "user_id": 1
        }
    },
    "name":"ReferenceError",//错误名称
    "releaseStage":"local",//报错环境pre|beta|local
    "stacktrace":"ReferenceError: aler is not defined
at VueComponent.created (webpack-internal:///370:32:9)
at callHook (webpack-internal:///1:2666:21)
at VueComponent.Vue._init (webpack-internal:///1:4227:5)
at new VueComponent (webpack-internal:///1:4397:12)
at createComponentInstanceForVnode (webpack-internal:///1:3679:10)
at init (webpack-internal:///1:3496:45)
at createComponent (webpack-internal:///1:5148:9)
at createElm (webpack-internal:///1:5091:9)
at Vue$3.patch [as __patch__] (webpack-internal:///1:5607:9)
at Vue$3.Vue._update (webpack-internal:///1:2415:19)",//错误栈
    "time":1544437068009,//发生错误时的客户端时间
    "title":"年糕妈妈优选",//页面标题
    "type":"caught",//错误类型参考fundebug
    "url":"http://localhost:3200/test",//页面地址
    "client":{//客户端信息
        "userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "height":800,
        "width":1280,
        "referrer":"http://localhost:3200/test"
    },
    "pageLevel":"p4"//页面错误级别,方便查询和优先处理重要页面的bug
}

Улучшенная система сбора логов

Если ваша система относительно небольшая и вы хотите использовать ее бесплатно, вы можете попробоватьfundebug.

Суммировать

Собирать ошибки во внешнем интерфейсе относительно просто, и как эффективно использовать эти собранные ошибки, нужно медленно обдумывать. Если в тексте что-то не так, вы можете указать на это в разделе комментариев.

подпись

by:Tao