From 6b215166a960ec8912f142cf8a1f5c7b33cc6d54 Mon Sep 17 00:00:00 2001 From: Bero Date: Tue, 13 Feb 2024 14:00:17 +0100 Subject: [PATCH 01/30] Basic error logging --- .../php-wasm/universal/src/lib/base-php.ts | 36 ++++-- packages/php-wasm/universal/src/lib/logger.ts | 121 ++++++++++++++++++ .../lib/playground-mu-plugin/0-playground.php | 72 +++++++++-- 3 files changed, 204 insertions(+), 25 deletions(-) create mode 100644 packages/php-wasm/universal/src/lib/logger.ts diff --git a/packages/php-wasm/universal/src/lib/base-php.ts b/packages/php-wasm/universal/src/lib/base-php.ts index c45b910800..f76c1ab925 100644 --- a/packages/php-wasm/universal/src/lib/base-php.ts +++ b/packages/php-wasm/universal/src/lib/base-php.ts @@ -28,6 +28,7 @@ import { UnhandledRejectionsTarget, } from './wasm-error-reporting'; import { Semaphore, createSpawnHandler, joinPaths } from '@php-wasm/util'; +import { LogSeverity, get_logger } from './logger'; const STRING = 'string'; const NUMBER = 'number'; @@ -264,21 +265,28 @@ export abstract class BasePHP implements IsomorphicLocalPHP { this.#setPHPCode(' ?>' + request.code); } this.#addServerGlobalEntriesInWasm(); - const response = await this.#handleRequest(); - if (request.throwOnError && response.exitCode !== 0) { - const output = { - stdout: response.text, - stderr: response.errors, - }; - console.warn(`PHP.run() output was:`, output); - const error = new Error( - `PHP.run() failed with exit code ${response.exitCode} and the following output` + try { + const response = await this.#handleRequest(); + if (request.throwOnError && response.exitCode !== 0) { + const output = { + stdout: response.text, + stderr: response.errors, + }; + console.warn(`PHP.run() output was:`, output); + const error = new Error( + `PHP.run() failed with exit code ${response.exitCode} and the following output` + ); + // @ts-ignore + error.output = output; + throw error; + } + return response; + } catch (e: any) { + get_logger(this).log( + e.message, + LogSeverity.Error, ); - // @ts-ignore - error.output = output; - throw error; } - return response; } finally { try { if (heapBodyPointer) { @@ -770,6 +778,7 @@ export abstract class BasePHP implements IsomorphicLocalPHP { // runtime. const oldFS = this[__private__dont__use].FS; + console.log('PHP runtime exited'); // Kill the current runtime try { this.exit(); @@ -799,6 +808,7 @@ export abstract class BasePHP implements IsomorphicLocalPHP { } exit(code = 0) { + console.log('PHP exit()'); this.dispatchEvent({ type: 'runtime.beforedestroy', }); diff --git a/packages/php-wasm/universal/src/lib/logger.ts b/packages/php-wasm/universal/src/lib/logger.ts new file mode 100644 index 0000000000..eb3cdbef57 --- /dev/null +++ b/packages/php-wasm/universal/src/lib/logger.ts @@ -0,0 +1,121 @@ +import { UniversalPHP } from "./universal-php"; + +export enum LogSeverity { + // A dine grained debugging event. Typically disabled in default configurations. + Trace = 1, + // A debugging event. + Debug = 5, + // An informational event. Indicates that an event happened. + Info = 9, + // A warning event. Not an error but is likely more important than an informational event. + Warn = 13, + // An error event. Something went wrong. + Error = 17, + // A fatal error such as application or system crash. + Fatal = 21, +} + +export type Log = { + // Time when the event occurred. + timestamp: number; + // Time when the event was observed. + observedTimestamp: number; + // Request trace id. + traceId: number; + // Request span id. + spanId: number; // Not implemented + // W3C trace flag. + traceFlags: number; + // The severity text (also known as log level). + severityText: string; + // Numerical value of the severity. + severityNumber: LogSeverity; + // The body of the log record. + body: string; + // Describes the source of the log. + resource: string; + // Describes the scope that emitted the log. + instrumentationScope: string; // Not implemented + // Additional information about the event. + attributes: Record; +}; + +export class Logger { + private php: UniversalPHP | undefined; + private logs: Log[] = []; + private wordpressLogs: string[] | undefined = undefined; + + constructor(php?: UniversalPHP) { + this.php = php; + if (this.php) { + this.php.addEventListener( + 'request.end', + () => { + console.log(this.getLogs()); + } + ) + this.php.onMessage((message) => { + try { + const logMessage = JSON.parse(message); + if (logMessage.type === 'wordpress-log') { + this.log( + logMessage.body, + logMessage.severityNumber, + logMessage.timestamp, + 'wordpress', + ); + } + } catch (error) { + // Not a log message + } + }); + } + if ( typeof window !== 'undefined' ) { + window.onerror = (message, source, lineno, colno) => { + this.log( + `${message} in ${source} on line ${lineno}:${colno}`, + LogSeverity.Fatal, + Date.now(), + 'javascript', + ); + } + } + } + + public log(body: string, severityNumber: LogSeverity = LogSeverity.Info, timestamp?: number, resource = 'php', instrumentationScope = 'php', attributes: Record = {}): void { + const now = Date.now(); + this.logs.push({ + timestamp: timestamp || now, + observedTimestamp: now, + traceId: 1, // @TODO: Implement trace id + spanId: 1, // @TODO: Implement span id + traceFlags: 1, + severityText: LogSeverity[severityNumber], + severityNumber, + body, + resource, + instrumentationScope, + attributes, + }); + } + + public getLogs(): Log[] { + return this.logs; + } + + public async addWordPressLogs() { + if (this.php && this.wordpressLogs === undefined) { + const rawLogs = await this.php.readFileAsText('/tmp/debug.log'); + } + } + +}; + +let logger: Logger | undefined = undefined; + +export function get_logger(php?: BasePHP): Logger { + if (!logger) { + logger = new Logger(php); + } + return logger; +} \ No newline at end of file diff --git a/packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php b/packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php index 8a8619dec3..9ca70bfbd6 100644 --- a/packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php +++ b/packages/playground/remote/src/lib/playground-mu-plugin/0-playground.php @@ -1,7 +1,7 @@ @@ -29,19 +29,19 @@ function () { ); /** - * Because the in-browser Playground doesn't have access to the internet, - * network-dependent features like directories don't work. Normally, you'll + * Because the in-browser Playground doesn't have access to the internet, + * network-dependent features like directories don't work. Normally, you'll * see a confusing message like "An unexpected error occurred." This mu-plugin * makes it more clear that the feature is not yet supported. - * + * * https://github.com/WordPress/wordpress-playground/issues/498 - * + * * Added styling to hide the Popular tags section of the Plugins page * and the nonfunctional Try Again button (both Plugins and Themes) that's * appended when the message is displayed. - * + * * https://github.com/WordPress/wordpress-playground/issues/927 - * + * */ add_action('admin_head', function () { echo '