Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ The `server` command supports the following optional arguments:
- `--login`: Automatically log the user in as an administrator.
- `--skip-wordpress-setup`: Do not download or install WordPress. Useful if you are mounting a full WordPress directory.
- `--skip-sqlite-setup`: Do not set up the SQLite database integration.
- `--quiet`: Do not output logs and progress messages.
- `--verbosity`: Output logs and progress messages. Defaults to 'normal'.
- `--debug`: Print the PHP error log if an error occurs during boot.

## Need some help with the CLI?
Expand Down
18 changes: 9 additions & 9 deletions packages/php-wasm/logger/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@
"lintFilePatterns": ["packages/php-wasm/logger/**/*.ts"],
"maxWarnings": 0
}
}
},
"typecheck": {
"executor": "nx:run-commands",
"options": {
"commands": [
"tsc -p packages/php-wasm/logger/tsconfig.lib.json --noEmit",
"tsc -p packages/php-wasm/logger/tsconfig.spec.json --noEmit"
]
},
"typecheck": {
"executor": "nx:run-commands",
"options": {
"commands": [
"tsc -p packages/php-wasm/logger/tsconfig.lib.json --noEmit",
"tsc -p packages/php-wasm/logger/tsconfig.spec.json --noEmit"
]
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { UniversalPHP, PHPRequestErrorEvent } from '../types';
import type { Logger } from '../logger';
import { type Logger, LogPrefix, LogSeverity } from '../logger';

let lastPHPLogLength = 0;
export const errorLogPath = '/wordpress/wp-content/debug.log';
Expand Down Expand Up @@ -32,6 +32,7 @@ export const collectPhpLogs = (
const currentLog = log.substring(lastPHPLogLength);
loggerInstance.logMessage({
message: currentLog,
severity: LogSeverity.Log,
raw: true,
});
lastPHPLogLength = log.length;
Expand All @@ -42,8 +43,9 @@ export const collectPhpLogs = (
if (event.error) {
loggerInstance.logMessage({
message: `${event.error.message} ${event.error.stack}`,
severity: 'Fatal',
prefix: event.source === 'request' ? 'PHP' : 'WASM Crash',
severity: LogSeverity.Fatal,
prefix:
event.source === 'request' ? LogPrefix.PHP : LogPrefix.WASM,
});
loggerInstance.dispatchEvent(
new CustomEvent(loggerInstance.fatalErrorEvent, {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Logger } from '../logger';
import { type Logger, LogSeverity } from '../logger';

/**
* Log Windows errors.
Expand All @@ -9,7 +9,7 @@ import type { Logger } from '../logger';
const logWindowErrorEvent = (loggerInstance: Logger, event: ErrorEvent) => {
loggerInstance.logMessage({
message: `${event.message} in ${event.filename} on line ${event.lineno}:${event.colno}`,
severity: 'Error',
severity: LogSeverity.Error,
});
};

Expand All @@ -30,7 +30,7 @@ const logPromiseRejection = (
const message = event?.reason.stack ?? event.reason;
loggerInstance.logMessage({
message,
severity: 'Error',
severity: LogSeverity.Error,
});
};

Expand Down
3 changes: 1 addition & 2 deletions packages/php-wasm/logger/src/lib/handlers/log-event.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { LogHandler } from '../log-handlers';
import type { Log } from '../logger';
import { logger } from '../logger';
import { type Log, logger } from '../logger';

export const logEventType = 'playground-log';

Expand Down
13 changes: 6 additions & 7 deletions packages/php-wasm/logger/src/lib/handlers/log-to-console.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { LogHandler } from '../log-handlers';
import type { Log } from '../logger';
import { prepareLogMessage } from '../logger';
import { type Log, LogSeverity, prepareLogMessage } from '../logger';

/**
* Log message to the console.
Expand All @@ -23,19 +22,19 @@ export const logToConsole: LogHandler = (log: Log, ...args: any[]): void => {
}
/* eslint-disable no-console */
switch (log.severity) {
case 'Debug':
case LogSeverity.Debug:
console.debug(log.message, ...args);
break;
case 'Info':
case LogSeverity.Info:
console.info(log.message, ...args);
break;
case 'Warn':
case LogSeverity.Warn:
console.warn(log.message, ...args);
break;
case 'Error':
case LogSeverity.Error:
console.error(log.message, ...args);
break;
case 'Fatal':
case LogSeverity.Fatal:
console.error(log.message, ...args);
break;
default:
Expand Down
7 changes: 3 additions & 4 deletions packages/php-wasm/logger/src/lib/handlers/log-to-memory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { LogHandler } from '../log-handlers';
import type { Log } from '../logger';
import { formatLogEntry } from '../logger';
import { formatLogEntry, type Log, LogPrefix } from '../logger';

const prepareLogMessage = (logMessage: object): string => {
if (logMessage instanceof Error) {
Expand All @@ -26,8 +25,8 @@ export const logToMemory: LogHandler = (log: Log): void => {
typeof log.message === 'object'
? prepareLogMessage(log.message)
: log.message,
log.severity ?? 'Info',
log.prefix ?? 'JavaScript'
log.severity,
log.prefix ?? LogPrefix.JS
);
addToLogArray(message);
}
Expand Down
83 changes: 64 additions & 19 deletions packages/php-wasm/logger/src/lib/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,54 @@ export { errorLogPath } from './collectors/collect-php-logs';

export type Log = {
message: any;
severity?: LogSeverity;
severity: LogSeverity;
prefix?: LogPrefix;
raw?: boolean;
};

/**
* Log verbosity levels
*/
export const LogVerbosity = {
Normal: 'normal',
Quiet: 'quiet',
Debug: 'debug',
} as const;

export type LogVerbosity = (typeof LogVerbosity)[keyof typeof LogVerbosity];

/**
* Log severity levels.
*/
export type LogSeverity = 'Debug' | 'Info' | 'Warn' | 'Error' | 'Fatal';
export const LogSeverity = {
Log: { name: 'log', level: 1 },
Info: { name: 'info', level: 1 },
Warn: { name: 'warn', level: 1 },
Error: { name: 'error', level: 1 },
Fatal: { name: 'fatal', level: 1 },
Debug: { name: 'debug', level: 2 },
} as const;

export type LogSeverity = (typeof LogSeverity)[keyof typeof LogSeverity];

/**
* Log prefix.
*/
export type LogPrefix = 'WASM Crash' | 'PHP' | 'JavaScript';
export const LogPrefix = {
WASM: 'Wasm Crash',
PHP: 'PHP',
JS: 'JavaScript',
} as const;

export type LogPrefix = (typeof LogPrefix)[keyof typeof LogPrefix];

/**
* A logger for Playground.
*/
export class Logger extends EventTarget {
public readonly fatalErrorEvent = 'playground-fatal-error';
private readonly handlers: LogHandler[];
private handlers: LogHandler[];
private verbosity = 1;

// constructor
constructor(
Expand Down Expand Up @@ -62,14 +89,32 @@ export class Logger extends EventTarget {
/**
* Log message with severity.
*
* @param message any
* @param severity LogSeverity
* @param raw boolean
* @param log Log
* @param args any
*/
public logMessage(log: Log, ...args: any[]): void {
for (const handler of this.handlers) {
handler(log, ...args);
if (log.severity.level <= this.verbosity) {
handler(log, ...args);
}
}
}

/**
* Filter message based on verbosiy
* @param verbosity LogVerbosity
*/
public filterByVerbosity(verbosity: LogVerbosity): void {
switch (verbosity) {
case LogVerbosity.Quiet:
this.verbosity = 0;
break;
case LogVerbosity.Normal:
this.verbosity = 1;
break;
case LogVerbosity.Debug:
this.verbosity = 2;
break;
}
}

Expand All @@ -83,8 +128,8 @@ export class Logger extends EventTarget {
this.logMessage(
{
message,
severity: undefined,
prefix: 'JavaScript',
severity: LogSeverity.Log,
prefix: LogPrefix.JS,
raw: false,
},
...args
Expand All @@ -101,8 +146,8 @@ export class Logger extends EventTarget {
this.logMessage(
{
message,
severity: 'Debug',
prefix: 'JavaScript',
severity: LogSeverity.Debug,
prefix: LogPrefix.JS,
raw: false,
},
...args
Expand All @@ -119,8 +164,8 @@ export class Logger extends EventTarget {
this.logMessage(
{
message,
severity: 'Info',
prefix: 'JavaScript',
severity: LogSeverity.Info,
prefix: LogPrefix.JS,
raw: false,
},
...args
Expand All @@ -137,8 +182,8 @@ export class Logger extends EventTarget {
this.logMessage(
{
message,
severity: 'Warn',
prefix: 'JavaScript',
severity: LogSeverity.Warn,
prefix: LogPrefix.JS,
raw: false,
},
...args
Expand All @@ -155,8 +200,8 @@ export class Logger extends EventTarget {
this.logMessage(
{
message,
severity: 'Error',
prefix: 'JavaScript',
severity: LogSeverity.Error,
prefix: LogPrefix.JS,
raw: false,
},
...args
Expand Down Expand Up @@ -209,7 +254,7 @@ export const formatLogEntry = (
}).format(date);
const now = formattedDate + ' ' + formattedTime;
message = prepareLogMessage(message);
return `[${now}] ${prefix} ${severity}: ${message}`;
return `[${now}] ${prefix} ${severity.name}: ${message}`;
};

/**
Expand Down
75 changes: 69 additions & 6 deletions packages/php-wasm/logger/src/test/logger.spec.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,87 @@
import { logger } from '../lib/logger';
import { clearMemoryLogs } from '../lib/log-handlers';
import { type Log, logger, LogVerbosity } from '../lib/logger';
import { clearMemoryLogs, type LogHandler } from '../lib/log-handlers';

describe('Logger', () => {
beforeEach(async () => {
let output: string[];
let handlers: LogHandler[];

function logToVariable(log: Log, arg?: string) {
output.push(`${log.message}${arg ? arg : ''}`);
}

beforeAll(() => {
// @ts-ignore
handlers = logger.handlers;
});

beforeEach(() => {
output = [];
// @ts-ignore
logger.handlers = [...handlers, logToVariable];

clearMemoryLogs();
});

it('Log message should be added', () => {
it('adds message in logs', () => {
logger.warn('test');
const logs = logger.getLogs();
expect(logs.length).toBe(1);
expect(logs[0]).toMatch(
/\[\d{2}-[A-Za-z]{3,4}-\d{4} \d{2}:\d{2}:\d{2} UTC\] JavaScript Warn: test/
/\[\d{2}-[A-Za-z]{3,4}-\d{4} \d{2}:\d{2}:\d{2} UTC\] JavaScript warn: test/
);
});

it('Log event should be dispatched', () => {
it('dispatches log event', () => {
const eventListener = vitest.fn();
logger.addEventListener('playground-log', eventListener);
logger.warn('test');
expect(eventListener).toHaveBeenCalled();
});

it('outputs main logs by default', () => {
logger.log('log');
logger.info('info');
logger.warn('warn');
logger.error('error');
logger.debug('debug');
const logs = logger.getLogs();
expect(logs.length).toBe(4);
expect(output).toEqual(['log', 'info', 'warn', 'error']);
});

it('outputs main logs when verbosity is set to normal', () => {
logger.filterByVerbosity(LogVerbosity.Normal);
logger.log('log');
logger.info('info');
logger.warn('warn');
logger.error('error');
logger.debug('debug');
const logs = logger.getLogs();
expect(logs.length).toBe(4);
expect(output).toEqual(['log', 'info', 'warn', 'error']);
});

it('outputs main and debug logs when verbosity is set to debug', () => {
logger.filterByVerbosity(LogVerbosity.Debug);
logger.log('log');
logger.info('info');
logger.warn('warn');
logger.error('error');
logger.debug('debug');
const logs = logger.getLogs();
expect(logs.length).toBe(5);
expect(output).toEqual(['log', 'info', 'warn', 'error', 'debug']);
});

it('does not output logs when verbosity is set to quiet', () => {
logger.filterByVerbosity(LogVerbosity.Quiet);
logger.log('log');
logger.info('info');
logger.warn('warn');
logger.error('error');
logger.debug('debug');
const logs = logger.getLogs();
expect(logs.length).toBe(0);
expect(output).toEqual([]);
});
});
Loading
Loading