errorResult.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /**
  2. * @class ErrorResult 错误结果统计模型
  3. */
  4. const BaseMod = require('./base')
  5. const Platform = require('./platform')
  6. const Channel = require('./channel')
  7. const Version = require('./version')
  8. const ErrorLog = require('./errorLog')
  9. const AppCrashLogs = require('./appCrashLogs')
  10. const SessionLog = require('./sessionLog')
  11. const {
  12. DateTime
  13. } = require('../lib')
  14. module.exports = class ErrorResult extends BaseMod {
  15. constructor() {
  16. super()
  17. this.tableName = 'error-result'
  18. this.platforms = []
  19. this.channels = []
  20. this.versions = []
  21. this.errors = []
  22. }
  23. /**
  24. * 错误结果统计
  25. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  26. * @param {Date|Time} date 指定日期或时间戳
  27. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  28. */
  29. async stat(type, date, reset) {
  30. //前端js错误统计
  31. const resJs = await this.statJs(type, date, reset)
  32. //原生应用崩溃错误统计
  33. const resCrash = await this.statCrash(type, date, reset)
  34. return {
  35. code: 0,
  36. msg: 'success',
  37. data: {
  38. resJs,
  39. resCrash
  40. }
  41. }
  42. }
  43. /**
  44. * 前端js错误结果统计
  45. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  46. * @param {Date|Time} date 指定日期或时间戳
  47. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  48. */
  49. async statJs(type, date, reset) {
  50. const allowedType = ['day']
  51. if (!allowedType.includes(type)) {
  52. return {
  53. code: 1002,
  54. msg: 'This type is not allowed'
  55. }
  56. }
  57. this.fillType = type
  58. const dateTime = new DateTime()
  59. const dateDimension = dateTime.getTimeDimensionByType(type, -1, date)
  60. this.startTime = dateDimension.startTime
  61. this.endTime = dateDimension.endTime
  62. if (this.debug) {
  63. console.log('dimension time', this.startTime + '--' + this.endTime)
  64. }
  65. // 查看当前时间段日志是否已存在,防止重复生成
  66. if (!reset) {
  67. const checkRes = await this.getCollection(this.tableName).where({
  68. type: 'js',
  69. start_time: this.startTime,
  70. end_time: this.endTime
  71. }).get()
  72. if (checkRes.data.length > 0) {
  73. console.log('error log have existed')
  74. return {
  75. code: 1003,
  76. msg: 'This log have existed'
  77. }
  78. }
  79. } else {
  80. const delRes = await this.delete(this.tableName, {
  81. type: 'js',
  82. start_time: this.startTime,
  83. end_time: this.endTime
  84. })
  85. console.log('delete old data result:', JSON.stringify(delRes))
  86. }
  87. // 数据获取
  88. this.errorLog = new ErrorLog()
  89. const statRes = await this.aggregate(this.errorLog.tableName, {
  90. project: {
  91. appid: 1,
  92. version: 1,
  93. platform: 1,
  94. channel: 1,
  95. error_hash: 1,
  96. create_time: 1
  97. },
  98. match: {
  99. create_time: {
  100. $gte: this.startTime,
  101. $lte: this.endTime
  102. }
  103. },
  104. group: {
  105. _id: {
  106. appid: '$appid',
  107. version: '$version',
  108. platform: '$platform',
  109. channel: '$channel',
  110. error_hash: '$error_hash'
  111. },
  112. error_count: {
  113. $sum: 1
  114. }
  115. },
  116. sort: {
  117. error_count: 1
  118. },
  119. getAll: true
  120. })
  121. let res = {
  122. code: 0,
  123. msg: 'success'
  124. }
  125. if (this.debug) {
  126. console.log('statRes', JSON.stringify(statRes))
  127. }
  128. if (statRes.data.length > 0) {
  129. this.fillData = []
  130. for (const i in statRes.data) {
  131. await this.fillJs(statRes.data[i])
  132. }
  133. if (this.fillData.length > 0) {
  134. res = await this.batchInsert(this.tableName, this.fillData)
  135. }
  136. }
  137. return res
  138. }
  139. /**
  140. * 前端js错误统计结果数据填充
  141. * @param {Object} data 数据集合
  142. */
  143. async fillJs(data) {
  144. // 平台信息
  145. let platformInfo = null
  146. if (this.platforms && this.platforms[data._id.platform]) {
  147. //暂存下数据,减少读库
  148. platformInfo = this.platforms[data._id.platform]
  149. } else {
  150. const platform = new Platform()
  151. platformInfo = await platform.getPlatformAndCreate(data._id.platform, null)
  152. if (!platformInfo || platformInfo.length === 0) {
  153. platformInfo._id = ''
  154. }
  155. this.platforms[data._id.platform] = platformInfo
  156. if (this.debug) {
  157. console.log('platformInfo', JSON.stringify(platformInfo))
  158. }
  159. }
  160. // 渠道信息
  161. let channelInfo = null
  162. const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel
  163. if (this.channels && this.channels[channelKey]) {
  164. channelInfo = this.channels[channelKey]
  165. } else {
  166. const channel = new Channel()
  167. channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel)
  168. if (!channelInfo || channelInfo.length === 0) {
  169. channelInfo._id = ''
  170. }
  171. this.channels[channelKey] = channelInfo
  172. if (this.debug) {
  173. console.log('channelInfo', JSON.stringify(channelInfo))
  174. }
  175. }
  176. // 版本信息
  177. let versionInfo = null
  178. const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version
  179. if (this.versions && this.versions[versionKey]) {
  180. versionInfo = this.versions[versionKey]
  181. } else {
  182. const version = new Version()
  183. versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version)
  184. if (!versionInfo || versionInfo.length === 0) {
  185. versionInfo._id = ''
  186. }
  187. this.versions[versionKey] = versionInfo
  188. if (this.debug) {
  189. console.log('versionInfo', JSON.stringify(versionInfo))
  190. }
  191. }
  192. // 错误信息
  193. let errorInfo = null
  194. if (this.errors && this.errors[data._id.error_hash]) {
  195. errorInfo = this.errors[data._id.error_hash]
  196. } else {
  197. const cacheKey = 'uni-stat-errors-' + data._id.error_hash
  198. errorInfo = await this.getCache(cacheKey)
  199. if (!errorInfo) {
  200. errorInfo = await this.getCollection(this.errorLog.tableName).where({
  201. error_hash: data._id.error_hash
  202. }).limit(1).get()
  203. if (!errorInfo || errorInfo.data.length === 0) {
  204. errorInfo.error_msg = ''
  205. } else {
  206. errorInfo = errorInfo.data[0]
  207. await this.setCache(cacheKey, errorInfo)
  208. }
  209. }
  210. this.errors[data._id.error_hash] = errorInfo
  211. }
  212. // 最近一次报错时间
  213. const matchCondition = data._id
  214. Object.assign(matchCondition, {
  215. create_time: {
  216. $gte: this.startTime,
  217. $lte: this.endTime
  218. }
  219. })
  220. const lastErrorLog = await this.getCollection(this.errorLog.tableName).where(matchCondition).orderBy(
  221. 'create_time', 'desc').limit(1).get()
  222. let lastErrorTime = ''
  223. if (lastErrorLog && lastErrorLog.data.length > 0) {
  224. lastErrorTime = lastErrorLog.data[0].create_time
  225. }
  226. //数据填充
  227. const datetime = new DateTime()
  228. const insertParams = {
  229. appid: data._id.appid,
  230. platform_id: platformInfo._id,
  231. channel_id: channelInfo._id,
  232. version_id: versionInfo._id,
  233. type: 'js',
  234. hash: data._id.error_hash,
  235. msg: errorInfo.error_msg,
  236. count: data.error_count,
  237. last_time: lastErrorTime,
  238. dimension: this.fillType,
  239. stat_date: datetime.getDate('Ymd', this.startTime),
  240. start_time: this.startTime,
  241. end_time: this.endTime
  242. }
  243. this.fillData.push(insertParams)
  244. return insertParams
  245. }
  246. /**
  247. * 原生应用错误结果统计
  248. * @param {String} type 统计类型 hour:实时统计 day:按天统计,week:按周统计 month:按月统计
  249. * @param {Date|Time} date 指定日期或时间戳
  250. * @param {Boolean} reset 是否重置,为ture时会重置该批次数据
  251. */
  252. async statCrash(type, date, reset) {
  253. const allowedType = ['day']
  254. if (!allowedType.includes(type)) {
  255. return {
  256. code: 1002,
  257. msg: 'This type is not allowed'
  258. }
  259. }
  260. this.fillType = type
  261. const dateTime = new DateTime()
  262. const dateDimension = dateTime.getTimeDimensionByType(type, -1, date)
  263. this.startTime = dateDimension.startTime
  264. this.endTime = dateDimension.endTime
  265. if (this.debug) {
  266. console.log('dimension time', this.startTime + '--' + this.endTime)
  267. }
  268. // 查看当前时间段日志是否已存在,防止重复生成
  269. if (!reset) {
  270. const checkRes = await this.getCollection(this.tableName).where({
  271. type: 'crash',
  272. start_time: this.startTime,
  273. end_time: this.endTime
  274. }).get()
  275. if (checkRes.data.length > 0) {
  276. console.log('error log have existed')
  277. return {
  278. code: 1003,
  279. msg: 'This log have existed'
  280. }
  281. }
  282. } else {
  283. const delRes = await this.delete(this.tableName, {
  284. type: 'crash',
  285. start_time: this.startTime,
  286. end_time: this.endTime
  287. })
  288. console.log('delete old data result:', JSON.stringify(delRes))
  289. }
  290. // 数据获取
  291. this.crashLogs = new AppCrashLogs()
  292. const statRes = await this.aggregate(this.crashLogs.tableName, {
  293. project: {
  294. appid: 1,
  295. version: 1,
  296. platform: 1,
  297. channel: 1,
  298. create_time: 1
  299. },
  300. match: {
  301. create_time: {
  302. $gte: this.startTime,
  303. $lte: this.endTime
  304. }
  305. },
  306. group: {
  307. _id: {
  308. appid: '$appid',
  309. version: '$version',
  310. platform: '$platform',
  311. channel: '$channel'
  312. },
  313. error_count: {
  314. $sum: 1
  315. }
  316. },
  317. sort: {
  318. error_count: 1
  319. },
  320. getAll: true
  321. })
  322. let res = {
  323. code: 0,
  324. msg: 'success'
  325. }
  326. if (this.debug) {
  327. console.log('statRes', JSON.stringify(statRes))
  328. }
  329. if (statRes.data.length > 0) {
  330. this.fillData = []
  331. for (const i in statRes.data) {
  332. await this.fillCrash(statRes.data[i])
  333. }
  334. if (this.fillData.length > 0) {
  335. res = await this.batchInsert(this.tableName, this.fillData)
  336. }
  337. }
  338. return res
  339. }
  340. async fillCrash(data) {
  341. // 平台信息
  342. let platformInfo = null
  343. if (this.platforms && this.platforms[data._id.platform]) {
  344. //暂存下数据,减少读库
  345. platformInfo = this.platforms[data._id.platform]
  346. } else {
  347. const platform = new Platform()
  348. platformInfo = await platform.getPlatformAndCreate(data._id.platform, null)
  349. if (!platformInfo || platformInfo.length === 0) {
  350. platformInfo._id = ''
  351. }
  352. this.platforms[data._id.platform] = platformInfo
  353. if (this.debug) {
  354. console.log('platformInfo', JSON.stringify(platformInfo))
  355. }
  356. }
  357. // 渠道信息
  358. let channelInfo = null
  359. data._id.channel = data._id.channel ? data._id.channel : '1001'
  360. const channelKey = data._id.appid + '_' + platformInfo._id + '_' + data._id.channel
  361. if (this.channels && this.channels[channelKey]) {
  362. channelInfo = this.channels[channelKey]
  363. } else {
  364. const channel = new Channel()
  365. channelInfo = await channel.getChannelAndCreate(data._id.appid, platformInfo._id, data._id.channel)
  366. if (!channelInfo || channelInfo.length === 0) {
  367. channelInfo._id = ''
  368. }
  369. this.channels[channelKey] = channelInfo
  370. if (this.debug) {
  371. console.log('channelInfo', JSON.stringify(channelInfo))
  372. }
  373. }
  374. // 版本信息
  375. let versionInfo = null
  376. const versionKey = data._id.appid + '_' + data._id.platform + '_' + data._id.version
  377. if (this.versions && this.versions[versionKey]) {
  378. versionInfo = this.versions[versionKey]
  379. } else {
  380. const version = new Version()
  381. versionInfo = await version.getVersionAndCreate(data._id.appid, data._id.platform, data._id.version)
  382. if (!versionInfo || versionInfo.length === 0) {
  383. versionInfo._id = ''
  384. }
  385. this.versions[versionKey] = versionInfo
  386. if (this.debug) {
  387. console.log('versionInfo', JSON.stringify(versionInfo))
  388. }
  389. }
  390. //app启动次数
  391. const sessionLog = new SessionLog()
  392. const sessionTimesRes = await this.getCollection(sessionLog.tableName).where({
  393. appid: data._id.appid,
  394. version: data._id.version,
  395. platform: data._id.platform,
  396. channel: data._id.channel,
  397. create_time: {
  398. $gte: this.startTime,
  399. $lte: this.endTime
  400. }
  401. }).count()
  402. let sessionTimes = 0
  403. if(sessionTimesRes && sessionTimesRes.total > 0) {
  404. sessionTimes = sessionTimesRes.total
  405. } else {
  406. console.log('Not found session logs')
  407. return false
  408. }
  409. //数据填充
  410. const datetime = new DateTime()
  411. const insertParams = {
  412. appid: data._id.appid,
  413. platform_id: platformInfo._id,
  414. channel_id: channelInfo._id,
  415. version_id: versionInfo._id,
  416. type: 'crash',
  417. count: data.error_count,
  418. app_launch_count: sessionTimes,
  419. dimension: this.fillType,
  420. stat_date: datetime.getDate('Ymd', this.startTime),
  421. start_time: this.startTime,
  422. end_time: this.endTime
  423. }
  424. this.fillData.push(insertParams)
  425. return insertParams
  426. }
  427. }