diff --git a/docs/api.md b/docs/api.md index 4b79efc9b2..54a4305028 100644 --- a/docs/api.md +++ b/docs/api.md @@ -27,7 +27,7 @@ - [class: Worker](#class-worker) - [class: BrowserServer](#class-browserserver) - [class: BrowserType](#class-browsertype) -- [class: LoggerSink](#class-loggersink) +- [class: Logger](#class-logger) - [class: ChromiumBrowser](#class-chromiumbrowser) - [class: ChromiumBrowserContext](#class-chromiumbrowsercontext) - [class: ChromiumCoverage](#class-chromiumcoverage) @@ -218,6 +218,7 @@ Indicates that the browser is connected. - `username` <[string]> - `password` <[string]> - `colorScheme` <"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. + - `logger` <[Logger]> Logger sink for Playwright logging. - returns: <[Promise]<[BrowserContext]>> Creates a new browser context. It won't share cookies/cache with other browser contexts. @@ -259,6 +260,7 @@ Creates a new browser context. It won't share cookies/cache with other browser c - `username` <[string]> - `password` <[string]> - `colorScheme` <"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See [page.emulateMedia(options)](#pageemulatemediaoptions) for more details. Defaults to '`light`'. + - `logger` <[Logger]> Logger sink for Playwright logging. - returns: <[Promise]<[Page]>> Creates a new page in a new browser context. Closing this page will close the context as well. @@ -3757,7 +3759,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. - `options` <[Object]> - `wsEndpoint` <[string]> A browser websocket endpoint to connect to. **required** - `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. - - `loggerSink` <[LoggerSink]> Sink for log messages. + - `logger` <[Logger]> Logger sink for Playwright logging. - returns: <[Promise]<[Browser]>> This methods attaches Playwright to an existing browser instance. @@ -3774,7 +3776,7 @@ This methods attaches Playwright to an existing browser instance. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. - `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`. - `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`. - - `loggerSink` <[LoggerSink]> Sink for log messages. + - `logger` <[Logger]> Logger sink for Playwright logging. - `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. @@ -3807,7 +3809,7 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. - `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`. - `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`. - - `loggerSink` <[LoggerSink]> Sink for log messages. + - `logger` <[Logger]> Logger sink for Playwright logging. - `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. @@ -3826,7 +3828,7 @@ Launches browser instance that uses persistent storage located at `userDataDir`. - `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`. - `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`. - `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`. - - `loggerSink` <[LoggerSink]> Sink for log messages. + - `logger` <[Logger]> Logger sink for Playwright logging. - `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. - `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`. - `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`. @@ -3853,7 +3855,7 @@ const { chromium } = require('playwright'); // Or 'webkit' or 'firefox'. Returns browser name. For example: `'chromium'`, `'webkit'` or `'firefox'`. -### class: LoggerSink +### class: Logger Playwright generates a lot of logs and they are accessible via the pluggable logger sink. @@ -3862,7 +3864,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. (async () => { const browser = await chromium.launch({ - loggerSink: { + logger: { isEnabled: (name, severity) => name === 'browser', log: (name, severity, message, args) => console.log(`${name} ${message}`) } @@ -3872,18 +3874,18 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'. ``` -- [loggerSink.isEnabled(name, severity)](#loggersinkisenabledname-severity) -- [loggerSink.log(name, severity, message, args, hints)](#loggersinklogname-severity-message-args-hints) +- [logger.isEnabled(name, severity)](#loggerisenabledname-severity) +- [logger.log(name, severity, message, args, hints)](#loggerlogname-severity-message-args-hints) -#### loggerSink.isEnabled(name, severity) +#### logger.isEnabled(name, severity) - `name` <[string]> logger name - `severity` <"verbose"|"info"|"warning"|"error"> - returns: <[boolean]> Determines whether sink is interested in the logger with the given name and severity. -#### loggerSink.log(name, severity, message, args, hints) +#### logger.log(name, severity, message, args, hints) - `name` <[string]> logger name - `severity` <"verbose"|"info"|"warning"|"error"> - `message` <[string]|[Error]> log message format @@ -4247,7 +4249,7 @@ const { chromium } = require('playwright'); [Frame]: #class-frame "Frame" [JSHandle]: #class-jshandle "JSHandle" [Keyboard]: #class-keyboard "Keyboard" -[LoggerSink]: #class-loggersink "LoggerSink" +[Logger]: #class-logger "Logger" [Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map" [Mouse]: #class-mouse "Mouse" [Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object" diff --git a/src/api.ts b/src/api.ts index e6620ea3ff..22c2b79d52 100644 --- a/src/api.ts +++ b/src/api.ts @@ -22,7 +22,7 @@ export { Dialog } from './dialog'; export { Download } from './download'; export { ElementHandle } from './dom'; export { FileChooser } from './fileChooser'; -export { LoggerSink } from './logger'; +export { Logger } from './logger'; export { TimeoutError } from './errors'; export { Frame } from './frames'; export { Keyboard, Mouse } from './input'; diff --git a/src/browser.ts b/src/browser.ts index cd3b2d4077..295826cac1 100644 --- a/src/browser.ts +++ b/src/browser.ts @@ -20,7 +20,7 @@ import { EventEmitter } from 'events'; import { Download } from './download'; import type { BrowserServer } from './server/browserServer'; import { Events } from './events'; -import { Logger, Log } from './logger'; +import { InnerLogger, Log } from './logger'; export interface Browser extends EventEmitter { newContext(options?: BrowserContextOptions): Promise; @@ -30,13 +30,13 @@ export interface Browser extends EventEmitter { close(): Promise; } -export abstract class BrowserBase extends EventEmitter implements Browser, Logger { +export abstract class BrowserBase extends EventEmitter implements Browser, InnerLogger { _downloadsPath: string = ''; private _downloads = new Map(); _ownedServer: BrowserServer | null = null; - readonly _logger: Logger; + readonly _logger: InnerLogger; - constructor(logger: Logger) { + constructor(logger: InnerLogger) { super(); this._logger = logger; } diff --git a/src/browserContext.ts b/src/browserContext.ts index de583e147c..e1b86a87df 100644 --- a/src/browserContext.ts +++ b/src/browserContext.ts @@ -24,7 +24,7 @@ import { Events } from './events'; import { ExtendedEventEmitter } from './extendedEventEmitter'; import { Download } from './download'; import { BrowserBase } from './browser'; -import { Log, Logger } from './logger'; +import { Log, InnerLogger, Logger, RootLogger } from './logger'; export type BrowserContextOptions = { viewport?: types.Size | null, @@ -43,10 +43,11 @@ export type BrowserContextOptions = { isMobile?: boolean, hasTouch?: boolean, colorScheme?: types.ColorScheme, - acceptDownloads?: boolean + acceptDownloads?: boolean, + logger?: Logger, }; -export interface BrowserContext extends Logger { +export interface BrowserContext extends InnerLogger { setDefaultNavigationTimeout(timeout: number): void; setDefaultTimeout(timeout: number): void; pages(): Page[]; @@ -79,11 +80,13 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements readonly _permissions = new Map(); readonly _downloads = new Set(); readonly _browserBase: BrowserBase; + private _logger: InnerLogger; constructor(browserBase: BrowserBase, options: BrowserContextOptions) { super(); this._browserBase = browserBase; this._options = options; + this._logger = options.logger ? new RootLogger(options.logger) : browserBase; this._closePromise = new Promise(fulfill => this._closePromiseFulfill = fulfill); } @@ -155,11 +158,11 @@ export abstract class BrowserContextBase extends ExtendedEventEmitter implements } _isLogEnabled(log: Log): boolean { - return this._browserBase._isLogEnabled(log); + return this._logger._isLogEnabled(log); } _log(log: Log, message: string | Error, ...args: any[]) { - return this._browserBase._log(log, message, ...args); + return this._logger._log(log, message, ...args); } } diff --git a/src/chromium/crBrowser.ts b/src/chromium/crBrowser.ts index 17764723ea..3b78e79eac 100644 --- a/src/chromium/crBrowser.ts +++ b/src/chromium/crBrowser.ts @@ -29,7 +29,7 @@ import { readProtocolStream } from './crProtocolHelper'; import { Events } from './events'; import { Protocol } from './protocol'; import { CRExecutionContext } from './crExecutionContext'; -import { Logger, logError } from '../logger'; +import { InnerLogger, logError } from '../logger'; export class CRBrowser extends BrowserBase { readonly _connection: CRConnection; @@ -47,7 +47,7 @@ export class CRBrowser extends BrowserBase { private _tracingPath: string | null = ''; private _tracingClient: CRSession | undefined; - static async connect(transport: ConnectionTransport, isPersistent: boolean, logger: Logger, slowMo?: number): Promise { + static async connect(transport: ConnectionTransport, isPersistent: boolean, logger: InnerLogger, slowMo?: number): Promise { const connection = new CRConnection(SlowMoTransport.wrap(transport, slowMo), logger); const browser = new CRBrowser(connection, logger, isPersistent); const session = connection.rootSession; @@ -84,7 +84,7 @@ export class CRBrowser extends BrowserBase { return browser; } - constructor(connection: CRConnection, logger: Logger, isPersistent: boolean) { + constructor(connection: CRConnection, logger: InnerLogger, isPersistent: boolean) { super(logger); this._connection = connection; this._session = this._connection.rootSession; diff --git a/src/chromium/crConnection.ts b/src/chromium/crConnection.ts index 0ac5f62c00..023976d0b9 100644 --- a/src/chromium/crConnection.ts +++ b/src/chromium/crConnection.ts @@ -19,7 +19,7 @@ import { assert } from '../helper'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport'; import { Protocol } from './protocol'; import { EventEmitter } from 'events'; -import { Logger } from '../logger'; +import { InnerLogger } from '../logger'; export const ConnectionEvents = { Disconnected: Symbol('ConnectionEvents.Disconnected') @@ -35,9 +35,9 @@ export class CRConnection extends EventEmitter { private readonly _sessions = new Map(); readonly rootSession: CRSession; _closed = false; - private _logger: Logger; + private _logger: InnerLogger; - constructor(transport: ConnectionTransport, logger: Logger) { + constructor(transport: ConnectionTransport, logger: InnerLogger) { super(); this._transport = transport; this._logger = logger; diff --git a/src/chromium/crCoverage.ts b/src/chromium/crCoverage.ts index 37ae9bf9b3..aea6d16661 100644 --- a/src/chromium/crCoverage.ts +++ b/src/chromium/crCoverage.ts @@ -21,7 +21,7 @@ import { Protocol } from './protocol'; import { EVALUATION_SCRIPT_URL } from './crExecutionContext'; import * as types from '../types'; -import { logError, Logger } from '../logger'; +import { logError, InnerLogger } from '../logger'; type JSRange = { startOffset: number, @@ -51,7 +51,7 @@ export class CRCoverage { private _jsCoverage: JSCoverage; private _cssCoverage: CSSCoverage; - constructor(client: CRSession, logger: Logger) { + constructor(client: CRSession, logger: InnerLogger) { this._jsCoverage = new JSCoverage(client, logger); this._cssCoverage = new CSSCoverage(client, logger); } @@ -81,9 +81,9 @@ class JSCoverage { _eventListeners: RegisteredListener[]; _resetOnNavigation: boolean; _reportAnonymousScripts = false; - private _logger: Logger; + private _logger: InnerLogger; - constructor(client: CRSession, logger: Logger) { + constructor(client: CRSession, logger: InnerLogger) { this._client = client; this._logger = logger; this._enabled = false; @@ -175,9 +175,9 @@ class CSSCoverage { _stylesheetSources: Map; _eventListeners: RegisteredListener[]; _resetOnNavigation: boolean; - private _logger: Logger; + private _logger: InnerLogger; - constructor(client: CRSession, logger: Logger) { + constructor(client: CRSession, logger: InnerLogger) { this._client = client; this._logger = logger; this._enabled = false; diff --git a/src/firefox/ffBrowser.ts b/src/firefox/ffBrowser.ts index 54036adf42..3f06f3e12c 100644 --- a/src/firefox/ffBrowser.ts +++ b/src/firefox/ffBrowser.ts @@ -27,7 +27,7 @@ import { ConnectionEvents, FFConnection } from './ffConnection'; import { headersArray } from './ffNetworkManager'; import { FFPage } from './ffPage'; import { Protocol } from './protocol'; -import { Logger } from '../logger'; +import { InnerLogger } from '../logger'; export class FFBrowser extends BrowserBase { _connection: FFConnection; @@ -38,14 +38,14 @@ export class FFBrowser extends BrowserBase { readonly _firstPagePromise: Promise; private _firstPageCallback = () => {}; - static async connect(transport: ConnectionTransport, logger: Logger, attachToDefaultContext: boolean, slowMo?: number): Promise { + static async connect(transport: ConnectionTransport, logger: InnerLogger, attachToDefaultContext: boolean, slowMo?: number): Promise { const connection = new FFConnection(SlowMoTransport.wrap(transport, slowMo), logger); const browser = new FFBrowser(connection, logger, attachToDefaultContext); await connection.send('Browser.enable', { attachToDefaultContext }); return browser; } - constructor(connection: FFConnection, logger: Logger, isPersistent: boolean) { + constructor(connection: FFConnection, logger: InnerLogger, isPersistent: boolean) { super(logger); this._connection = connection; this._ffPages = new Map(); diff --git a/src/firefox/ffConnection.ts b/src/firefox/ffConnection.ts index 1fd84407e0..a42587e25c 100644 --- a/src/firefox/ffConnection.ts +++ b/src/firefox/ffConnection.ts @@ -19,7 +19,7 @@ import { EventEmitter } from 'events'; import { assert } from '../helper'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport'; import { Protocol } from './protocol'; -import { Logger } from '../logger'; +import { InnerLogger } from '../logger'; export const ConnectionEvents = { Disconnected: Symbol('Disconnected'), @@ -33,7 +33,7 @@ export class FFConnection extends EventEmitter { private _lastId: number; private _callbacks: Map; private _transport: ConnectionTransport; - private _logger: Logger; + private _logger: InnerLogger; readonly _sessions: Map; _closed: boolean; @@ -43,7 +43,7 @@ export class FFConnection extends EventEmitter { removeListener: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; once: (event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this; - constructor(transport: ConnectionTransport, logger: Logger) { + constructor(transport: ConnectionTransport, logger: InnerLogger) { super(); this._transport = transport; this._logger = logger; diff --git a/src/javascript.ts b/src/javascript.ts index 471e96b31d..7f70cca24b 100644 --- a/src/javascript.ts +++ b/src/javascript.ts @@ -17,7 +17,7 @@ import * as types from './types'; import * as dom from './dom'; import { helper } from './helper'; -import { Logger } from './logger'; +import { InnerLogger } from './logger'; export interface ExecutionContextDelegate { evaluate(context: ExecutionContext, returnByValue: boolean, pageFunction: string | Function, ...args: any[]): Promise; @@ -29,9 +29,9 @@ export interface ExecutionContextDelegate { export class ExecutionContext { readonly _delegate: ExecutionContextDelegate; - readonly _logger: Logger; + readonly _logger: InnerLogger; - constructor(delegate: ExecutionContextDelegate, logger: Logger) { + constructor(delegate: ExecutionContextDelegate, logger: InnerLogger) { this._delegate = delegate; this._logger = logger; } diff --git a/src/logger.ts b/src/logger.ts index eecfdf4f85..75d4bf0a6d 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -25,26 +25,26 @@ export type Log = { }; export interface Logger { - _isLogEnabled(log: Log): boolean; - _log(log: Log, message: string | Error, ...args: any[]): void; -} - -export interface LoggerSink { isEnabled(name: string, severity: LoggerSeverity): boolean; log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }): void; } +export interface InnerLogger { + _isLogEnabled(log: Log): boolean; + _log(log: Log, message: string | Error, ...args: any[]): void; +} + export const errorLog: Log = { name: 'generic', severity: 'error' }; -export function logError(logger: Logger): (error: Error) => void { +export function logError(logger: InnerLogger): (error: Error) => void { return error => logger._log(errorLog, error, []); } -export class RootLogger implements Logger { - private _userSink: LoggerSink | undefined; +export class RootLogger implements InnerLogger { + private _userSink: Logger | undefined; private _debugSink: DebugLoggerSink; - constructor(userSink: LoggerSink | undefined) { + constructor(userSink: Logger | undefined) { this._userSink = userSink; this._debugSink = new DebugLoggerSink(); } @@ -72,17 +72,17 @@ const colorMap = new Map([ ['reset', 0], ]); -class DebugLoggerSink implements LoggerSink { +class DebugLoggerSink implements Logger { private _debuggers = new Map(); isEnabled(name: string, severity: LoggerSeverity): boolean { - return debug.enabled('pw:' + name); + return debug.enabled(`pw:${name}`); } log(name: string, severity: LoggerSeverity, message: string | Error, args: any[], hints: { color?: string }) { let cachedDebugger = this._debuggers.get(name); if (!cachedDebugger) { - cachedDebugger = debug('pw:' + name); + cachedDebugger = debug(`pw:${name}`); this._debuggers.set(name, cachedDebugger); let color = hints.color || 'reset'; diff --git a/src/page.ts b/src/page.ts index d259b12983..2bcb932d4d 100644 --- a/src/page.ts +++ b/src/page.ts @@ -31,7 +31,7 @@ import * as accessibility from './accessibility'; import { ExtendedEventEmitter } from './extendedEventEmitter'; import { EventEmitter } from 'events'; import { FileChooser } from './fileChooser'; -import { logError, Logger, Log } from './logger'; +import { logError, InnerLogger, Log } from './logger'; export interface PageDelegate { readonly rawMouse: input.RawMouse; @@ -87,7 +87,7 @@ type PageState = { extraHTTPHeaders: network.Headers | null; }; -export class Page extends ExtendedEventEmitter implements Logger { +export class Page extends ExtendedEventEmitter implements InnerLogger { private _closed = false; private _closedCallback: () => void; private _closedPromise: Promise; @@ -537,13 +537,13 @@ export class Page extends ExtendedEventEmitter implements Logger { } export class Worker extends EventEmitter { - private _logger: Logger; + private _logger: InnerLogger; private _url: string; private _executionContextPromise: Promise; private _executionContextCallback: (value?: js.ExecutionContext) => void; _existingExecutionContext: js.ExecutionContext | null = null; - constructor(logger: Logger, url: string) { + constructor(logger: InnerLogger, url: string) { super(); this._logger = logger; this._url = url; diff --git a/src/server/browserType.ts b/src/server/browserType.ts index 095582edbb..7f68a9422e 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -16,7 +16,7 @@ import { BrowserContext } from '../browserContext'; import { BrowserServer } from './browserServer'; -import { LoggerSink } from '../logger'; +import { Logger } from '../logger'; export type BrowserArgOptions = { headless?: boolean, @@ -31,14 +31,14 @@ type LaunchOptionsBase = BrowserArgOptions & { handleSIGTERM?: boolean, handleSIGHUP?: boolean, timeout?: number, - loggerSink?: LoggerSink, + logger?: Logger, env?: {[key: string]: string} | undefined }; export type ConnectOptions = { wsEndpoint: string, slowMo?: number, - loggerSink?: LoggerSink, + logger?: Logger, }; export type LaunchOptions = LaunchOptionsBase & { slowMo?: number }; export type LaunchServerOptions = LaunchOptionsBase & { port?: number }; diff --git a/src/server/chromium.ts b/src/server/chromium.ts index 25d4f15cc3..716589969e 100644 --- a/src/server/chromium.ts +++ b/src/server/chromium.ts @@ -31,7 +31,7 @@ import { BrowserServer, WebSocketWrapper } from './browserServer'; import { Events } from '../events'; import { ConnectionTransport, ProtocolRequest, WebSocketTransport } from '../transport'; import { BrowserContext } from '../browserContext'; -import { Logger, logError, RootLogger } from '../logger'; +import { InnerLogger, logError, RootLogger } from '../logger'; export class Chromium implements BrowserType { private _executablePath: (string|undefined); @@ -71,7 +71,7 @@ export class Chromium implements BrowserType { return browser._defaultContext!; } - private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport, downloadsPath: string, logger: Logger }> { + private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport, downloadsPath: string, logger: InnerLogger }> { const { ignoreDefaultArgs = false, args = [], @@ -83,7 +83,7 @@ export class Chromium implements BrowserType { port = 0, } = options; assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.'); - const logger = new RootLogger(options.loggerSink); + const logger = new RootLogger(options.logger); let temporaryUserDataDir: string | null = null; if (!userDataDir) { @@ -137,7 +137,7 @@ export class Chromium implements BrowserType { async connect(options: ConnectOptions): Promise { return await WebSocketTransport.connect(options.wsEndpoint, transport => { - return CRBrowser.connect(transport, false, new RootLogger(options.loggerSink), options.slowMo); + return CRBrowser.connect(transport, false, new RootLogger(options.logger), options.slowMo); }); } @@ -179,7 +179,7 @@ export class Chromium implements BrowserType { } } -function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: Logger, port: number): WebSocketWrapper { +function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: InnerLogger, port: number): WebSocketWrapper { const server = new ws.Server({ port }); const guid = helper.guid(); diff --git a/src/server/firefox.ts b/src/server/firefox.ts index 64f5dcdff4..168d6671b4 100644 --- a/src/server/firefox.ts +++ b/src/server/firefox.ts @@ -31,7 +31,7 @@ import { BrowserServer, WebSocketWrapper } from './browserServer'; import { BrowserArgOptions, BrowserType, LaunchOptions, LaunchServerOptions, ConnectOptions } from './browserType'; import { launchProcess, waitForLine } from './processLauncher'; import { ConnectionTransport, SequenceNumberMixer, WebSocketTransport } from '../transport'; -import { RootLogger, Logger, logError } from '../logger'; +import { RootLogger, InnerLogger, logError } from '../logger'; const mkdtempAsync = util.promisify(fs.mkdtemp); @@ -79,7 +79,7 @@ export class Firefox implements BrowserType { return browserContext; } - private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, downloadsPath: string, logger: Logger }> { + private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, downloadsPath: string, logger: InnerLogger }> { const { ignoreDefaultArgs = false, args = [], @@ -92,7 +92,7 @@ export class Firefox implements BrowserType { port = 0, } = options; assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.'); - const logger = new RootLogger(options.loggerSink); + const logger = new RootLogger(options.logger); const firefoxArguments = []; @@ -153,7 +153,7 @@ export class Firefox implements BrowserType { } async connect(options: ConnectOptions): Promise { - const logger = new RootLogger(options.loggerSink); + const logger = new RootLogger(options.logger); return await WebSocketTransport.connect(options.wsEndpoint, transport => { return FFBrowser.connect(transport, logger, false, options.slowMo); }); @@ -198,7 +198,7 @@ export class Firefox implements BrowserType { } } -function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: Logger, port: number): WebSocketWrapper { +function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: InnerLogger, port: number): WebSocketWrapper { const server = new ws.Server({ port }); const guid = helper.guid(); const idMixer = new SequenceNumberMixer<{id: number, socket: ws}>(); diff --git a/src/server/pipeTransport.ts b/src/server/pipeTransport.ts index f5744ba3ce..1b850afc12 100644 --- a/src/server/pipeTransport.ts +++ b/src/server/pipeTransport.ts @@ -17,7 +17,7 @@ import { helper, RegisteredListener } from '../helper'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse } from '../transport'; -import { logError, Logger } from '../logger'; +import { logError, InnerLogger } from '../logger'; export class PipeTransport implements ConnectionTransport { private _pipeWrite: NodeJS.WritableStream | null; @@ -28,7 +28,7 @@ export class PipeTransport implements ConnectionTransport { onmessage?: (message: ProtocolResponse) => void; onclose?: () => void; - constructor(pipeWrite: NodeJS.WritableStream, pipeRead: NodeJS.ReadableStream, logger: Logger) { + constructor(pipeWrite: NodeJS.WritableStream, pipeRead: NodeJS.ReadableStream, logger: InnerLogger) { this._pipeWrite = pipeWrite; this._eventListeners = [ helper.addEventListener(pipeRead, 'data', buffer => this._dispatch(buffer)), diff --git a/src/server/processLauncher.ts b/src/server/processLauncher.ts index 1cc982ee8b..e9ed827c5a 100644 --- a/src/server/processLauncher.ts +++ b/src/server/processLauncher.ts @@ -16,7 +16,7 @@ */ import * as childProcess from 'child_process'; -import { Log, Logger } from '../logger'; +import { Log, InnerLogger } from '../logger'; import * as fs from 'fs'; import * as os from 'os'; import * as path from 'path'; @@ -59,7 +59,7 @@ export type LaunchProcessOptions = { // Note: attemptToGracefullyClose should reject if it does not close the browser. attemptToGracefullyClose: () => Promise, onkill: (exitCode: number | null, signal: string | null) => void, - logger: Logger, + logger: InnerLogger, }; type LaunchResult = { diff --git a/src/server/webkit.ts b/src/server/webkit.ts index 61af30000c..52bc48f714 100644 --- a/src/server/webkit.ts +++ b/src/server/webkit.ts @@ -31,7 +31,7 @@ import { LaunchType } from '../browser'; import { BrowserServer, WebSocketWrapper } from './browserServer'; import { Events } from '../events'; import { BrowserContext } from '../browserContext'; -import { Logger, logError, RootLogger } from '../logger'; +import { InnerLogger, logError, RootLogger } from '../logger'; export class WebKit implements BrowserType { private _executablePath: (string|undefined); @@ -71,7 +71,7 @@ export class WebKit implements BrowserType { return browser._defaultContext!; } - private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport, downloadsPath: string, logger: Logger }> { + private async _launchServer(options: LaunchServerOptions, launchType: LaunchType, userDataDir?: string): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport, downloadsPath: string, logger: InnerLogger }> { const { ignoreDefaultArgs = false, args = [], @@ -83,7 +83,7 @@ export class WebKit implements BrowserType { port = 0, } = options; assert(!port || launchType === 'server', 'Cannot specify a port without launching as a server.'); - const logger = new RootLogger(options.loggerSink); + const logger = new RootLogger(options.logger); let temporaryUserDataDir: string | null = null; if (!userDataDir) { @@ -137,7 +137,7 @@ export class WebKit implements BrowserType { async connect(options: ConnectOptions): Promise { return await WebSocketTransport.connect(options.wsEndpoint, transport => { - return WKBrowser.connect(transport, new RootLogger(options.loggerSink), options.slowMo); + return WKBrowser.connect(transport, new RootLogger(options.logger), options.slowMo); }); } @@ -170,7 +170,7 @@ const mkdtempAsync = util.promisify(fs.mkdtemp); const WEBKIT_PROFILE_PATH = path.join(os.tmpdir(), 'playwright_dev_profile-'); -function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: Logger, port: number): WebSocketWrapper { +function wrapTransportWithWebSocket(transport: ConnectionTransport, logger: InnerLogger, port: number): WebSocketWrapper { const server = new ws.Server({ port }); const guid = helper.guid(); const idMixer = new SequenceNumberMixer<{id: number, socket: ws}>(); diff --git a/src/webkit/wkBrowser.ts b/src/webkit/wkBrowser.ts index 07146214b5..f5355d419f 100644 --- a/src/webkit/wkBrowser.ts +++ b/src/webkit/wkBrowser.ts @@ -26,7 +26,7 @@ import * as types from '../types'; import { Protocol } from './protocol'; import { kPageProxyMessageReceived, PageProxyMessageReceivedPayload, WKConnection, WKSession } from './wkConnection'; import { WKPage } from './wkPage'; -import { Logger } from '../logger'; +import { InnerLogger } from '../logger'; const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.4 Safari/605.1.15'; @@ -41,12 +41,12 @@ export class WKBrowser extends BrowserBase { private _firstPageCallback: () => void = () => {}; private readonly _firstPagePromise: Promise; - static async connect(transport: ConnectionTransport, logger: Logger, slowMo: number = 0, attachToDefaultContext: boolean = false): Promise { + static async connect(transport: ConnectionTransport, logger: InnerLogger, slowMo: number = 0, attachToDefaultContext: boolean = false): Promise { const browser = new WKBrowser(SlowMoTransport.wrap(transport, slowMo), logger, attachToDefaultContext); return browser; } - constructor(transport: ConnectionTransport, logger: Logger, attachToDefaultContext: boolean) { + constructor(transport: ConnectionTransport, logger: InnerLogger, attachToDefaultContext: boolean) { super(logger); this._connection = new WKConnection(transport, logger, this._onDisconnect.bind(this)); this._browserSession = this._connection.browserSession; diff --git a/src/webkit/wkConnection.ts b/src/webkit/wkConnection.ts index a50799cbd2..3b8465886e 100644 --- a/src/webkit/wkConnection.ts +++ b/src/webkit/wkConnection.ts @@ -19,7 +19,7 @@ import { EventEmitter } from 'events'; import { assert } from '../helper'; import { ConnectionTransport, ProtocolRequest, ProtocolResponse, protocolLog } from '../transport'; import { Protocol } from './protocol'; -import { Logger } from '../logger'; +import { InnerLogger } from '../logger'; // WKPlaywright uses this special id to issue Browser.close command which we // should ignore. @@ -37,9 +37,9 @@ export class WKConnection { private _closed = false; readonly browserSession: WKSession; - private _logger: Logger; + private _logger: InnerLogger; - constructor(transport: ConnectionTransport, logger: Logger, onDisconnect: () => void) { + constructor(transport: ConnectionTransport, logger: InnerLogger, onDisconnect: () => void) { this._transport = transport; this._logger = logger; this._transport.onmessage = this._dispatchMessage.bind(this); diff --git a/test/fixtures.spec.js b/test/fixtures.spec.js index a52b06b153..eeaa2d0a81 100644 --- a/test/fixtures.spec.js +++ b/test/fixtures.spec.js @@ -21,8 +21,6 @@ const {FFOX, CHROMIUM, WEBKIT, WIN, LINUX} = require('./utils').testOptions(brow async function testSignal(state, action, exitOnClose) { const options = Object.assign({}, state.defaultBrowserOptions, { - // Disable DUMPIO to cleanly read stdout. - dumpio: false, handleSIGINT: true, handleSIGTERM: true, handleSIGHUP: true, diff --git a/test/logger.spec.js b/test/logger.spec.js new file mode 100644 index 0000000000..693ab40518 --- /dev/null +++ b/test/logger.spec.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const fs = require('fs'); +const path = require('path'); +const {FFOX, CHROMIUM, WEBKIT} = require('./utils').testOptions(browserType); + +describe('Logger', function() { + it('should log', async({browserType, defaultBrowserOptions}) => { + const log = []; + const browser = await browserType.launch({...defaultBrowserOptions, logger: { + log: (name, severity, message) => log.push({name, severity, message}), + isEnabled: (name, severity) => severity !== 'verbose' + }}); + await browser.close(); + expect(log.length > 0).toBeTruthy(); + expect(log.filter(item => item.name.includes('browser')).length > 0).toBeTruthy(); + expect(log.filter(item => item.severity === 'info').length > 0).toBeTruthy(); + expect(log.filter(item => item.message.includes('')).length > 0).toBeTruthy(); + }); + it('should log context-level', async({browserType, defaultBrowserOptions}) => { + const log = []; + const browser = await browserType.launch(defaultBrowserOptions); + const page = await browser.newPage({ + logger: { + log: (name, severity, message) => log.push({name, severity, message}), + isEnabled: (name, severity) => severity !== 'verbose' + } + }); + await page.setContent(''); + await page.click('button'); + await browser.close(); + + expect(log.length > 0).toBeTruthy(); + expect(log.filter(item => item.message.includes('waiting for element')).length > 0).toBeTruthy(); + }); +}); diff --git a/test/test.config.js b/test/test.config.js index 585561e5c0..f9f97d78cf 100644 --- a/test/test.config.js +++ b/test/test.config.js @@ -210,6 +210,7 @@ module.exports = { './defaultbrowsercontext.spec.js', './fixtures.spec.js', './launcher.spec.js', + './logger.spec.js', './headful.spec.js', './multiclient.spec.js', ], diff --git a/test/test.js b/test/test.js index 2765c2029e..2b40ee55f4 100644 --- a/test/test.js +++ b/test/test.js @@ -105,7 +105,7 @@ function collect(browserNames) { const browserEnvironment = new Environment(browserName); browserEnvironment.beforeAll(async state => { state._logger = null; - state.browser = await state.browserType.launch({...launchOptions, loggerSink: { + state.browser = await state.browserType.launch({...launchOptions, logger: { isEnabled: (name, severity) => { return name === 'browser' || (name === 'protocol' && config.dumpProtocolOnFailure);