Revision 52d1650fa2ea8f57acbcfabb82f7c7a57e5b6d09 authored by Boris Sekachev on 07 December 2023, 08:38:09 UTC, committed by GitHub on 07 December 2023, 08:38:09 UTC
1 parent 571e622
logger-storage.ts
// Copyright (C) 2019-2022 Intel Corporation
// Copyright (C) 2022-2023 CVAT.ai Corporation
//
// SPDX-License-Identifier: MIT
import PluginRegistry from './plugins';
import serverProxy from './server-proxy';
import logFactory, { EventLogger } from './log';
import { LogType } from './enums';
import { ArgumentError } from './exceptions';
function sleep(ms): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
function defaultUpdate(previousLog: EventLogger, currentPayload: any): object {
return {
...previousLog.payload,
...currentPayload,
};
}
interface IgnoreRule {
lastLog: EventLogger | null;
timeThreshold?: number;
ignore: (previousLog: EventLogger, currentPayload: any) => boolean;
update: (previousLog: EventLogger, currentPayload: any) => object;
}
type IgnoredRules = LogType.zoomImage | LogType.changeAttribute | LogType.changeFrame;
class LoggerStorage {
public clientID: string;
public collection: Array<EventLogger>;
public ignoreRules: Record<IgnoredRules, IgnoreRule>;
public isActiveChecker: (() => boolean) | null;
public saving: boolean;
public compressedLogs: Array<LogType>;
constructor() {
this.clientID = Date.now().toString().substr(-6);
this.collection = [];
this.isActiveChecker = null;
this.saving = false;
this.compressedLogs = [LogType.changeFrame];
this.ignoreRules = {
[LogType.zoomImage]: {
lastLog: null,
timeThreshold: 4000,
ignore(previousLog: EventLogger): boolean {
return (Date.now() - previousLog.time.getTime()) < this.timeThreshold;
},
update: defaultUpdate,
},
[LogType.changeAttribute]: {
lastLog: null,
ignore(previousLog: EventLogger, currentPayload: any): boolean {
return (
currentPayload.object_id === previousLog.payload.object_id &&
currentPayload.id === previousLog.payload.id
);
},
update: defaultUpdate,
},
[LogType.changeFrame]: {
lastLog: null,
ignore(previousLog: EventLogger, currentPayload: any): boolean {
return (
currentPayload.job_id === previousLog.payload.job_id &&
currentPayload.step === previousLog.payload.step
);
},
update(previousLog: EventLogger, currentPayload: any): object {
return {
...previousLog.payload,
to: currentPayload.to,
count: previousLog.payload.count + 1,
};
},
},
};
}
public async configure(isActiveChecker, activityHelper): Promise<void> {
const result = await PluginRegistry.apiWrapper.call(
this,
LoggerStorage.prototype.configure,
isActiveChecker,
activityHelper,
);
return result;
}
public async log(logType: LogType, payload = {}, wait = false): Promise<EventLogger> {
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.log, logType, payload, wait);
return result;
}
public async save(): Promise<void> {
const result = await PluginRegistry.apiWrapper.call(this, LoggerStorage.prototype.save);
return result;
}
}
Object.defineProperties(LoggerStorage.prototype.configure, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(isActiveChecker: () => boolean, userActivityCallback: Array<any>) {
if (typeof isActiveChecker !== 'function') {
throw new ArgumentError('isActiveChecker argument must be callable');
}
if (!Array.isArray(userActivityCallback)) {
throw new ArgumentError('userActivityCallback argument must be an array');
}
this.isActiveChecker = () => !!isActiveChecker();
},
},
});
Object.defineProperties(LoggerStorage.prototype.log, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation(logType: LogType, payload: any, wait: boolean) {
if (typeof payload !== 'object') {
throw new ArgumentError('Payload must be an object');
}
if (typeof wait !== 'boolean') {
throw new ArgumentError('Wait must be boolean');
}
if (!this.compressedLogs.includes(logType)) {
this.compressedLogs.forEach((compressedType: LogType) => {
this.ignoreRules[compressedType].lastLog = null;
});
}
if (logType in this.ignoreRules) {
const ignoreRule = this.ignoreRules[logType];
const { lastLog } = ignoreRule;
if (lastLog && ignoreRule.ignore(lastLog, payload)) {
lastLog.payload = ignoreRule.update(lastLog, payload);
return ignoreRule.lastLog;
}
}
const logPayload = { ...payload };
logPayload.client_id = this.clientID;
if (this.isActiveChecker) {
logPayload.is_active = this.isActiveChecker();
}
const log = logFactory(logType, { ...logPayload });
const pushEvent = (): void => {
log.validatePayload();
log.onClose(null);
this.collection.push(log);
if (logType in this.ignoreRules) {
this.ignoreRules[logType].lastLog = log;
}
};
if (log.scope === LogType.exception) {
await serverProxy.events.save({
events: [log.dump()],
timestamp: new Date().toISOString(),
});
return log;
}
if (wait) {
log.onClose(pushEvent);
} else {
pushEvent();
}
return log;
},
},
});
Object.defineProperties(LoggerStorage.prototype.save, {
implementation: {
writable: false,
enumerable: false,
value: async function implementation() {
if (!this.collection.length) {
return;
}
while (this.saving) {
await sleep(100);
}
const collectionToSend = [...this.collection];
const logPayload: any = {
client_id: this.clientID,
};
if (this.isActiveChecker) {
logPayload.is_active = this.isActiveChecker();
}
try {
this.saving = true;
await serverProxy.events.save({
events: collectionToSend.map((log) => log.dump()),
timestamp: new Date().toISOString(),
});
for (const rule of Object.values<IgnoreRule>(this.ignoreRules)) {
rule.lastLog = null;
}
this.collection = [];
} finally {
this.saving = false;
}
},
},
});
export default new LoggerStorage();
Computing file changes ...