feat: add console output file option for debugging and monitoring
Add comprehensive console logging to file functionality: - CLI option --console-output-file to specify output file path - Environment variable PLAYWRIGHT_MCP_CONSOLE_OUTPUT_FILE support - Session configuration via browser_configure_snapshots tool - Real-time structured logging with timestamp, session ID, and URL - Automatic directory creation and graceful error handling - Captures all console message types (log, error, warn, page errors) Useful for debugging browser interactions and monitoring console activity during automated sessions.
This commit is contained in:
parent
ec8b0c24b5
commit
7de63b5bab
7
config.d.ts
vendored
7
config.d.ts
vendored
@ -143,4 +143,11 @@ export type Config = {
|
||||
* Default is false.
|
||||
*/
|
||||
differentialSnapshots?: boolean;
|
||||
|
||||
/**
|
||||
* File path to write browser console output to. When specified, all console
|
||||
* messages from browser pages will be written to this file in real-time.
|
||||
* Useful for debugging and monitoring browser activity.
|
||||
*/
|
||||
consoleOutputFile?: string;
|
||||
};
|
||||
|
||||
@ -31,6 +31,7 @@ export type CLIOptions = {
|
||||
caps?: string[];
|
||||
cdpEndpoint?: string;
|
||||
config?: string;
|
||||
consoleOutputFile?: string;
|
||||
device?: string;
|
||||
executablePath?: string;
|
||||
headless?: boolean;
|
||||
@ -93,6 +94,7 @@ export type FullConfig = Config & {
|
||||
includeSnapshots: boolean;
|
||||
maxSnapshotTokens: number;
|
||||
differentialSnapshots: boolean;
|
||||
consoleOutputFile?: string;
|
||||
};
|
||||
|
||||
export async function resolveConfig(config: Config): Promise<FullConfig> {
|
||||
@ -212,6 +214,7 @@ export function configFromCLIOptions(cliOptions: CLIOptions): Config {
|
||||
includeSnapshots: cliOptions.includeSnapshots,
|
||||
maxSnapshotTokens: cliOptions.maxSnapshotTokens,
|
||||
differentialSnapshots: cliOptions.differentialSnapshots,
|
||||
consoleOutputFile: cliOptions.consoleOutputFile,
|
||||
};
|
||||
|
||||
return result;
|
||||
@ -238,6 +241,7 @@ function configFromEnv(): Config {
|
||||
options.includeSnapshots = envToBoolean(process.env.PLAYWRIGHT_MCP_INCLUDE_SNAPSHOTS);
|
||||
options.maxSnapshotTokens = envToNumber(process.env.PLAYWRIGHT_MCP_MAX_SNAPSHOT_TOKENS);
|
||||
options.differentialSnapshots = envToBoolean(process.env.PLAYWRIGHT_MCP_DIFFERENTIAL_SNAPSHOTS);
|
||||
options.consoleOutputFile = envToString(process.env.PLAYWRIGHT_MCP_CONSOLE_OUTPUT_FILE);
|
||||
options.sandbox = envToBoolean(process.env.PLAYWRIGHT_MCP_SANDBOX);
|
||||
options.outputDir = envToString(process.env.PLAYWRIGHT_MCP_OUTPUT_DIR);
|
||||
options.port = envToNumber(process.env.PLAYWRIGHT_MCP_PORT);
|
||||
|
||||
@ -641,6 +641,7 @@ export class Context {
|
||||
includeSnapshots?: boolean;
|
||||
maxSnapshotTokens?: number;
|
||||
differentialSnapshots?: boolean;
|
||||
consoleOutputFile?: string;
|
||||
}): void {
|
||||
// Update configuration at runtime
|
||||
if (updates.includeSnapshots !== undefined)
|
||||
@ -659,5 +660,9 @@ export class Context {
|
||||
this.resetDifferentialSnapshot();
|
||||
|
||||
}
|
||||
|
||||
if (updates.consoleOutputFile !== undefined)
|
||||
(this.config as any).consoleOutputFile = updates.consoleOutputFile === '' ? undefined : updates.consoleOutputFile;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,6 +38,7 @@ program
|
||||
.option('--caps <caps>', 'comma-separated list of additional capabilities to enable, possible values: vision, pdf.', commaSeparatedList)
|
||||
.option('--cdp-endpoint <endpoint>', 'CDP endpoint to connect to.')
|
||||
.option('--config <path>', 'path to the configuration file.')
|
||||
.option('--console-output-file <path>', 'file path to write browser console output to for debugging and monitoring.')
|
||||
.option('--device <device>', 'device to emulate, for example: "iPhone 15"')
|
||||
.option('--executable-path <path>', 'path to the browser executable.')
|
||||
.option('--headless', 'run browser in headless mode, headed by default')
|
||||
|
||||
35
src/tab.ts
35
src/tab.ts
@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
import { EventEmitter } from 'events';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import * as playwright from 'playwright';
|
||||
import { callOnPageNoTrace, waitForCompletion } from './tools/utils.js';
|
||||
import { logUnhandledError } from './log.js';
|
||||
@ -123,6 +125,39 @@ export class Tab extends EventEmitter<TabEventsInterface> {
|
||||
private _handleConsoleMessage(message: ConsoleMessage) {
|
||||
this._consoleMessages.push(message);
|
||||
this._recentConsoleMessages.push(message);
|
||||
|
||||
// Write to console output file if configured
|
||||
if (this.context.config.consoleOutputFile)
|
||||
this._writeConsoleToFile(message);
|
||||
|
||||
}
|
||||
|
||||
private _writeConsoleToFile(message: ConsoleMessage) {
|
||||
try {
|
||||
const consoleFile = this.context.config.consoleOutputFile!;
|
||||
const timestamp = new Date().toISOString();
|
||||
const url = this.page.url();
|
||||
const sessionId = this.context.sessionId;
|
||||
|
||||
const logEntry = `[${timestamp}] [${sessionId}] [${url}] ${message.toString()}\n`;
|
||||
|
||||
// Ensure directory exists
|
||||
const dir = path.dirname(consoleFile);
|
||||
if (!fs.existsSync(dir))
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
|
||||
|
||||
// Append to file (async to avoid blocking)
|
||||
fs.appendFile(consoleFile, logEntry, err => {
|
||||
if (err) {
|
||||
// Log error but don't fail the operation
|
||||
logUnhandledError(err);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// Silently handle errors to avoid breaking browser functionality
|
||||
logUnhandledError(error);
|
||||
}
|
||||
}
|
||||
|
||||
private _onClose() {
|
||||
|
||||
@ -77,7 +77,8 @@ const installPopularExtensionSchema = z.object({
|
||||
const configureSnapshotsSchema = z.object({
|
||||
includeSnapshots: z.boolean().optional().describe('Enable/disable automatic snapshots after interactive operations. When false, use browser_snapshot for explicit snapshots.'),
|
||||
maxSnapshotTokens: z.number().min(0).optional().describe('Maximum tokens allowed in snapshots before truncation. Use 0 to disable truncation.'),
|
||||
differentialSnapshots: z.boolean().optional().describe('Enable differential snapshots that show only changes since last snapshot instead of full page snapshots.')
|
||||
differentialSnapshots: z.boolean().optional().describe('Enable differential snapshots that show only changes since last snapshot instead of full page snapshots.'),
|
||||
consoleOutputFile: z.string().optional().describe('File path to write browser console output to. Set to empty string to disable console file output.')
|
||||
});
|
||||
|
||||
export default [
|
||||
@ -564,6 +565,14 @@ export default [
|
||||
|
||||
}
|
||||
|
||||
if (params.consoleOutputFile !== undefined) {
|
||||
if (params.consoleOutputFile === '')
|
||||
changes.push(`📝 Console output file: disabled`);
|
||||
else
|
||||
changes.push(`📝 Console output file: ${params.consoleOutputFile}`);
|
||||
|
||||
}
|
||||
|
||||
// Apply the updated configuration using the context method
|
||||
context.updateSnapshotConfig(params);
|
||||
|
||||
@ -572,7 +581,8 @@ export default [
|
||||
response.addResult('No snapshot configuration changes specified.\n\n**Current settings:**\n' +
|
||||
`📸 Auto-snapshots: ${context.config.includeSnapshots ? 'enabled' : 'disabled'}\n` +
|
||||
`📏 Max snapshot tokens: ${context.config.maxSnapshotTokens === 0 ? 'unlimited' : context.config.maxSnapshotTokens.toLocaleString()}\n` +
|
||||
`🔄 Differential snapshots: ${context.config.differentialSnapshots ? 'enabled' : 'disabled'}`);
|
||||
`🔄 Differential snapshots: ${context.config.differentialSnapshots ? 'enabled' : 'disabled'}\n` +
|
||||
`📝 Console output file: ${context.config.consoleOutputFile || 'disabled'}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user