feat(logger): introduce context-level logger (#1896)

This commit is contained in:
Pavel Feldman 2020-04-20 23:24:53 -07:00 committed by GitHub
parent 2320d9cbf3
commit 5b085fdf03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 144 additions and 90 deletions

View File

@ -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'.
```
<!-- GEN:toc -->
- [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)
<!-- GEN:stop -->
#### 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"

View File

@ -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';

View File

@ -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<BrowserContext>;
@ -30,13 +30,13 @@ export interface Browser extends EventEmitter {
close(): Promise<void>;
}
export abstract class BrowserBase extends EventEmitter implements Browser, Logger {
export abstract class BrowserBase extends EventEmitter implements Browser, InnerLogger {
_downloadsPath: string = '';
private _downloads = new Map<string, Download>();
_ownedServer: BrowserServer | null = null;
readonly _logger: Logger;
readonly _logger: InnerLogger;
constructor(logger: Logger) {
constructor(logger: InnerLogger) {
super();
this._logger = logger;
}

View File

@ -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<string, string[]>();
readonly _downloads = new Set<Download>();
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);
}
}

View File

@ -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<CRBrowser> {
static async connect(transport: ConnectionTransport, isPersistent: boolean, logger: InnerLogger, slowMo?: number): Promise<CRBrowser> {
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;

View File

@ -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<string, CRSession>();
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;

View File

@ -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<string, string>;
_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;

View File

@ -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<void>;
private _firstPageCallback = () => {};
static async connect(transport: ConnectionTransport, logger: Logger, attachToDefaultContext: boolean, slowMo?: number): Promise<FFBrowser> {
static async connect(transport: ConnectionTransport, logger: InnerLogger, attachToDefaultContext: boolean, slowMo?: number): Promise<FFBrowser> {
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();

View File

@ -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<number, {resolve: Function, reject: Function, error: Error, method: string}>;
private _transport: ConnectionTransport;
private _logger: Logger;
private _logger: InnerLogger;
readonly _sessions: Map<string, FFSession>;
_closed: boolean;
@ -43,7 +43,7 @@ export class FFConnection extends EventEmitter {
removeListener: <T extends keyof Protocol.Events | symbol>(event: T, listener: (payload: T extends symbol ? any : Protocol.Events[T extends keyof Protocol.Events ? T : never]) => void) => this;
once: <T extends keyof Protocol.Events | symbol>(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;

View File

@ -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<any>;
@ -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;
}

View File

@ -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<string, number>([
['reset', 0],
]);
class DebugLoggerSink implements LoggerSink {
class DebugLoggerSink implements Logger {
private _debuggers = new Map<string, debug.IDebugger>();
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';

View File

@ -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<void>;
@ -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<js.ExecutionContext>;
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;

View File

@ -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 };

View File

@ -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<CRBrowser> {
private _executablePath: (string|undefined);
@ -71,7 +71,7 @@ export class Chromium implements BrowserType<CRBrowser> {
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<CRBrowser> {
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<CRBrowser> {
async connect(options: ConnectOptions): Promise<CRBrowser> {
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<CRBrowser> {
}
}
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();

View File

@ -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<FFBrowser> {
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<FFBrowser> {
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<FFBrowser> {
}
async connect(options: ConnectOptions): Promise<FFBrowser> {
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<FFBrowser> {
}
}
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}>();

View File

@ -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)),

View File

@ -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<any>,
onkill: (exitCode: number | null, signal: string | null) => void,
logger: Logger,
logger: InnerLogger,
};
type LaunchResult = {

View File

@ -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<WKBrowser> {
private _executablePath: (string|undefined);
@ -71,7 +71,7 @@ export class WebKit implements BrowserType<WKBrowser> {
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<WKBrowser> {
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<WKBrowser> {
async connect(options: ConnectOptions): Promise<WKBrowser> {
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}>();

View File

@ -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<void>;
static async connect(transport: ConnectionTransport, logger: Logger, slowMo: number = 0, attachToDefaultContext: boolean = false): Promise<WKBrowser> {
static async connect(transport: ConnectionTransport, logger: InnerLogger, slowMo: number = 0, attachToDefaultContext: boolean = false): Promise<WKBrowser> {
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;

View File

@ -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);

View File

@ -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,

50
test/logger.spec.js Normal file
View File

@ -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('<launching>')).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('<button>Button</button>');
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();
});
});

View File

@ -210,6 +210,7 @@ module.exports = {
'./defaultbrowsercontext.spec.js',
'./fixtures.spec.js',
'./launcher.spec.js',
'./logger.spec.js',
'./headful.spec.js',
'./multiclient.spec.js',
],

View File

@ -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);