您正在学习的是试看内容,报名后可学习全部内容 报名课程

接口错误采集

fetch的劫持:

import parseResponse from '../utils/parseResponse';

/**
 * hack Fetch
 * 搜集错误接口,响应时间超过500ms的正确接口,或者根据开关开启所有正确接口
 */

FEDLOG.requestLog = function (url, type, extData = {}, isIme) {
    const { status = '', code = '', traceId = '', duration = '', msg = '', params = '' } = extData
    this.send({
        t1: 'monitor',
        t2: 'api',
        t3: type,
        d1: url.replace(/^(https?:)?/, '').replace(/\?.*$/, ''),  //接口url
        d2: `${status}-${code}`,   // http状态码
        d3: `${duration}-${traceId}`,  //接口耗时
        d4: msg,  //错误信息
        d5: params  //请求参数
    })
}

window.FEDLOG_MAX_API_LATENCY = 500;
window.FEDLOG_SAMPLING_API = 0.2;
FEDLOG.injectFetchHook = function () {
    if (typeof window.fetch !== 'function') return;
    var oldFetch = window.fetch;
    const makeLog = (reqClone, url, info, args, type) => {
        var rbody = args;
        if (reqClone) {
            reqClone.text().then(function (res) {
                if (res) {
                    try {
                        rbody = JSON.parse(res)
                    } catch (e) {
                        rbody = res
                    }
                }
                FEDLOG.requestLog(url, type, {
                    ...info,
                    params: JSON.stringify(rbody)
                });
            }).catch(() => { })
        } else {
            FEDLOG.requestLog(url, type, {
                ...info,
                params: JSON.stringify(rbody)
            });
        }
    }
    window.fetch = function fetch(arg0, setting) {
        var args = (arguments.length === 1) ? [arguments[0]] : Array.apply(null, arguments);
        // method: 'HEAD' 或 mode: 'no-cors' 的请求不监听
        if (setting && (setting.method === 'HEAD' || setting.mode === 'no-cors')) {
            return oldFetch.apply(window, args);
        }

        // 请求的 url
        var url = ((arg0 && (typeof arg0 !== 'string')) ? arg0.url : arg0) || '';

        // 跳过非接口
        if (url.match(/\.(js|css|png|jpg|gif|jpeg|webp)(\?.*)?$/)) {
            return oldFetch.apply(window, args);
        }
        var begin = Date.now();
        // 获取请求参数
        var reqClone = ""
        if (args[0] instanceof Request) {
            reqClone = args[0].clone()
        }
        return oldFetch.apply(window, args).then(function (origin) {
            //polyfill未必有clone方法
            var response = origin.clone ? origin.clone() : origin;

            parseResponse.getResponseBody(response, setting || {}).then(function (resInfo) {
                var time = Date.now() - begin;
                var res = {}
                if (resInfo && typeof resInfo == 'object') res = resInfo;
                var status = response.status;
                res = parseResponse.parseResponse(res, status);
                var traceId = '';
                try {
                    traceId = response.headers.get('traceid');
                } catch (ex) { }
                var shouldUpload = (FEDLOG.env != 'dev') && (time > window.FEDLOG_MAX_API_LATENCY) && (Math.random() < window.FEDLOG_SAMPLING_API);
                if (!res.success || shouldUpload || window.FEDLOG_ENABLE_API_SUCCESS) {
                    var type = res.success ? 'requestSuccess' : 'requestError';
                    var info = {
                        status,
                        duration: time,
                        traceId,
                        code: res.code,
                        msg: res.msg,
                        body: res.body,
                        type: 'fetch'
                    }
                    makeLog(reqClone, url, info, args, type)

                }
            }).catch(() => { })

            return origin;
        }, function (err) {
            //https://github.com/github/fetch/issues/201
            // 大部分原因是请求的返回header没有Access-Control-Allow-Origin: *
            var info = {
                status: 401,
                duration: Date.now() - begin,
                msg: err.stack || err.message,
                type: 'fetch'
            }
            makeLog(reqClone, url, info, args, 'requestError')
            console.error(err)
            return err;
        });
    };
};

xhr的劫持:

import parseResponse from '../utils/parseResponse';

/**
 * 接口埋点包装
 */
FEDLOG.requestLog = function (url, type, extData = {}, isIme) {
    const { status = '', code = '', traceId = '', duration = '', msg = '', params = '' } = extData
    this.send({
        t1: 'monitor',
        t2: 'api',
        t3: type,
        d1: url.replace(/^(https?:)?/, '').replace(/\?.*$/, ''),  //接口url
        d2: `${status}-${code}`,   // http状态码
        d3: `${duration}-${traceId}`,  //接口耗时
        d4: msg,  //错误信息
        d5: params  //请求参数
    })
}

function responseData(res, url, duration, status, traceId) {
    if (!res.success || window.FEDLOG_ENABLE_API_SUCCESS) {
        FEDLOG.requestLog(url, res.success ? 'requestSuccess' : 'requestError', {
            status,
            duration,
            traceId,
            code: res.code,
            msg: res.msg,
            type: 'xhr'
        });

    }
}


FEDLOG.injectXhrHook = function () {
    if (typeof window.XMLHttpRequest !== 'function' || !window.addEventListener) return;
    var oldXMLHttpRequest = window.XMLHttpRequest;

    window.XMLHttpRequest = function XMLHttpRequest(props) {
        var xhr = new oldXMLHttpRequest(props)
        var send = xhr.send,
            open = xhr.open,
            begin,
            url;
        var isFEDLOG;

        xhr.open = function (method0, url0) {
            var args = (arguments.length === 1) ? [arguments[0]] : Array.apply(null, arguments);
            open.apply(xhr, args);
            url = url0 || '';

            if (url.match('logstores')) {
                isFEDLOG = true;
            }
        };

        xhr.send = function (data) {
            begin = Date.now();
            send.apply(xhr, arguments);
        };

        xhr.addEventListener('readystatechange', function (e) {
            // FEDLOG请求排除
            if (isFEDLOG) {
                return;
            }
            if (!url || xhr.readyState !== 4) return;
            var time = Date.now() - begin;
            var status = xhr.status;
            var res = {};
            // getResponseHeader只能拿到部分w3c认为安全的响应头(Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma),或者由业务在响应头里增加“Access-Control-Expose-Headers: x-eagleeye-id”
            var traceId;
            try {
                // 不判断浏览器会报Refused to get unsafe header "eagleeye-traceid"
                var resHeaders = xhr.getAllResponseHeaders();
                if (resHeaders.indexOf('traceId') !== -1) {
                    traceId = xhr.getResponseHeader('traceId');
                }
            } catch (ex) { }
            if (!xhr.responseType || xhr.responseType === 'text') {
                res = parseResponse.parseResponse(xhr.responseText, status);
                responseData(res, url, time, status, traceId);
            } else if (xhr.responseType === 'blob') {
                var reader = new FileReader()
                reader.readAsText.apply(reader, [xhr.response]);
                reader.onloadend = function () {
                    res = parseResponse.parseResponse(reader.result, status);
                    responseData(res, url, time, status, traceId);
                }
            }
        });

        return xhr;
    };
};

课程资料

Github: https://github.com/chalecao/f...
PPT:见最后一节课程