UNPKG

logan-web

Version:

Web SDK for Logan, supports log and report in H5 and browser environment.

247 lines (245 loc) 9.72 kB
import { CustomDB, idbIsSupported, deleteDB } from 'idb-managed'; import { dateFormat2Day, M_BYTE, sizeOf, getStartOfDay, dayFormat2Date } from './utils'; import { ResultMsg } from '../interface'; const LOGAN_DB_VERSION = 1; const LOGAN_DB_NAME = 'logan_web_db'; const LOG_DETAIL_TABLE_NAME = 'logan_detail_table'; const LOG_DETAIL_REPORTNAME_INDEX = 'logReportName'; const LOG_DETAIL_CREATETIME_INDEX = 'logCreateTime'; const LOG_DAY_TABLE_NAME = 'log_day_table'; export const LOG_DAY_TABLE_PRIMARY_KEY = 'logDay'; export type FormattedLogReportName = string; const DEFAULT_LOG_DURATION = 7 * 24 * 3600 * 1000; // logan-web keeps 7 days logs locally const DEFAULT_SINGLE_DAY_MAX_SIZE = 7 * M_BYTE; // 7M storage limit for one day const DEFAULT_SINGLE_PAGE_MAX_SIZE = 1 * M_BYTE; // 1M storage limit for one page type TimeStamp = number; interface LogReportNameParsedOb { logDay: string; pageIndex: number; } interface LoganLogItem { [LOG_DETAIL_REPORTNAME_INDEX]: string; [LOG_DETAIL_CREATETIME_INDEX]: TimeStamp; logSize: number; logString: string; } export interface LoganLogDayItem { [LOG_DAY_TABLE_PRIMARY_KEY]: string; totalSize: number; reportPagesInfo: { pageSizes: number[]; // Array of pageSize of each page. }; } export default class LoganDB { public static idbIsSupported = idbIsSupported; public static deleteDB = deleteDB; private DB: CustomDB; constructor(dbName?: string) { this.DB = new CustomDB({ dbName: dbName || LOGAN_DB_NAME, dbVersion: LOGAN_DB_VERSION, tables: { [LOG_DETAIL_TABLE_NAME]: { indexList: [ { indexName: LOG_DETAIL_REPORTNAME_INDEX, unique: false }, { indexName: LOG_DETAIL_CREATETIME_INDEX, unique: false } ] }, [LOG_DAY_TABLE_NAME]: { primaryKey: LOG_DAY_TABLE_PRIMARY_KEY } } }); } /** * @param logDay * @param pageIndex start from 0 */ logReportNameFormatter ( logDay: string, pageIndex: number ): FormattedLogReportName { return `${logDay}_${pageIndex}`; } logReportNameParser (reportName: FormattedLogReportName): LogReportNameParsedOb { const splitArray = reportName.split('_'); return { logDay: splitArray[0], pageIndex: +splitArray[1] }; } async getLogDayInfo (logDay: string): Promise<LoganLogDayItem | null> { return ((await this.DB.getItem( LOG_DAY_TABLE_NAME, logDay )) as any) as LoganLogDayItem | null; } async getLogDaysInfo ( fromLogDay: string, toLogDay: string ): Promise<LoganLogDayItem[]> { if (fromLogDay === toLogDay) { const result = ((await this.DB.getItem( LOG_DAY_TABLE_NAME, fromLogDay )) as any) as LoganLogDayItem | null; return result ? [result] : []; } else { return ((await this.DB.getItemsInRange({ tableName: LOG_DAY_TABLE_NAME, indexRange: { indexName: LOG_DAY_TABLE_PRIMARY_KEY, lowerIndex: fromLogDay, upperIndex: toLogDay, lowerExclusive: false, upperExclusive: false } })) as any[]) as LoganLogDayItem[]; } } async getLogsByReportName ( reportName: FormattedLogReportName ): Promise<LoganLogItem[]> { const logs = ((await this.DB.getItemsInRange({ tableName: LOG_DETAIL_TABLE_NAME, indexRange: { indexName: LOG_DETAIL_REPORTNAME_INDEX, onlyIndex: reportName } })) as any[]) as LoganLogItem[]; return logs; } async addLog (logString: string): Promise<void> { const logSize = sizeOf(logString); const now = new Date(); const today: string = dateFormat2Day(now); const todayInfo: LoganLogDayItem = (await this.getLogDayInfo( today )) || { [LOG_DAY_TABLE_PRIMARY_KEY]: today, totalSize: 0, reportPagesInfo: { pageSizes: [0] } }; if (todayInfo.totalSize + logSize > DEFAULT_SINGLE_DAY_MAX_SIZE) { throw new Error(ResultMsg.EXCEED_LOG_SIZE_LIMIT); } if (!todayInfo.reportPagesInfo || !todayInfo.reportPagesInfo.pageSizes) { todayInfo.reportPagesInfo = { pageSizes: [0] }; } const currentPageSizesArr = todayInfo.reportPagesInfo.pageSizes; const currentPageIndex = currentPageSizesArr.length - 1; const currentPageSize = currentPageSizesArr[currentPageIndex]; const needNewPage = currentPageSize > 0 && currentPageSize + logSize > DEFAULT_SINGLE_PAGE_MAX_SIZE; const nextPageSizesArr = (function (): number[] { const arrCopy = currentPageSizesArr.slice(); if (needNewPage) { arrCopy.push(logSize); } else { arrCopy[currentPageIndex] += logSize; } return arrCopy; })(); const logItem: LoganLogItem = { [LOG_DETAIL_REPORTNAME_INDEX]: this.logReportNameFormatter( today, needNewPage ? currentPageIndex + 1 : currentPageIndex ), [LOG_DETAIL_CREATETIME_INDEX]: +now, logSize, logString }; const updatedTodayInfo: LoganLogDayItem = { [LOG_DAY_TABLE_PRIMARY_KEY]: today, totalSize: todayInfo.totalSize + logSize, reportPagesInfo: { pageSizes: nextPageSizesArr } }; // The expire time is the start of the day after 7 days. const durationBeforeExpired = DEFAULT_LOG_DURATION - (+new Date() - getStartOfDay(new Date())); await this.DB.addItems([ { tableName: LOG_DAY_TABLE_NAME, item: updatedTodayInfo, itemDuration: durationBeforeExpired }, { tableName: LOG_DETAIL_TABLE_NAME, item: logItem, itemDuration: durationBeforeExpired } ]); } /** * Delete reported pages of logDay, in case that new pages are added after last report. */ async incrementalDelete (logDay: string, reportedPageIndexes: number[]): Promise<void> { const dayInfo: LoganLogDayItem | null = await this.getLogDayInfo(logDay); if (dayInfo && dayInfo.reportPagesInfo && dayInfo.reportPagesInfo.pageSizes instanceof Array) { const currentPageSizesArr = dayInfo.reportPagesInfo.pageSizes; const currentTotalSize = dayInfo.totalSize; const totalReportedSize = currentPageSizesArr.reduce((accSize, currentSize, indexOfPage) => { if (reportedPageIndexes.indexOf(indexOfPage) >= 0) { return accSize + currentSize; } else { return accSize; } }, 0); const pageSizesArrayWithNewPage = (function addNewPageIfLastPageIsReported (): number[] { // Add a new page with 0 page size if the last page is reported. if (reportedPageIndexes.indexOf(currentPageSizesArr.length - 1) >= 0) { return currentPageSizesArr.concat([0]); } else { return currentPageSizesArr; } })(); const resetReportedPageSizes = pageSizesArrayWithNewPage.reduce((accSizesArray, currentSize, index) => { if (reportedPageIndexes.indexOf(index) >= 0) { return accSizesArray.concat([0]); // Reset to 0 if this page is reported. } else { return accSizesArray.concat([currentSize]); } }, [] as number[]); // Update dayInfo with new pageSizeArray and new totalSize const updatedDayInfo = { ...dayInfo, reportPagesInfo: { pageSizes: resetReportedPageSizes }, totalSize: Math.max(currentTotalSize - totalReportedSize, 0) }; // The expire time is the start of the day after 7 days. const durationBeforeExpired = DEFAULT_LOG_DURATION - (+new Date() - getStartOfDay(new Date())) - (getStartOfDay(new Date()) - dayFormat2Date(logDay).getTime()); await this.DB.addItems([ { tableName: LOG_DAY_TABLE_NAME, item: updatedDayInfo, itemDuration: durationBeforeExpired } ]); // Delete logs of reported pages by iterating reportedPageIndexes. for (const pageIndex of reportedPageIndexes) { await this.DB.deleteItemsInRange([ { tableName: LOG_DETAIL_TABLE_NAME, indexRange: { indexName: LOG_DETAIL_REPORTNAME_INDEX, onlyIndex: this.logReportNameFormatter(logDay, pageIndex) } } ]); } } } }