diff --git a/src/Container.ts b/src/Container.ts index 981cb4a..c8787d8 100644 --- a/src/Container.ts +++ b/src/Container.ts @@ -5,6 +5,8 @@ import { ActionQueue } from './Queue/ActionQueue'; import { Executor } from './Executor'; import { ControlPanel } from './ControlPanel'; import { DataStorageTaskProvider } from './Queue/DataStorageTaskProvider'; +import { Statistics } from './Statistics'; +import { StatisticsStorage } from './Storage/StatisticsStorage'; export class Container { private readonly version: string; @@ -33,7 +35,7 @@ export class Container { this._executor = this._executor || (() => { - return new Executor(this.version, this.scheduler); + return new Executor(this.version, this.scheduler, this.statistics); })(); return this._executor; } @@ -48,4 +50,15 @@ export class Container { })(); return this._controlPanel; } + + private _statistics: Statistics | undefined; + + get statistics(): Statistics { + this._statistics = + this._statistics || + (() => { + return new Statistics(new StatisticsStorage()); + })(); + return this._statistics; + } } diff --git a/src/Executor.ts b/src/Executor.ts index 6a67dfd..29c6eef 100644 --- a/src/Executor.ts +++ b/src/Executor.ts @@ -23,11 +23,11 @@ export class Executor { private executionState: ExecutionStorage; private logger: Logger; - constructor(version: string, scheduler: Scheduler) { + constructor(version: string, scheduler: Scheduler, statistics: Statistics) { this.version = version; this.scheduler = scheduler; this.grabbers = new GrabberManager(); - this.statistics = new Statistics(); + this.statistics = statistics; this.executionState = new ExecutionStorage(); this.logger = new ConsoleLogger(this.constructor.name); } @@ -103,7 +103,7 @@ export class Executor { throw new ActionError(`Action task id ${cmd.args.taskId} not equal current task id ${task.id}`); } if (actionHandler) { - this.statistics.incrementAction(); + this.statistics.incrementAction(timestamp()); await actionHandler.run(cmd.args, task); } else { this.logger.warn('ACTION NOT FOUND', cmd.name); diff --git a/src/Statistics.ts b/src/Statistics.ts index 8ac8eca..30185f8 100644 --- a/src/Statistics.ts +++ b/src/Statistics.ts @@ -1,25 +1,54 @@ -import { StatisticsStorage } from './Storage/StatisticsStorage'; import * as dateFormat from 'dateformat'; +const KEY_FORMAT = 'yyyy-mm-dd-HH'; +const KEEP_RECORD_COUNT = 24; + export interface ActionStatistics { [key: string]: number; } -export class Statistics { - private state: StatisticsStorage; +export interface StatisticsStorageInterface { + getActionStatistics(): ActionStatistics; + setActionStatistics(statistics: ActionStatistics): void; +} - constructor() { - this.state = new StatisticsStorage(); +export class Statistics { + private state: StatisticsStorageInterface; + + static readonly keepRecords = KEEP_RECORD_COUNT; + + constructor(storage: StatisticsStorageInterface) { + this.state = storage; } - incrementAction(): void { + incrementAction(ts: number): void { const stat = this.state.getActionStatistics(); - const key = dateFormat(Date.now(), 'yyyy-mm-dd-HH'); + const key = dateFormat(ts * 1000, KEY_FORMAT); stat[key] = (stat[key] || 0) + 1; + this.trimStatistics(stat); this.state.setActionStatistics(stat); } + private trimStatistics(stat: ActionStatistics) { + const topKeys = this.getTopStatKeys(stat); + const statKeys = Object.keys(stat); + for (let key of statKeys) { + if (!topKeys.includes(key)) { + delete stat[key]; + } + } + return stat; + } + + private getTopStatKeys(stat: ActionStatistics) { + const keys = Object.keys(stat); + return keys + .sort() + .reverse() + .slice(0, KEEP_RECORD_COUNT); + } + getActionStatistics(): ActionStatistics { - return {}; + return this.state.getActionStatistics(); } } diff --git a/src/Storage/StatisticsStorage.ts b/src/Storage/StatisticsStorage.ts index 8b89a52..1114bdb 100644 --- a/src/Storage/StatisticsStorage.ts +++ b/src/Storage/StatisticsStorage.ts @@ -1,11 +1,11 @@ import { DataStorage } from '../DataStorage'; -import { ActionStatistics } from '../Statistics'; +import { ActionStatistics, StatisticsStorageInterface } from '../Statistics'; const NAMESPACE = 'statistics.v1'; const ACTION_STATISTICS_KEY = 'actions'; -export class StatisticsStorage { +export class StatisticsStorage implements StatisticsStorageInterface { private storage: DataStorage; constructor() { this.storage = new DataStorage(NAMESPACE); diff --git a/src/utils.ts b/src/utils.ts index b608c00..6c98bc5 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -115,6 +115,10 @@ export function notify(msg: string): void { setTimeout(() => n && n.close(), 4000); } +export interface NowTimeGenerator { + (): number; +} + export function markPage(text: string, version: string) { jQuery('body').append( '