From 51b7783f0ba1347e84ffd1f1ab4857cc0c5bef0c Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 10 Nov 2023 14:52:00 -0300 Subject: [PATCH 01/15] feat: implements deno logger --- deno-runtime/lib/logger.ts | 44 +++++++++++++++++++++ deno-runtime/lib/messenger.ts | 7 +++- deno-runtime/main.ts | 6 ++- src/server/runtime/AppsEngineDenoRuntime.ts | 13 ++++-- 4 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 deno-runtime/lib/logger.ts diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts new file mode 100644 index 000000000..07b4911d7 --- /dev/null +++ b/deno-runtime/lib/logger.ts @@ -0,0 +1,44 @@ +export class Logger { + private static instance: Logger; + private entries: Array = []; + + private constructor() {} + + public static getInstance(): Logger { + if (!Logger.instance) { + Logger.instance = new Logger(); + } + + return Logger.instance; + } + + public debug(...args: Array) { + this.addEntry('DEBUG', ...args) + } + public info(...args: Array){ + this.addEntry('INFO', ...args) + } + private addEntry(severity: 'DEBUG' | 'INFO',...items: Array) { + const i = items.map((v) => { + if (v instanceof Error) { + return JSON.stringify(v, Object.getOwnPropertyNames(v)); + } + if (typeof v === 'object' && typeof v.stack === 'string' && typeof v.message === 'string') { + return JSON.stringify(v, Object.getOwnPropertyNames(v)); + } + const str = JSON.stringify(v, null, 2); + return str ? JSON.parse(str) : str; // force call toJSON to prevent circular references + }); + + this.entries.push({ + severity, + timestamp: new Date(), + args: i, + }); + } + public flush() { + const logs = this.entries; + this.entries = []; + return logs; + } +} diff --git a/deno-runtime/lib/messenger.ts b/deno-runtime/lib/messenger.ts index 1b8951726..aea704399 100644 --- a/deno-runtime/lib/messenger.ts +++ b/deno-runtime/lib/messenger.ts @@ -1,4 +1,5 @@ import * as jsonrpc from 'jsonrpc-lite'; +import { Logger } from './logger.ts'; export type RequestDescriptor = Pick; @@ -70,13 +71,15 @@ export async function sendMethodNotFound(id: jsonrpc.ID): Promise { } export async function errorResponse({ error: { message, code = -32000, data }, id }: ErrorResponseDescriptor): Promise { - const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, data)); + const logs = Logger.getInstance().flush(); + const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, {logs, ...data})); await send(rpc); } export async function successResponse({ id, result }: SuccessResponseDescriptor): Promise { - const rpc = jsonrpc.success(id, result); + const logs = Logger.getInstance().flush(); + const rpc = jsonrpc.success(id, {value: result, logs}); await send(rpc); } diff --git a/deno-runtime/main.ts b/deno-runtime/main.ts index 192be3276..1b7679647 100644 --- a/deno-runtime/main.ts +++ b/deno-runtime/main.ts @@ -13,12 +13,16 @@ import { sanitizeDeprecatedUsage } from "./lib/sanitizeDeprecatedUsage.ts"; import { AppAccessorsInstance } from "./lib/accessors/mod.ts"; import * as Messenger from "./lib/messenger.ts"; import { AppObjectRegistry } from "./AppObjectRegistry.ts"; +import { Logger } from "./lib/logger.ts"; const require = createRequire(import.meta.url); // @deno-types='../definition/App.d.ts' const { App } = require('../definition/App'); +const logger = Logger.getInstance() +logger.info('Starting Deno App Wrapper'); + const ALLOWED_NATIVE_MODULES = ['path', 'url', 'crypto', 'buffer', 'stream', 'net', 'http', 'https', 'zlib', 'util', 'punycode', 'os', 'querystring']; const ALLOWED_EXTERNAL_MODULES = ['uuid']; @@ -60,7 +64,7 @@ async function handlInitializeApp({ id, source }: { id: string; source: string } const exports = await wrapAppCode(source)(require); // This is the same naive logic we've been using in the App Compiler const appClass = Object.values(exports)[0] as typeof App; - const app = new appClass({ author: {} }, console, AppAccessorsInstance.getDefaultAppAccessors()); + const app = new appClass({ author: {} }, logger, AppAccessorsInstance.getDefaultAppAccessors()); if (typeof app.getName !== 'function') { throw new Error('App must contain a getName function'); diff --git a/src/server/runtime/AppsEngineDenoRuntime.ts b/src/server/runtime/AppsEngineDenoRuntime.ts index a9400cc1f..6f722c8be 100644 --- a/src/server/runtime/AppsEngineDenoRuntime.ts +++ b/src/server/runtime/AppsEngineDenoRuntime.ts @@ -260,6 +260,11 @@ export class DenoRuntimeSubprocessController extends EventEmitter { if (message.type === 'success') { param = message.payload.result; + const {value, logs} = param as any; + param = value; + + logs.forEach(({args, severity, timestamp}: { timestamp: any; severity: any; args: any; }) => console.log(`${timestamp} - [${severity}] - ${args}`)) + } else { param = message.payload.error; } @@ -311,10 +316,10 @@ export class AppsEngineDenoRuntime { private readonly apiManager: AppApiManager; - constructor(manager: AppManager) { - this.accessorManager = manager.getAccessorManager(); - this.apiManager = manager.getApiManager(); - } + // constructor(manager: AppManager) { + // this.accessorManager = manager.getAccessorManager(); + // this.apiManager = manager.getApiManager(); + // } public async startRuntimeForApp({ appId, appSource }: AppRuntimeParams, options = { force: false }): Promise { if (appId in this.subprocesses && !options.force) { From 11c62d9c2d35f4dafcbb85d5f97b140260f45c25 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 10 Nov 2023 14:59:37 -0300 Subject: [PATCH 02/15] feat: add other severities for logger --- deno-runtime/lib/logger.ts | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index 07b4911d7..418003e6a 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -1,3 +1,12 @@ +enum LogMessageSeverity { + DEBUG = 'debug', + INFORMATION = 'info', + LOG = 'log', + WARNING = 'warning', + ERROR = 'error', + SUCCESS = 'success', +} + export class Logger { private static instance: Logger; private entries: Array = []; @@ -13,12 +22,30 @@ export class Logger { } public debug(...args: Array) { - this.addEntry('DEBUG', ...args) + this.addEntry(LogMessageSeverity.DEBUG, ...args) } + public info(...args: Array){ - this.addEntry('INFO', ...args) + this.addEntry(LogMessageSeverity.INFORMATION, ...args) + } + + public log(...args: Array){ + this.addEntry(LogMessageSeverity.LOG, ...args) + } + + public warning(...args: Array){ + this.addEntry(LogMessageSeverity.WARNING, ...args) } - private addEntry(severity: 'DEBUG' | 'INFO',...items: Array) { + + public error(...args: Array){ + this.addEntry(LogMessageSeverity.ERROR, ...args) + } + + public success(...args: Array){ + this.addEntry(LogMessageSeverity.SUCCESS, ...args) + } + + private addEntry(severity: LogMessageSeverity,...items: Array) { const i = items.map((v) => { if (v instanceof Error) { return JSON.stringify(v, Object.getOwnPropertyNames(v)); @@ -36,6 +63,7 @@ export class Logger { args: i, }); } + public flush() { const logs = this.entries; this.entries = []; From c04c9a519f491a7960c90a22e44e2b7d6a535ca2 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Mon, 27 Nov 2023 17:43:34 -0300 Subject: [PATCH 03/15] feat: parse the logs to the AppConsole --- deno-runtime/lib/logger.ts | 3 ++ deno-runtime/main.ts | 1 + src/server/runtime/AppsEngineDenoRuntime.ts | 32 +++++++++++++++------ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index 418003e6a..1876b4efa 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -10,6 +10,7 @@ enum LogMessageSeverity { export class Logger { private static instance: Logger; private entries: Array = []; + public method = ''; private constructor() {} @@ -59,6 +60,7 @@ export class Logger { this.entries.push({ severity, + method: this.method, timestamp: new Date(), args: i, }); @@ -67,6 +69,7 @@ export class Logger { public flush() { const logs = this.entries; this.entries = []; + this.method = ''; return logs; } } diff --git a/deno-runtime/main.ts b/deno-runtime/main.ts index 1b7679647..91293e20d 100644 --- a/deno-runtime/main.ts +++ b/deno-runtime/main.ts @@ -102,6 +102,7 @@ async function handleRequest({ type, payload }: Messenger.JsonRpcRequest): Promi const { id, method, params } = payload; + logger.method = method; switch (method) { case 'construct': { const [appId, source] = params as [string, string]; diff --git a/src/server/runtime/AppsEngineDenoRuntime.ts b/src/server/runtime/AppsEngineDenoRuntime.ts index 6f722c8be..6557dbd89 100644 --- a/src/server/runtime/AppsEngineDenoRuntime.ts +++ b/src/server/runtime/AppsEngineDenoRuntime.ts @@ -6,6 +6,8 @@ import * as jsonrpc from 'jsonrpc-lite'; import type { AppAccessorManager, AppApiManager } from '../managers'; import type { AppManager } from '../AppManager'; +import type { AppLogStorage } from '../storage'; +import { AppConsole } from '../logging'; export type AppRuntimeParams = { appId: string; @@ -68,8 +70,10 @@ export class DenoRuntimeSubprocessController extends EventEmitter { private readonly api: AppApiManager; + private readonly logStorage: AppLogStorage; + // We need to keep the appSource around in case the Deno process needs to be restarted - constructor(private readonly appId: string, private readonly appSource: string, deps: ControllerDeps) { + constructor(private readonly appId: string, private readonly appSource: string, manager: AppManager) { super(); this.state = 'uninitialized'; @@ -86,8 +90,9 @@ export class DenoRuntimeSubprocessController extends EventEmitter { this.state = 'invalid'; } - this.accessors = deps.accessors; - this.api = deps.api; + // this.accessors = manager.getAccessorManager(); + // this.api = manager.getApiManager(); + // this.logStorage = manager.getLogStorage(); } emit(eventName: string | symbol, ...args: any[]): boolean { @@ -260,11 +265,22 @@ export class DenoRuntimeSubprocessController extends EventEmitter { if (message.type === 'success') { param = message.payload.result; - const {value, logs} = param as any; + const { value, logs } = param as any; param = value; - logs.forEach(({args, severity, timestamp}: { timestamp: any; severity: any; args: any; }) => console.log(`${timestamp} - [${severity}] - ${args}`)) + const logger = new AppConsole(logs[0].method); // the method will be the same for all entries + logs.forEach((log: any) => { + const logMethod = logger[log.severity as keyof AppConsole]; + + if (typeof logMethod !== 'function') { + throw new Error('Invalid log severity'); + } + + logMethod.apply(logger, log.args); + console.log(`${log.timestamp} - ${log.method} [${log.severity}]: ${log.args}`); + }); + // this.logStorage.storeEntries(this.appId, logs); } else { param = message.payload.error; } @@ -312,9 +328,7 @@ type ExecRequestContext = { export class AppsEngineDenoRuntime { private readonly subprocesses: Record = {}; - private readonly accessorManager: AppAccessorManager; - - private readonly apiManager: AppApiManager; + private readonly appManager: AppManager; // constructor(manager: AppManager) { // this.accessorManager = manager.getAccessorManager(); @@ -326,7 +340,7 @@ export class AppsEngineDenoRuntime { throw new Error('App already has an associated runtime'); } - this.subprocesses[appId] = new DenoRuntimeSubprocessController(appId, appSource, { accessors: this.accessorManager, api: this.apiManager }); + this.subprocesses[appId] = new DenoRuntimeSubprocessController(appId, appSource, this.appManager); await this.subprocesses[appId].setupApp(); } From af94a9ee139a49f241799cb507cf6f3cfb845823 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Mon, 27 Nov 2023 18:28:35 -0300 Subject: [PATCH 04/15] feat: add function caller name --- deno-runtime/lib/logger.ts | 39 +++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index 1876b4efa..f4d01f41d 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -1,3 +1,5 @@ +import * as stackTrace from 'npm:stack-trace' + enum LogMessageSeverity { DEBUG = 'debug', INFORMATION = 'info', @@ -23,30 +25,30 @@ export class Logger { } public debug(...args: Array) { - this.addEntry(LogMessageSeverity.DEBUG, ...args) + this.addEntry(LogMessageSeverity.DEBUG, this.getStack(stackTrace.get()), ...args) } public info(...args: Array){ - this.addEntry(LogMessageSeverity.INFORMATION, ...args) + this.addEntry(LogMessageSeverity.INFORMATION, this.getStack(stackTrace.get()), ...args) } public log(...args: Array){ - this.addEntry(LogMessageSeverity.LOG, ...args) + this.addEntry(LogMessageSeverity.LOG, this.getStack(stackTrace.get()), ...args) } public warning(...args: Array){ - this.addEntry(LogMessageSeverity.WARNING, ...args) + this.addEntry(LogMessageSeverity.WARNING, this.getStack(stackTrace.get()), ...args) } public error(...args: Array){ - this.addEntry(LogMessageSeverity.ERROR, ...args) + this.addEntry(LogMessageSeverity.ERROR, this.getStack(stackTrace.get()), ...args) } public success(...args: Array){ - this.addEntry(LogMessageSeverity.SUCCESS, ...args) + this.addEntry(LogMessageSeverity.SUCCESS, this.getStack(stackTrace.get()), ...args) } - private addEntry(severity: LogMessageSeverity,...items: Array) { + private addEntry(severity: LogMessageSeverity, caller?: string,...items: Array) { const i = items.map((v) => { if (v instanceof Error) { return JSON.stringify(v, Object.getOwnPropertyNames(v)); @@ -59,6 +61,7 @@ export class Logger { }); this.entries.push({ + caller, severity, method: this.method, timestamp: new Date(), @@ -66,6 +69,28 @@ export class Logger { }); } + private getStack(stack: Array) { + let func = 'anonymous'; + + if (stack.length === 1) { + return func; + } + + const frame = stack[1]; + + if (frame.getMethodName() === null) { + func = 'anonymous OR constructor'; + } else { + func = frame.getMethodName(); + } + + if (frame.getFunctionName() !== null) { + func = `${func} -> ${frame.getFunctionName()}`; + } + + return func; + } + public flush() { const logs = this.entries; this.entries = []; From 4aecd7f8e305b7cf488e16380fd7b39a1c887b0a Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 10:49:48 -0300 Subject: [PATCH 05/15] feat: change `storeEntries` params --- src/server/storage/AppLogStorage.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server/storage/AppLogStorage.ts b/src/server/storage/AppLogStorage.ts index 31dfb3232..1c5e4824f 100644 --- a/src/server/storage/AppLogStorage.ts +++ b/src/server/storage/AppLogStorage.ts @@ -1,5 +1,4 @@ import type { ILoggerStorageEntry } from '../logging'; -import type { AppConsole } from '../logging/AppConsole'; export interface IAppLogStorageFindOptions { sort?: { [field: string]: number }; @@ -17,7 +16,7 @@ export abstract class AppLogStorage { public abstract find(query: { [field: string]: any }, options?: IAppLogStorageFindOptions): Promise>; - public abstract storeEntries(appId: string, logger: AppConsole): Promise; + public abstract storeEntries(logEntry: ILoggerStorageEntry): Promise; public abstract getEntriesFor(appId: string): Promise>; From 66b6c915456d0033b408c0718e5b6c9453b95633 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 10:50:04 -0300 Subject: [PATCH 06/15] feat: instantiate logger every at every call --- deno-runtime/lib/logger.ts | 51 ++++++++++++++------- deno-runtime/lib/messenger.ts | 10 ++-- deno-runtime/main.ts | 10 ++-- src/server/AppManager.ts | 3 +- src/server/managers/AppApi.ts | 5 +- src/server/managers/AppSlashCommand.ts | 3 +- src/server/managers/AppVideoConfProvider.ts | 3 +- src/server/runtime/AppsEngineDenoRuntime.ts | 19 ++------ 8 files changed, 60 insertions(+), 44 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index f4d01f41d..76453962f 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -9,19 +9,27 @@ enum LogMessageSeverity { SUCCESS = 'success', } -export class Logger { - private static instance: Logger; - private entries: Array = []; - public method = ''; - - private constructor() {} - - public static getInstance(): Logger { - if (!Logger.instance) { - Logger.instance = new Logger(); - } +interface ILoggerStorageEntry { + appId: string; + method: string; + entries: Array; + startTime: Date; + endTime: Date; + totalTime: number; + _createdAt: Date; +} - return Logger.instance; +export class Logger { + private appId: string; + private entries: Array; + private start: Date; + private method: string; + + constructor(method: string, appId: string) { + this.appId = appId; + this.method = method; + this.entries = []; + this.start = new Date(); } public debug(...args: Array) { @@ -91,10 +99,19 @@ export class Logger { return func; } - public flush() { - const logs = this.entries; - this.entries = []; - this.method = ''; - return logs; + private getTotalTime(): number { + return new Date().getTime() - this.start.getTime(); + } + + public getLogs(): ILoggerStorageEntry { + return { + appId: this.appId, + method: this.method, + entries: this.entries, + startTime: this.start, + endTime: new Date(), + totalTime: this.getTotalTime(), + _createdAt: new Date(), + }; } } diff --git a/deno-runtime/lib/messenger.ts b/deno-runtime/lib/messenger.ts index aea704399..b5158c798 100644 --- a/deno-runtime/lib/messenger.ts +++ b/deno-runtime/lib/messenger.ts @@ -1,5 +1,7 @@ import * as jsonrpc from 'jsonrpc-lite'; -import { Logger } from './logger.ts'; + +import { AppObjectRegistry } from "../AppObjectRegistry.ts"; +import type { Logger } from './logger.ts' export type RequestDescriptor = Pick; @@ -71,14 +73,16 @@ export async function sendMethodNotFound(id: jsonrpc.ID): Promise { } export async function errorResponse({ error: { message, code = -32000, data }, id }: ErrorResponseDescriptor): Promise { - const logs = Logger.getInstance().flush(); + const logger = AppObjectRegistry.get('logger') as Logger; + const logs = logger.getLogs(); const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, {logs, ...data})); await send(rpc); } export async function successResponse({ id, result }: SuccessResponseDescriptor): Promise { - const logs = Logger.getInstance().flush(); + const logger = AppObjectRegistry.get('logger') as Logger; + const logs = logger.getLogs(); const rpc = jsonrpc.success(id, {value: result, logs}); await send(rpc); diff --git a/deno-runtime/main.ts b/deno-runtime/main.ts index 91293e20d..720038828 100644 --- a/deno-runtime/main.ts +++ b/deno-runtime/main.ts @@ -20,9 +20,6 @@ const require = createRequire(import.meta.url); // @deno-types='../definition/App.d.ts' const { App } = require('../definition/App'); -const logger = Logger.getInstance() -logger.info('Starting Deno App Wrapper'); - const ALLOWED_NATIVE_MODULES = ['path', 'url', 'crypto', 'buffer', 'stream', 'net', 'http', 'https', 'zlib', 'util', 'punycode', 'os', 'querystring']; const ALLOWED_EXTERNAL_MODULES = ['uuid']; @@ -64,6 +61,7 @@ async function handlInitializeApp({ id, source }: { id: string; source: string } const exports = await wrapAppCode(source)(require); // This is the same naive logic we've been using in the App Compiler const appClass = Object.values(exports)[0] as typeof App; + const logger = AppObjectRegistry.get('logger'); const app = new appClass({ author: {} }, logger, AppAccessorsInstance.getDefaultAppAccessors()); if (typeof app.getName !== 'function') { @@ -102,7 +100,11 @@ async function handleRequest({ type, payload }: Messenger.JsonRpcRequest): Promi const { id, method, params } = payload; - logger.method = method; + const appId: string = method === 'construct' ? (params as Array)[0] : AppObjectRegistry.get('id') as string; + + const logger = new Logger(method, appId); + AppObjectRegistry.set('logger', logger); + switch (method) { case 'construct': { const [appId, source] = params as [string, string]; diff --git a/src/server/AppManager.ts b/src/server/AppManager.ts index bc609f682..4335ec343 100644 --- a/src/server/AppManager.ts +++ b/src/server/AppManager.ts @@ -36,6 +36,7 @@ import type { IAppStorageItem } from './storage'; import { AppLogStorage, AppMetadataStorage } from './storage'; import { AppSourceStorage } from './storage/AppSourceStorage'; import { AppInstallationSource } from './storage/IAppStorageItem'; +import { AppConsole } from './logging'; export interface IAppInstallParameters { enable: boolean; @@ -263,7 +264,7 @@ export class AppManager { const app = DisabledApp.createNew(item.info, AppStatus.COMPILER_ERROR_DISABLED); app.getLogger().error(e); - await this.logStorage.storeEntries(app.getID(), app.getLogger()); + await this.logStorage.storeEntries(AppConsole.toStorageEntry(app.getID(), app.getLogger())); const prl = new ProxiedApp(this, item, app, new AppsEngineEmptyRuntime(app)); this.apps.set(item.id, prl); diff --git a/src/server/managers/AppApi.ts b/src/server/managers/AppApi.ts index cc371af83..c771e5fe2 100644 --- a/src/server/managers/AppApi.ts +++ b/src/server/managers/AppApi.ts @@ -6,6 +6,7 @@ import type { IApiEndpointInfo } from '../../definition/api/IApiEndpointInfo'; import type { ProxiedApp } from '../ProxiedApp'; import type { AppLogStorage } from '../storage'; import type { AppAccessorManager } from './AppAccessorManager'; +import { AppConsole } from '../logging'; export class AppApi { public readonly computedPath: string; @@ -83,12 +84,12 @@ export class AppApi { ], }); logger.debug(`${path}'s ${method} was successfully executed.`); - await logStorage.storeEntries(this.app.getID(), logger); + await logStorage.storeEntries(AppConsole.toStorageEntry(this.app.getID(), logger)); return result; } catch (e) { logger.error(e); logger.debug(`${path}'s ${method} was unsuccessful.`); - await logStorage.storeEntries(this.app.getID(), logger); + await logStorage.storeEntries(AppConsole.toStorageEntry(this.app.getID(), logger)); throw e; } } diff --git a/src/server/managers/AppSlashCommand.ts b/src/server/managers/AppSlashCommand.ts index 8a4147e49..7e849fd63 100644 --- a/src/server/managers/AppSlashCommand.ts +++ b/src/server/managers/AppSlashCommand.ts @@ -1,6 +1,7 @@ import { AppMethod } from '../../definition/metadata'; import type { ISlashCommand, ISlashCommandPreview, ISlashCommandPreviewItem, SlashCommandContext } from '../../definition/slashcommands'; import type { ProxiedApp } from '../ProxiedApp'; +import { AppConsole } from '../logging'; import type { AppLogStorage } from '../storage'; import type { AppAccessorManager } from './AppAccessorManager'; @@ -93,7 +94,7 @@ export class AppSlashCommand { logger.error(e); logger.debug(`${command}'s ${method} was unsuccessful.`); } finally { - await logStorage.storeEntries(this.app.getID(), logger); + await logStorage.storeEntries(AppConsole.toStorageEntry(this.app.getID(), logger)); } } } diff --git a/src/server/managers/AppVideoConfProvider.ts b/src/server/managers/AppVideoConfProvider.ts index 27e2c52f0..c2f0143f9 100644 --- a/src/server/managers/AppVideoConfProvider.ts +++ b/src/server/managers/AppVideoConfProvider.ts @@ -3,6 +3,7 @@ import type { IBlock } from '../../definition/uikit'; import type { VideoConference } from '../../definition/videoConferences'; import type { IVideoConferenceUser } from '../../definition/videoConferences/IVideoConferenceUser'; import type { IVideoConferenceOptions, IVideoConfProvider, VideoConfData, VideoConfDataExtended } from '../../definition/videoConfProviders'; +import { AppConsole } from '../logging'; import type { ProxiedApp } from '../ProxiedApp'; import type { AppLogStorage } from '../storage'; import type { AppAccessorManager } from './AppAccessorManager'; @@ -116,7 +117,7 @@ export class AppVideoConfProvider { } try { - await logStorage.storeEntries(this.app.getID(), logger); + await logStorage.storeEntries(AppConsole.toStorageEntry(this.app.getID(), logger)); } catch (e) { // Don't care, at the moment. // TODO: Evaluate to determine if we do care diff --git a/src/server/runtime/AppsEngineDenoRuntime.ts b/src/server/runtime/AppsEngineDenoRuntime.ts index 3753a94b6..50762715c 100644 --- a/src/server/runtime/AppsEngineDenoRuntime.ts +++ b/src/server/runtime/AppsEngineDenoRuntime.ts @@ -7,7 +7,6 @@ import * as jsonrpc from 'jsonrpc-lite'; import type { AppAccessorManager, AppApiManager } from '../managers'; import type { AppManager } from '../AppManager'; import type { AppLogStorage } from '../storage'; -import { AppConsole } from '../logging'; export type AppRuntimeParams = { appId: string; @@ -268,19 +267,7 @@ export class DenoRuntimeSubprocessController extends EventEmitter { const { value, logs } = param as any; param = value; - const logger = new AppConsole(logs[0].method); // the method will be the same for all entries - logs.forEach((log: any) => { - const logMethod = logger[log.severity as keyof AppConsole]; - - if (typeof logMethod !== 'function') { - throw new Error('Invalid log severity'); - } - - logMethod.apply(logger, log.args); - console.log(`${log.timestamp} - ${log.method} [${log.severity}]: ${log.args}`); - }); - - // this.logStorage.storeEntries(this.appId, logs); + this.logStorage.storeEntries(logs); } else { param = message.payload.error; } @@ -328,7 +315,9 @@ type ExecRequestContext = { export class AppsEngineDenoRuntime { private readonly subprocesses: Record = {}; - constructor(private readonly manager: AppManager) {} + private manager: AppManager; + + // constructor(private readonly manager: AppManager) {} public async startRuntimeForApp({ appId, appSource }: AppRuntimeParams, options = { force: false }): Promise { if (appId in this.subprocesses && !options.force) { From beaf0979475234cccec607fda424457fbd713714 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 11:22:12 -0300 Subject: [PATCH 07/15] fix: lint --- src/server/runtime/AppsEngineDenoRuntime.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/runtime/AppsEngineDenoRuntime.ts b/src/server/runtime/AppsEngineDenoRuntime.ts index 9543dec15..0556f2de2 100644 --- a/src/server/runtime/AppsEngineDenoRuntime.ts +++ b/src/server/runtime/AppsEngineDenoRuntime.ts @@ -74,7 +74,7 @@ export class DenoRuntimeSubprocessController extends EventEmitter { private readonly api: AppApiManager; private readonly logStorage: AppLogStorage; - + private readonly bridges: AppBridges; // We need to keep the appSource around in case the Deno process needs to be restarted From 8ef2f6e64e45bdc19d184782da6b3c17cd98a952 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 11:42:03 -0300 Subject: [PATCH 08/15] fix: lint problems --- deno-runtime/lib/logger.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index 76453962f..f0517a3ff 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -1,4 +1,6 @@ +// deno-lint-ignore-file no-explicit-any import * as stackTrace from 'npm:stack-trace' +import { StackFrame } from 'npm:stack-trace' enum LogMessageSeverity { DEBUG = 'debug', @@ -9,10 +11,18 @@ enum LogMessageSeverity { SUCCESS = 'success', } +type Entry = { + caller: string; + severity: LogMessageSeverity; + method: string; + timestamp: Date; + args: Array; +} + interface ILoggerStorageEntry { appId: string; method: string; - entries: Array; + entries: Array; startTime: Date; endTime: Date; totalTime: number; @@ -21,7 +31,7 @@ interface ILoggerStorageEntry { export class Logger { private appId: string; - private entries: Array; + private entries: Array; private start: Date; private method: string; @@ -32,31 +42,31 @@ export class Logger { this.start = new Date(); } - public debug(...args: Array) { + public debug(...args: Array): void { this.addEntry(LogMessageSeverity.DEBUG, this.getStack(stackTrace.get()), ...args) } - public info(...args: Array){ + public info(...args: Array): void { this.addEntry(LogMessageSeverity.INFORMATION, this.getStack(stackTrace.get()), ...args) } - public log(...args: Array){ + public log(...args: Array): void { this.addEntry(LogMessageSeverity.LOG, this.getStack(stackTrace.get()), ...args) } - public warning(...args: Array){ + public warning(...args: Array): void { this.addEntry(LogMessageSeverity.WARNING, this.getStack(stackTrace.get()), ...args) } - public error(...args: Array){ + public error(...args: Array): void { this.addEntry(LogMessageSeverity.ERROR, this.getStack(stackTrace.get()), ...args) } - public success(...args: Array){ + public success(...args: Array): void { this.addEntry(LogMessageSeverity.SUCCESS, this.getStack(stackTrace.get()), ...args) } - private addEntry(severity: LogMessageSeverity, caller?: string,...items: Array) { + private addEntry(severity: LogMessageSeverity, caller: string,...items: Array): void { const i = items.map((v) => { if (v instanceof Error) { return JSON.stringify(v, Object.getOwnPropertyNames(v)); @@ -77,7 +87,7 @@ export class Logger { }); } - private getStack(stack: Array) { + private getStack(stack: Array): string { let func = 'anonymous'; if (stack.length === 1) { From afdbadb4be743225b8800e74cf7d50d2f7d9cfa5 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 11:47:55 -0300 Subject: [PATCH 09/15] chore: deno.lock --- deno-runtime/deno.lock | 61 +++++++++++------------------------------- 1 file changed, 15 insertions(+), 46 deletions(-) diff --git a/deno-runtime/deno.lock b/deno-runtime/deno.lock index d168f8f0c..fbc6771b9 100644 --- a/deno-runtime/deno.lock +++ b/deno-runtime/deno.lock @@ -1,15 +1,17 @@ { - "version": "3", - "packages": { + "version": "2", + "remote": {}, + "npm": { "specifiers": { - "npm:@rocket.chat/ui-kit@^0.31.22": "npm:@rocket.chat/ui-kit@0.31.25_@rocket.chat+icons@0.32.0", - "npm:acorn-walk@8.2.0": "npm:acorn-walk@8.2.0", - "npm:acorn@8.10.0": "npm:acorn@8.10.0", - "npm:astring@1.8.6": "npm:astring@1.8.6", - "npm:jsonrpc-lite@2.2.0": "npm:jsonrpc-lite@2.2.0", - "npm:uuid@8.3.2": "npm:uuid@8.3.2" + "@rocket.chat/ui-kit@^0.31.22": "@rocket.chat/ui-kit@0.31.25_@rocket.chat+icons@0.32.0", + "acorn-walk@8.2.0": "acorn-walk@8.2.0", + "acorn@8.10.0": "acorn@8.10.0", + "astring@1.8.6": "astring@1.8.6", + "jsonrpc-lite@2.2.0": "jsonrpc-lite@2.2.0", + "stack-trace": "stack-trace@0.0.10", + "uuid@8.3.2": "uuid@8.3.2" }, - "npm": { + "packages": { "@rocket.chat/icons@0.32.0": { "integrity": "sha512-7yhhELKNLb9kUtXCvau0V+iMXraV2bOsxcPjc/ZtLR5VeeIDTeaflqRWGtLroX6f3bE+J1n5qB5zi8A4YXuH2g==", "dependencies": {} @@ -36,47 +38,14 @@ "integrity": "sha512-/cbbSxtZWs1O7R4tWqabrCM/t3N8qKUZMAg9IUqpPvUs6UyRvm6pCNYkskyKN/XU0UgffW+NY2ZRr8t0AknX7g==", "dependencies": {} }, + "stack-trace@0.0.10": { + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dependencies": {} + }, "uuid@8.3.2": { "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dependencies": {} } } - }, - "remote": { - "https://deno.land/std@0.203.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", - "https://deno.land/std@0.203.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", - "https://deno.land/std@0.203.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", - "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", - "https://deno.land/std@0.203.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", - "https://deno.land/std@0.203.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", - "https://deno.land/std@0.203.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", - "https://deno.land/std@0.203.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", - "https://deno.land/std@0.203.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", - "https://deno.land/std@0.203.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", - "https://deno.land/std@0.203.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", - "https://deno.land/std@0.203.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", - "https://deno.land/std@0.203.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", - "https://deno.land/std@0.203.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", - "https://deno.land/std@0.203.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", - "https://deno.land/std@0.203.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", - "https://deno.land/std@0.203.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", - "https://deno.land/std@0.203.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", - "https://deno.land/std@0.203.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", - "https://deno.land/std@0.203.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", - "https://deno.land/std@0.203.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", - "https://deno.land/std@0.203.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", - "https://deno.land/std@0.203.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", - "https://deno.land/std@0.203.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", - "https://deno.land/std@0.203.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", - "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", - "https://deno.land/std@0.203.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", - "https://deno.land/std@0.203.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", - "https://deno.land/std@0.203.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", - "https://deno.land/std@0.203.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", - "https://deno.land/std@0.203.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", - "https://deno.land/std@0.203.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", - "https://deno.land/std@0.203.0/testing/_test_suite.ts": "30f018feeb3835f12ab198d8a518f9089b1bcb2e8c838a8b615ab10d5005465c", - "https://deno.land/std@0.203.0/testing/bdd.ts": "3f446df5ef8e856a869e8eec54c8482590415741ff0b6358a00c43486cc15769", - "https://deno.land/std@0.203.0/testing/mock.ts": "6576b4aa55ee20b1990d656a78fff83599e190948c00e9f25a7f3ac5e9d6492d" } } From 08853e09ac148bcf6270b5cae0308b4844e1d2af Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 11:56:24 -0300 Subject: [PATCH 10/15] fix: change logStorage param --- src/server/ProxiedApp.ts | 2 +- src/server/compiler/AppCompiler.ts | 2 +- src/server/managers/AppSchedulerManager.ts | 3 ++- tests/test-data/storage/logStorage.ts | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/server/ProxiedApp.ts b/src/server/ProxiedApp.ts index 13e6b246a..7ce957caf 100644 --- a/src/server/ProxiedApp.ts +++ b/src/server/ProxiedApp.ts @@ -89,7 +89,7 @@ export class ProxiedApp implements IApp { throw e; } } finally { - await this.manager.getLogStorage().storeEntries(this.getID(), logger); + await this.manager.getLogStorage().storeEntries(AppConsole.toStorageEntry(this.getID(), logger)); } return result; diff --git a/src/server/compiler/AppCompiler.ts b/src/server/compiler/AppCompiler.ts index d81e5c293..5805401d7 100644 --- a/src/server/compiler/AppCompiler.ts +++ b/src/server/compiler/AppCompiler.ts @@ -83,7 +83,7 @@ export class AppCompiler { // TODO: Fix this type cast from to any to the right one const app = new ProxiedApp(manager, storage, rl as App, new Runtime(rl as App, customRequire as any)); - await manager.getLogStorage().storeEntries(app.getID(), logger); + await manager.getLogStorage().storeEntries(AppConsole.toStorageEntry(app.getID(), logger)); return app; } diff --git a/src/server/managers/AppSchedulerManager.ts b/src/server/managers/AppSchedulerManager.ts index e76267c48..36bf61056 100644 --- a/src/server/managers/AppSchedulerManager.ts +++ b/src/server/managers/AppSchedulerManager.ts @@ -5,6 +5,7 @@ import type { AppManager } from '../AppManager'; import type { IInternalSchedulerBridge } from '../bridges/IInternalSchedulerBridge'; import type { SchedulerBridge } from '../bridges/SchedulerBridge'; import type { AppAccessorManager } from '.'; +import { AppConsole } from '../logging'; function createProcessorId(jobId: string, appId: string): string { return jobId.includes(`_${appId}`) ? jobId : `${jobId}_${appId}`; @@ -84,7 +85,7 @@ export class AppSchedulerManager { throw e; } finally { - await this.manager.getLogStorage().storeEntries(appId, logger); + await this.manager.getLogStorage().storeEntries(AppConsole.toStorageEntry(appId, logger)); } }; } diff --git a/tests/test-data/storage/logStorage.ts b/tests/test-data/storage/logStorage.ts index cb7f80bdc..5ddb90e56 100644 --- a/tests/test-data/storage/logStorage.ts +++ b/tests/test-data/storage/logStorage.ts @@ -1,4 +1,4 @@ -import type { AppConsole, ILoggerStorageEntry } from '../../../src/server/logging'; +import type { ILoggerStorageEntry } from '../../../src/server/logging'; import type { IAppLogStorageFindOptions } from '../../../src/server/storage'; import { AppLogStorage } from '../../../src/server/storage'; @@ -11,7 +11,7 @@ export class TestsAppLogStorage extends AppLogStorage { return Promise.resolve([]); } - public storeEntries(appId: string, logger: AppConsole): Promise { + public storeEntries(logEntry: ILoggerStorageEntry): Promise { return Promise.resolve({} as ILoggerStorageEntry); } From 3d49c5a61cb3dd9045fdf2bb14b9955bb0c8c42b Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Fri, 8 Dec 2023 12:02:28 -0300 Subject: [PATCH 11/15] chore: deno.lock --- deno-runtime/deno.lock | 58 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/deno-runtime/deno.lock b/deno-runtime/deno.lock index fbc6771b9..6f892ee7f 100644 --- a/deno-runtime/deno.lock +++ b/deno-runtime/deno.lock @@ -1,17 +1,16 @@ { - "version": "2", - "remote": {}, - "npm": { + "version": "3", + "packages": { "specifiers": { - "@rocket.chat/ui-kit@^0.31.22": "@rocket.chat/ui-kit@0.31.25_@rocket.chat+icons@0.32.0", - "acorn-walk@8.2.0": "acorn-walk@8.2.0", - "acorn@8.10.0": "acorn@8.10.0", - "astring@1.8.6": "astring@1.8.6", - "jsonrpc-lite@2.2.0": "jsonrpc-lite@2.2.0", - "stack-trace": "stack-trace@0.0.10", - "uuid@8.3.2": "uuid@8.3.2" + "npm:@rocket.chat/ui-kit@^0.31.22": "npm:@rocket.chat/ui-kit@0.31.25_@rocket.chat+icons@0.32.0", + "npm:acorn-walk@8.2.0": "npm:acorn-walk@8.2.0", + "npm:acorn@8.10.0": "npm:acorn@8.10.0", + "npm:astring@1.8.6": "npm:astring@1.8.6", + "npm:jsonrpc-lite@2.2.0": "npm:jsonrpc-lite@2.2.0", + "npm:stack-trace": "npm:stack-trace@0.0.10", + "npm:uuid@8.3.2": "npm:uuid@8.3.2" }, - "packages": { + "npm": { "@rocket.chat/icons@0.32.0": { "integrity": "sha512-7yhhELKNLb9kUtXCvau0V+iMXraV2bOsxcPjc/ZtLR5VeeIDTeaflqRWGtLroX6f3bE+J1n5qB5zi8A4YXuH2g==", "dependencies": {} @@ -47,5 +46,42 @@ "dependencies": {} } } + }, + "remote": { + "https://deno.land/std@0.203.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.203.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", + "https://deno.land/std@0.203.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.203.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.203.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.203.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", + "https://deno.land/std@0.203.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.203.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", + "https://deno.land/std@0.203.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", + "https://deno.land/std@0.203.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", + "https://deno.land/std@0.203.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", + "https://deno.land/std@0.203.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", + "https://deno.land/std@0.203.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", + "https://deno.land/std@0.203.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", + "https://deno.land/std@0.203.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.203.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.203.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.203.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.203.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.203.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", + "https://deno.land/std@0.203.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.203.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", + "https://deno.land/std@0.203.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.203.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.203.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.203.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.203.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", + "https://deno.land/std@0.203.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.203.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.203.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", + "https://deno.land/std@0.203.0/testing/_test_suite.ts": "30f018feeb3835f12ab198d8a518f9089b1bcb2e8c838a8b615ab10d5005465c", + "https://deno.land/std@0.203.0/testing/bdd.ts": "3f446df5ef8e856a869e8eec54c8482590415741ff0b6358a00c43486cc15769", + "https://deno.land/std@0.203.0/testing/mock.ts": "6576b4aa55ee20b1990d656a78fff83599e190948c00e9f25a7f3ac5e9d6492d" } } From 0da2edc3b786e60b9b64aa94a0e9b0537bed1559 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Mon, 11 Dec 2023 09:18:18 -0300 Subject: [PATCH 12/15] refactor: change any for unknown --- deno-runtime/lib/logger.ts | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index f0517a3ff..53bee44a5 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -1,4 +1,3 @@ -// deno-lint-ignore-file no-explicit-any import * as stackTrace from 'npm:stack-trace' import { StackFrame } from 'npm:stack-trace' @@ -16,7 +15,7 @@ type Entry = { severity: LogMessageSeverity; method: string; timestamp: Date; - args: Array; + args: Array; } interface ILoggerStorageEntry { @@ -42,40 +41,44 @@ export class Logger { this.start = new Date(); } - public debug(...args: Array): void { + public debug(...args: Array): void { this.addEntry(LogMessageSeverity.DEBUG, this.getStack(stackTrace.get()), ...args) } - public info(...args: Array): void { + public info(...args: Array): void { this.addEntry(LogMessageSeverity.INFORMATION, this.getStack(stackTrace.get()), ...args) } - public log(...args: Array): void { + public log(...args: Array): void { this.addEntry(LogMessageSeverity.LOG, this.getStack(stackTrace.get()), ...args) } - public warning(...args: Array): void { + public warning(...args: Array): void { this.addEntry(LogMessageSeverity.WARNING, this.getStack(stackTrace.get()), ...args) } - public error(...args: Array): void { + public error(...args: Array): void { this.addEntry(LogMessageSeverity.ERROR, this.getStack(stackTrace.get()), ...args) } - public success(...args: Array): void { + public success(...args: Array): void { this.addEntry(LogMessageSeverity.SUCCESS, this.getStack(stackTrace.get()), ...args) } - private addEntry(severity: LogMessageSeverity, caller: string,...items: Array): void { - const i = items.map((v) => { - if (v instanceof Error) { - return JSON.stringify(v, Object.getOwnPropertyNames(v)); + private addEntry(severity: LogMessageSeverity, caller: string, ...items: Array): void { + const i = items.map((args) => { + if (args instanceof Error) { + return JSON.stringify(args, Object.getOwnPropertyNames(args)); } - if (typeof v === 'object' && typeof v.stack === 'string' && typeof v.message === 'string') { - return JSON.stringify(v, Object.getOwnPropertyNames(v)); + if (typeof args === 'object' && args !== null && 'stack' in args) { + return JSON.stringify(args, Object.getOwnPropertyNames(args)); } - const str = JSON.stringify(v, null, 2); + if (typeof args === 'object' && args !== null && 'message' in args) { + return JSON.stringify(args, Object.getOwnPropertyNames(args)); + } + const str = JSON.stringify(args, null, 2); return str ? JSON.parse(str) : str; // force call toJSON to prevent circular references + }); this.entries.push({ From 606ac914e281a9935e27f64c97e90184129ba983 Mon Sep 17 00:00:00 2001 From: Rafael Tapia Date: Mon, 11 Dec 2023 16:41:56 -0300 Subject: [PATCH 13/15] test: add tests to logger file --- deno-runtime/lib/tests/logger.test.ts | 111 ++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 deno-runtime/lib/tests/logger.test.ts diff --git a/deno-runtime/lib/tests/logger.test.ts b/deno-runtime/lib/tests/logger.test.ts new file mode 100644 index 000000000..2f275e30c --- /dev/null +++ b/deno-runtime/lib/tests/logger.test.ts @@ -0,0 +1,111 @@ +import { assertEquals } from 'https://deno.land/std@0.203.0/assert/mod.ts'; +import { describe, it } from 'https://deno.land/std@0.203.0/testing/bdd.ts'; +import { Logger } from "../logger.ts"; + +describe('Logger', () => { + it('getLogs should return an array of entries', () => { + const logger = new Logger('test', 'test'); + logger.info('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.method, 'test'); + }) + + it('should be able to add entries of different severity', () => { + const logger = new Logger('test', 'test'); + logger.info('test'); + logger.debug('test'); + logger.error('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 3); + assertEquals(logs.entries[0].severity, 'info'); + assertEquals(logs.entries[1].severity, 'debug'); + assertEquals(logs.entries[2].severity, 'error'); + }) + + it('should be able to add an info entry', () => { + const logger = new Logger('test', 'test'); + logger.info('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'info'); + }); + + it('should be able to add an debug entry', () => { + const logger = new Logger('test', 'test'); + logger.debug('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'debug'); + }); + + it('should be able to add an error entry', () => { + const logger = new Logger('test', 'test'); + logger.error('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'error'); + }); + + it('should be able to add an success entry', () => { + const logger = new Logger('test', 'test'); + logger.success('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'success'); + }); + + it('should be able to add an warning entry', () => { + const logger = new Logger('test', 'test'); + logger.warning('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'warning'); + }); + + it('should be able to add an log entry', () => { + const logger = new Logger('test', 'test'); + logger.log('test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'log'); + }); + + it('should be able to add an entry with multiple arguments', () => { + const logger = new Logger('test', 'test'); + logger.log('test', 'test', 'test'); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].args[1], 'test'); + assertEquals(logs.entries[0].args[2], 'test'); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'log'); + }); + + it('should be able to add an entry with multiple arguments of different types', () => { + const logger = new Logger('test', 'test'); + logger.log('test', 1, true, { foo: 'bar' }); + const logs = logger.getLogs(); + assertEquals(logs.entries.length, 1); + assertEquals(logs.entries[0].args[0], 'test'); + assertEquals(logs.entries[0].args[1], 1); + assertEquals(logs.entries[0].args[2], true); + assertEquals(logs.entries[0].args[3], { foo: 'bar' }); + assertEquals(logs.entries[0].method, 'test'); + assertEquals(logs.entries[0].severity, 'log'); + }); + +}) From f23206409f59578f54a2f9afecafaba5f9fcee89 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 13 Dec 2023 18:39:55 -0300 Subject: [PATCH 14/15] Logger can be undefined --- deno-runtime/AppObjectRegistry.ts | 6 ++++-- deno-runtime/lib/logger.ts | 6 +++++- deno-runtime/lib/messenger.ts | 21 +++++++++++++++------ 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/deno-runtime/AppObjectRegistry.ts b/deno-runtime/AppObjectRegistry.ts index d75e78460..9069c17ea 100644 --- a/deno-runtime/AppObjectRegistry.ts +++ b/deno-runtime/AppObjectRegistry.ts @@ -1,8 +1,10 @@ +export type Maybe = T | null | undefined; + export const AppObjectRegistry = new class { registry: Record = {}; - public get(key: string): unknown { - return this.registry[key]; + public get(key: string): Maybe { + return this.registry[key] as Maybe; } public set(key: string, value: unknown): void { diff --git a/deno-runtime/lib/logger.ts b/deno-runtime/lib/logger.ts index 53bee44a5..bf84a176f 100644 --- a/deno-runtime/lib/logger.ts +++ b/deno-runtime/lib/logger.ts @@ -78,7 +78,7 @@ export class Logger { } const str = JSON.stringify(args, null, 2); return str ? JSON.parse(str) : str; // force call toJSON to prevent circular references - + }); this.entries.push({ @@ -116,6 +116,10 @@ export class Logger { return new Date().getTime() - this.start.getTime(); } + public hasEntries(): boolean { + return this.entries.length > 0; + } + public getLogs(): ILoggerStorageEntry { return { appId: this.appId, diff --git a/deno-runtime/lib/messenger.ts b/deno-runtime/lib/messenger.ts index 97dd43859..1baf34d1d 100644 --- a/deno-runtime/lib/messenger.ts +++ b/deno-runtime/lib/messenger.ts @@ -73,17 +73,26 @@ export async function sendMethodNotFound(id: jsonrpc.ID): Promise { } export async function errorResponse({ error: { message, code = -32000, data }, id }: ErrorResponseDescriptor): Promise { - const logger = AppObjectRegistry.get('logger') as Logger; - const logs = logger.getLogs(); - const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, {logs, ...data})); + const logger = AppObjectRegistry.get('logger'); + + if (logger?.hasEntries()) { + data.logs = logger.getLogs(); + } + + const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, data)); await send(rpc); } export async function successResponse({ id, result }: SuccessResponseDescriptor): Promise { - const logger = AppObjectRegistry.get('logger') as Logger; - const logs = logger.getLogs(); - const rpc = jsonrpc.success(id, {value: result, logs}); + const payload = { value: result } as Record; + const logger = AppObjectRegistry.get('logger'); + + if (logger?.hasEntries()) { + payload.logs = logger.getLogs(); + } + + const rpc = jsonrpc.success(id, payload); await send(rpc); } From c4a7bb749a9e0f1982486277f06aba78d791f9cf Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 13 Dec 2023 19:50:51 -0300 Subject: [PATCH 15/15] Add Transport object to aid testing and messenger test for logs --- deno-runtime/lib/messenger.ts | 53 +++++++++---- deno-runtime/lib/tests/messenger.test.ts | 95 ++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 deno-runtime/lib/tests/messenger.test.ts diff --git a/deno-runtime/lib/messenger.ts b/deno-runtime/lib/messenger.ts index 1baf34d1d..eb2d8bd6e 100644 --- a/deno-runtime/lib/messenger.ts +++ b/deno-runtime/lib/messenger.ts @@ -29,6 +29,36 @@ export function isErrorResponse(message: jsonrpc.JsonRpc): message is jsonrpc.Er const encoder = new TextEncoder(); export const RPCResponseObserver = new EventTarget(); +export const Transport = new class Transporter { + private selectedTransport: Transporter["stdoutTransport"] | Transporter["noopTransport"]; + + constructor() { + this.selectedTransport = this.stdoutTransport.bind(this); + } + + private async stdoutTransport(message: jsonrpc.JsonRpc): Promise { + const encoded = encoder.encode(message.serialize()); + await Deno.stdout.write(encoded); + } + + private async noopTransport(_message: jsonrpc.JsonRpc): Promise { } + + public selectTransport(transport: 'stdout' | 'noop'): void { + switch (transport) { + case 'stdout': + this.selectedTransport = this.stdoutTransport.bind(this); + break; + case 'noop': + this.selectedTransport = this.noopTransport.bind(this); + break; + } + } + + public send(message: jsonrpc.JsonRpc): Promise { + return this.selectedTransport(message); + } +} + export function parseMessage(message: string) { const parsed = jsonrpc.parse(message); @@ -43,36 +73,31 @@ export function parseMessage(message: string) { return parsed; } -export async function send(message: jsonrpc.JsonRpc): Promise { - const encoded = encoder.encode(message.serialize()); - await Deno.stdout.write(encoded); -} - export async function sendInvalidRequestError(): Promise { const rpc = jsonrpc.error(null, jsonrpc.JsonRpcError.invalidRequest(null)); - await send(rpc); + await Transport.send(rpc); } export async function sendInvalidParamsError(id: jsonrpc.ID): Promise { const rpc = jsonrpc.error(id, jsonrpc.JsonRpcError.invalidParams(null)); - await send(rpc); + await Transport.send(rpc); } export async function sendParseError(): Promise { const rpc = jsonrpc.error(null, jsonrpc.JsonRpcError.parseError(null)); - await send(rpc); + await Transport.send(rpc); } export async function sendMethodNotFound(id: jsonrpc.ID): Promise { const rpc = jsonrpc.error(id, jsonrpc.JsonRpcError.methodNotFound(null)); - await send(rpc); + await Transport.send(rpc); } -export async function errorResponse({ error: { message, code = -32000, data }, id }: ErrorResponseDescriptor): Promise { +export async function errorResponse({ error: { message, code = -32000, data = {} }, id }: ErrorResponseDescriptor): Promise { const logger = AppObjectRegistry.get('logger'); if (logger?.hasEntries()) { @@ -81,7 +106,7 @@ export async function errorResponse({ error: { message, code = -32000, data }, i const rpc = jsonrpc.error(id, new jsonrpc.JsonRpcError(message, code, data)); - await send(rpc); + await Transport.send(rpc); } export async function successResponse({ id, result }: SuccessResponseDescriptor): Promise { @@ -94,13 +119,13 @@ export async function successResponse({ id, result }: SuccessResponseDescriptor) const rpc = jsonrpc.success(id, payload); - await send(rpc); + await Transport.send(rpc); } export async function sendRequest(requestDescriptor: RequestDescriptor): Promise { const request = jsonrpc.request(Math.random().toString(36).slice(2), requestDescriptor.method, requestDescriptor.params); - await send(request); + await Transport.send(request); // TODO: add timeout to this return new Promise((resolve, reject) => { @@ -123,5 +148,5 @@ export async function sendRequest(requestDescriptor: RequestDescriptor): Promise export function sendNotification({ method, params }: NotificationDescriptor) { const request = jsonrpc.notification(method, params); - send(request); + Transport.send(request); } diff --git a/deno-runtime/lib/tests/messenger.test.ts b/deno-runtime/lib/tests/messenger.test.ts new file mode 100644 index 000000000..588618bfa --- /dev/null +++ b/deno-runtime/lib/tests/messenger.test.ts @@ -0,0 +1,95 @@ +import { assertEquals, assertObjectMatch } from 'https://deno.land/std@0.203.0/assert/mod.ts'; +import { afterAll, beforeEach, describe, it } from 'https://deno.land/std@0.203.0/testing/bdd.ts'; +import { spy } from 'https://deno.land/std@0.203.0/testing/mock.ts'; + +import * as Messenger from '../messenger.ts'; +import { AppObjectRegistry } from '../../AppObjectRegistry.ts'; +import { Logger } from '../logger.ts'; + +describe('Messenger', () => { + beforeEach(() => { + AppObjectRegistry.clear(); + AppObjectRegistry.set('logger', new Logger('test', 'test')); + Messenger.Transport.selectTransport('noop'); + }); + + afterAll(() => { + AppObjectRegistry.clear(); + Messenger.Transport.selectTransport('stdout'); + }); + + it('should add logs to success responses', async () => { + const theSpy = spy(Messenger.Transport, 'send'); + + const logger = AppObjectRegistry.get('logger') as Logger; + + logger.info('test'); + + await Messenger.successResponse({ id: 'test', result: 'test' }); + + assertEquals(theSpy.calls.length, 1); + + const [responseArgument] = theSpy.calls[0].args; + + assertObjectMatch(responseArgument, { + jsonrpc: '2.0', + id: 'test', + result: { + value: 'test', + logs: { + appId: 'test', + method: 'test', + entries: [ + { + severity: 'info', + method: 'test', + args: ['test'], + caller: 'anonymous OR constructor', + }, + ], + }, + }, + }); + + theSpy.restore(); + }); + + it('should add logs to error responses', async () => { + const theSpy = spy(Messenger.Transport, 'send'); + + const logger = AppObjectRegistry.get('logger') as Logger; + + logger.info('test'); + + await Messenger.errorResponse({ id: 'test', error: { code: -32000, message: 'test' } }); + + assertEquals(theSpy.calls.length, 1); + + const [responseArgument] = theSpy.calls[0].args; + + assertObjectMatch(responseArgument, { + jsonrpc: '2.0', + id: 'test', + error: { + code: -32000, + message: 'test', + data: { + logs: { + appId: 'test', + method: 'test', + entries: [ + { + severity: 'info', + method: 'test', + args: ['test'], + caller: 'anonymous OR constructor', + }, + ], + }, + }, + }, + }); + + theSpy.restore(); + }); +});