From f1e05bc936ae3c6f144056ae249914e8a0260c0c Mon Sep 17 00:00:00 2001 From: Anton Vakhrushev Date: Sat, 16 May 2020 10:24:58 +0300 Subject: [PATCH] Add storage logger --- src/Container.ts | 11 ++++-- src/Executor.ts | 7 ++-- src/Logger.ts | 72 +++++++++++++++++++++++++++++++++++++++ src/Storage/LogStorage.ts | 31 +++++++++++++++++ 4 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 src/Storage/LogStorage.ts diff --git a/src/Container.ts b/src/Container.ts index 0a4d978..8a683dd 100644 --- a/src/Container.ts +++ b/src/Container.ts @@ -1,14 +1,15 @@ import { Scheduler } from './Scheduler'; import { TaskQueue } from './Queue/TaskQueue'; -import { ConsoleLogger } from './Logger'; +import { AggregateLogger, ConsoleLogger, LogLevel, StorageLogger } from './Logger'; 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'; -import { VillageRepository, VillageRepositoryInterface } from './VillageRepository'; +import { VillageRepository } from './VillageRepository'; import { VillageStateRepository } from './VillageState'; +import { LogStorage } from './Storage/LogStorage'; export class Container { private readonly version: string; @@ -70,7 +71,11 @@ export class Container { this._executor = this._executor || (() => { - return new Executor(this.version, this.scheduler, this.villageStateRepository, this.statistics); + const logger = new AggregateLogger([ + new ConsoleLogger(Executor.name), + new StorageLogger(new LogStorage(), LogLevel.warning), + ]); + return new Executor(this.version, this.scheduler, this.villageStateRepository, this.statistics, logger); })(); return this._executor; } diff --git a/src/Executor.ts b/src/Executor.ts index 385547b..6a912be 100644 --- a/src/Executor.ts +++ b/src/Executor.ts @@ -2,7 +2,7 @@ import { markPage, sleepMicro, timestamp, waitForLoad } from './utils'; import { AbortTaskError, ActionError, GrabError, TryLaterError, VillageNotFound } from './Errors'; import { TaskQueueRenderer } from './TaskQueueRenderer'; import { createActionHandler } from './Action/ActionController'; -import { ConsoleLogger, Logger } from './Logger'; +import { Logger } from './Logger'; import { GrabberManager } from './Grabber/GrabberManager'; import { Scheduler } from './Scheduler'; import { Statistics } from './Statistics'; @@ -29,7 +29,8 @@ export class Executor { version: string, scheduler: Scheduler, villageStateRepository: VillageStateRepository, - statistics: Statistics + statistics: Statistics, + logger: Logger ) { this.version = version; this.scheduler = scheduler; @@ -37,7 +38,7 @@ export class Executor { this.grabbers = new GrabberManager(scheduler); this.statistics = statistics; this.executionState = new ExecutionStorage(); - this.logger = new ConsoleLogger(this.constructor.name); + this.logger = logger; } async run() { diff --git a/src/Logger.ts b/src/Logger.ts index 19a66ff..69e6cc8 100644 --- a/src/Logger.ts +++ b/src/Logger.ts @@ -1,9 +1,17 @@ +import { timestamp } from './utils'; + export interface Logger { info(...args: any[]): void; warn(...args: any[]): void; error(...args: any[]): void; } +export enum LogLevel { + info = 3, + warning = 2, + error = 1, +} + export class NullLogger implements Logger { info(...args: any[]): void {} warn(...args: any[]): void {} @@ -29,3 +37,67 @@ export class ConsoleLogger implements Logger { console.error(this.name + ':', ...args); } } + +export interface StorageLogRecord { + level: string; + ts: number; + message: string; +} + +export interface LogStorageInterface { + write(record: StorageLogRecord): void; +} + +export class StorageLogger implements Logger { + private storage: LogStorageInterface; + private readonly level: LogLevel; + + constructor(storage: LogStorageInterface, level: LogLevel) { + this.storage = storage; + this.level = level; + } + + info(...args: any[]): void { + if (this.level >= LogLevel.info) { + this.storage.write({ level: 'info', message: args.join(' '), ts: timestamp() }); + } + } + + warn(...args: any[]): void { + if (this.level >= LogLevel.warning) { + this.storage.write({ level: 'warn', message: args.join(' '), ts: timestamp() }); + } + } + + error(...args: any[]): void { + if (this.level >= LogLevel.error) { + this.storage.write({ level: 'error', message: args.join(' '), ts: timestamp() }); + } + } +} + +export class AggregateLogger implements Logger { + private readonly loggers: Array; + + constructor(loggers: Array) { + this.loggers = loggers; + } + + info(...args: any[]): void { + for (let lg of this.loggers) { + lg.info(...args); + } + } + + warn(...args: any[]): void { + for (let lg of this.loggers) { + lg.warn(...args); + } + } + + error(...args: any[]): void { + for (let lg of this.loggers) { + lg.error(...args); + } + } +} diff --git a/src/Storage/LogStorage.ts b/src/Storage/LogStorage.ts new file mode 100644 index 0000000..95fc124 --- /dev/null +++ b/src/Storage/LogStorage.ts @@ -0,0 +1,31 @@ +import { DataStorage } from '../DataStorage'; +import { ActionStatistics, StatisticsStorageInterface } from '../Statistics'; +import { LogStorageInterface, StorageLogRecord } from '../Logger'; + +const NAMESPACE = 'logs.v1'; +const RECORD_LIST_KEY = 'records'; + +const RECORD_COUNT = 200; + +export class LogStorage implements LogStorageInterface { + private storage: DataStorage; + constructor() { + this.storage = new DataStorage(NAMESPACE); + } + + write(record: StorageLogRecord): void { + const records = this.getRecords(); + records.push(record); + this.setRecords(records.slice(-RECORD_COUNT)); + } + + getRecords(): Array { + return this.storage.getTypedList(RECORD_LIST_KEY, { + factory: () => ({ level: '', message: '', ts: 0 }), + }); + } + + setRecords(records: Array): void { + this.storage.set(RECORD_LIST_KEY, records); + } +}