import { Pipe } from './index'; import Core, { LOG_TYPE, NormalLog, SEND_TYPE } from '@tencent/aegis-core'; let isFirstSend = true; // 是否为首次发送白名单接口请求,如果是首次为白名单+pv/uv,否则只请求白名单 export const createWhitelistPipe = function (aegis: Core): Pipe { let isWhiteList = false; let requestEnd = false; // 请求白名单接口 // 这个 “setTimeout” 存在是因为 “createWhitelistPipe” 被执行时子类的 “send” 方法还没覆盖 “Core” 的 “send” 占位 // 晚点执行请求也有好处,防止业务方说 Aegis 请求阻塞他们的重要请求 // 接收到config变化后再执行,初始化会触发一次,每次setConfig也会触发 let timer: number; let sendWhitelist = false; aegis.lifeCycle.on('onConfigChange', () => { timer && clearTimeout(timer); timer = setTimeout(() => { if (sendWhitelist) { return; } sendWhitelist = true; const { pvUrl = '', whiteListUrl = '' } = aegis.config; aegis.send( { url: isFirstSend ? pvUrl : whiteListUrl, type: isFirstSend ? SEND_TYPE.PV : SEND_TYPE.WHITE_LIST, }, (res) => { requestEnd = true; try { res = JSON.parse(res) || {}; if (res.retcode === 0) { isWhiteList = res.result.is_in_white_list; aegis.isWhiteList = isWhiteList; } const { onWhitelist } = aegis.config; if (typeof onWhitelist === 'function') { onWhitelist(isWhiteList); } } catch (e) { } }, () => { requestEnd = true; }, ); isFirstSend = false; // 修复用户中途换号登录,不会请求白名单了bug sendWhitelist = false; }, aegis.config.uin ? 50 : 500) as unknown as number; }); // 不确定是否是白名单时暂存的数据 // TODO 这里有一个坑,存在 pool 里的数据,如果后续没有日志上报,pool 将不会被报上去。。 const pool: NormalLog[] = []; aegis.lifeCycle.on('destroy', () => { pool.length = 0; }); // aegis/issue/issues/218 let errorHappened = false; return function (logs: NormalLog[], resolve) { // 检查是否有ERROR级别的错误日志 if (!errorHappened && logs.some(log => log.level === LOG_TYPE.ERROR)) { errorHappened = true; } // 在白名单中 || 本次实例中出现过一次ERROR级别的日志,就把所有日志上报 if (isWhiteList || errorHappened) { // 白名单,清空并带上pool中的日志 // 修正INFO_ALL类型为INFO类型(后端并不能识别INFO_ALL类型) resolve(logs.concat(pool.splice(0)).map((log) => { log.level === LOG_TYPE.INFO_ALL && (log.level = LOG_TYPE.INFO); return log; })); } else { // 非白名单或者白名单请求还未结束 const otherLog = logs.filter((log) => { if ( log.level !== LOG_TYPE.INFO && log.level !== LOG_TYPE.API_RESPONSE ) { // 修正INFO_ALL类型为INFO类型(后端并不能识别INFO_ALL类型) log.level === LOG_TYPE.INFO_ALL && (log.level = LOG_TYPE.INFO); // 除了INFO类型和API_RESPONSE类型需要判断白名单外,其他日志类型都直接上报 return true; } if (!requestEnd) { // 白名单请求还没结束 pool.push(log); pool.length >= 200 && (pool.length = 200); } return false; }); // 必须判断长度,不然后面的 pipe 会空跑 otherLog.length && resolve(otherLog); } }; };