diff --git a/src/Zemu.ts b/src/Zemu.ts index 1afa6da..c3f0dc1 100644 --- a/src/Zemu.ts +++ b/src/Zemu.ts @@ -279,7 +279,7 @@ export default class Zemu { } log(message: string): void { - if (this.startOptions.logging) { + if (this.startOptions.logger?.enabled ?? this.startOptions.logging) { const currentTimestamp = new Date().toISOString().slice(11, 23); process.stdout.write(`[${this.containerName}] ${currentTimestamp}: ${message}\n`); } diff --git a/src/emulator.ts b/src/emulator.ts index b23d8a1..f5623df 100644 --- a/src/emulator.ts +++ b/src/emulator.ts @@ -15,13 +15,21 @@ ******************************************************************************* */ import Docker, { type Container, type ContainerInfo } from "dockerode"; import path from "path"; +import { Transform } from "stream"; export const DEV_CERT_PRIVATE_KEY = "ff701d781f43ce106f72dc26a46b6a83e053b5d07bb3d4ceab79c91ca822a66b"; export const BOLOS_SDK = "/project/deps/nanos-secure-sdk"; export const DEFAULT_APP_PATH = "/project/app/bin"; export default class EmuContainer { - private logging: boolean; + private logger: { + enabled: boolean; + timestamp: { + enabled: boolean; + format: "unix" | "iso"; + }; + }; + private readonly elfLocalPath: string; private readonly name: string; private readonly image: string; @@ -33,7 +41,13 @@ export default class EmuContainer { this.elfLocalPath = elfLocalPath; this.libElfs = libElfs; this.name = name; - this.logging = false; + this.logger = { + enabled: false, + timestamp: { + enabled: false, + format: "iso", + }, + }; } static killContainerByName(name: string): void { @@ -83,11 +97,35 @@ export default class EmuContainer { } log(message: string): void { - if (this.logging ?? false) process.stdout.write(`${message}\n`); + if (this.logger.enabled) { + let msg = message; + + if (this.logger.timestamp.enabled) { + switch (this.logger.timestamp.format) { + case "iso": + msg = `[${new Date().toISOString()}] ${message}`; + break; + case "unix": + msg = `[${new Date().getTime()}] ${message}`; + break; + default: + throw new Error("invalid logger timestamp format"); + } + } + + process.stdout.write(`${msg}\n`); + } } async runContainer(options: { logging: boolean; + logger?: { + enabled: boolean; + timestamp: { + enabled: boolean; + format: "unix" | "iso"; + }; + }; custom: string; model: string; transportPort: string; @@ -95,7 +133,7 @@ export default class EmuContainer { }): Promise { const docker = new Docker(); - this.logging = options.logging; + this.logger = options.logger ?? { enabled: options.logging, timestamp: { enabled: false, format: "iso" } }; const appFilename = path.basename(this.elfLocalPath); const appDir = path.dirname(this.elfLocalPath); @@ -153,11 +191,35 @@ export default class EmuContainer { this.log(`[ZEMU] Connected ${this.currentContainer.id}`); - if (this.logging) { - this.currentContainer.attach({ stream: true, stdout: true, stderr: true }, (err: any, stream: any) => { - if (err != null) throw err; - stream.pipe(process.stdout); + if (this.logger.enabled) { + const timestampTransform = new Transform({ + transform: (chunk, encoding, callback) => { + if (this.logger.timestamp.enabled) { + switch (this.logger.timestamp.format) { + case "iso": + callback(null, `[${new Date().toISOString()}] ${chunk}`); + break; + case "unix": + callback(null, `[${new Date().getTime()}] ${chunk}`); + break; + default: + throw new Error("invalid logger timestamp format"); + } + } else { + callback(null, `${chunk}`); + } + }, }); + + this.currentContainer.attach( + { stream: true, stdout: true, stderr: true }, + (err: any, stream: NodeJS.ReadWriteStream | undefined) => { + if (err != null) throw err; + if (stream == null) return; + + stream.pipe(timestampTransform).pipe(process.stdout); + }, + ); this.log(`[ZEMU] Attached ${this.currentContainer.id}`); } diff --git a/src/types.ts b/src/types.ts index 519299d..b287517 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,6 +43,13 @@ export type TModel = "nanos" | "nanosp" | "nanox" | "stax" | "flex"; export interface IStartOptions { logging: boolean; + logger?: { + enabled: boolean; + timestamp: { + enabled: boolean; + format: "unix" | "iso"; + }; + }; startDelay: number; custom: string; model: TModel;