Skip to content

Commit 6674458

Browse files
authored
feat(rpc): log api calls into LoggerSink (#2904)
1 parent c63b706 commit 6674458

19 files changed

+127
-83
lines changed

src/rpc/client/browser.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { Page } from './page';
2121
import { ChannelOwner } from './channelOwner';
2222
import { Events } from '../../events';
2323
import { CDPSession } from './cdpSession';
24+
import { LoggerSink } from '../../loggerSink';
2425

2526
export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
2627
readonly _contexts = new Set<BrowserContext>();
@@ -36,8 +37,8 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
3637
return browser ? Browser.from(browser) : null;
3738
}
3839

39-
constructor(parent: ChannelOwner, guid: string, initializer: BrowserInitializer) {
40-
super(parent, guid, initializer, true);
40+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserInitializer) {
41+
super(parent, type, guid, initializer, true);
4142
this._channel.on('close', () => {
4243
this._isConnected = false;
4344
this.emit(Events.Browser.Disconnected);
@@ -47,10 +48,12 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
4748
this._closedPromise = new Promise(f => this.once(Events.Browser.Disconnected, f));
4849
}
4950

50-
async newContext(options: types.BrowserContextOptions = {}): Promise<BrowserContext> {
51-
delete (options as any).logger;
51+
async newContext(options: types.BrowserContextOptions & { logger?: LoggerSink } = {}): Promise<BrowserContext> {
52+
const logger = options.logger;
53+
options = { ...options, logger: undefined };
5254
const context = BrowserContext.from(await this._channel.newContext(options));
5355
this._contexts.add(context);
56+
context._logger = logger || this._logger;
5457
context._browser = this;
5558
return context;
5659
}
@@ -59,8 +62,7 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
5962
return [...this._contexts];
6063
}
6164

62-
async newPage(options: types.BrowserContextOptions = {}): Promise<Page> {
63-
delete (options as any).logger;
65+
async newPage(options: types.BrowserContextOptions & { logger?: LoggerSink } = {}): Promise<Page> {
6466
const context = await this.newContext(options);
6567
const page = await context.newPage();
6668
page._ownedContext = context;

src/rpc/client/browserContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
5050
return context ? BrowserContext.from(context) : null;
5151
}
5252

53-
constructor(parent: ChannelOwner, guid: string, initializer: BrowserContextInitializer) {
54-
super(parent, guid, initializer, true);
53+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) {
54+
super(parent, type, guid, initializer, true);
5555
initializer.pages.forEach(p => {
5656
const page = Page.from(p);
5757
this._pages.add(page);

src/rpc/client/browserServer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export class BrowserServer extends ChannelOwner<BrowserServerChannel, BrowserSer
2424
return (server as any)._object;
2525
}
2626

27-
constructor(parent: ChannelOwner, guid: string, initializer: BrowserServerInitializer) {
28-
super(parent, guid, initializer);
27+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserServerInitializer) {
28+
super(parent, type, guid, initializer);
2929
this._channel.on('close', () => this.emit(Events.BrowserServer.Close));
3030
}
3131

src/rpc/client/browserType.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@ import { Browser } from './browser';
2020
import { BrowserContext } from './browserContext';
2121
import { ChannelOwner } from './channelOwner';
2222
import { BrowserServer } from './browserServer';
23+
import { LoggerSink } from '../../loggerSink';
2324

2425
export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeInitializer> {
2526

26-
static from(browserTyep: BrowserTypeChannel): BrowserType {
27-
return (browserTyep as any)._object;
27+
static from(browserType: BrowserTypeChannel): BrowserType {
28+
return (browserType as any)._object;
2829
}
2930

30-
constructor(parent: ChannelOwner, guid: string, initializer: BrowserTypeInitializer) {
31-
super(parent, guid, initializer);
31+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserTypeInitializer) {
32+
super(parent, type, guid, initializer);
3233
}
3334

3435
executablePath(): string {
@@ -39,23 +40,32 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
3940
return this._initializer.name;
4041
}
4142

42-
async launch(options: types.LaunchOptions = {}): Promise<Browser> {
43-
delete (options as any).logger;
44-
return Browser.from(await this._channel.launch(options));
43+
async launch(options: types.LaunchOptions & { logger?: LoggerSink } = {}): Promise<Browser> {
44+
const logger = options.logger;
45+
options = { ...options, logger: undefined };
46+
const browser = Browser.from(await this._channel.launch(options));
47+
browser._logger = logger;
48+
return browser;
4549
}
4650

47-
async launchServer(options: types.LaunchServerOptions = {}): Promise<BrowserServer> {
48-
delete (options as any).logger;
51+
async launchServer(options: types.LaunchServerOptions & { logger?: LoggerSink } = {}): Promise<BrowserServer> {
52+
options = { ...options, logger: undefined };
4953
return BrowserServer.from(await this._channel.launchServer(options));
5054
}
5155

52-
async launchPersistentContext(userDataDir: string, options: types.LaunchOptions & types.BrowserContextOptions = {}): Promise<BrowserContext> {
53-
delete (options as any).logger;
54-
return BrowserContext.from(await this._channel.launchPersistentContext({ userDataDir, ...options }));
56+
async launchPersistentContext(userDataDir: string, options: types.LaunchOptions & types.BrowserContextOptions & { logger?: LoggerSink } = {}): Promise<BrowserContext> {
57+
const logger = options.logger;
58+
options = { ...options, logger: undefined };
59+
const context = BrowserContext.from(await this._channel.launchPersistentContext({ userDataDir, ...options }));
60+
context._logger = logger;
61+
return context;
5562
}
5663

57-
async connect(options: types.ConnectOptions): Promise<Browser> {
58-
delete (options as any).logger;
59-
return Browser.from(await this._channel.connect(options));
64+
async connect(options: types.ConnectOptions & { logger?: LoggerSink }): Promise<Browser> {
65+
const logger = options.logger;
66+
options = { ...options, logger: undefined };
67+
const browser = Browser.from(await this._channel.connect(options));
68+
browser._logger = logger;
69+
return browser;
6070
}
6171
}

src/rpc/client/cdpSession.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ export class CDPSession extends ChannelOwner<CDPSessionChannel, CDPSessionInitia
2929
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;
3030
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;
3131

32-
constructor(parent: ChannelOwner, guid: string, initializer: CDPSessionInitializer) {
33-
super(parent, guid, initializer, true);
32+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: CDPSessionInitializer) {
33+
super(parent, type, guid, initializer, true);
3434

3535
this._channel.on('event', ({ method, params }) => this.emit(method, params));
3636
this._channel.on('disconnected', () => this._dispose());

src/rpc/client/channelOwner.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { EventEmitter } from 'events';
1818
import { Channel } from '../channels';
1919
import { Connection } from './connection';
2020
import { assert } from '../../helper';
21+
import { LoggerSink } from '../../loggerSink';
2122

2223
export abstract class ChannelOwner<T extends Channel = Channel, Initializer = {}> extends EventEmitter {
2324
private _connection: Connection;
@@ -27,21 +28,25 @@ export abstract class ChannelOwner<T extends Channel = Channel, Initializer = {}
2728
// Only "isScope" channel owners have registered objects inside.
2829
private _objects = new Map<string, ChannelOwner>();
2930

31+
readonly _type: string;
3032
readonly _guid: string;
3133
readonly _channel: T;
3234
readonly _initializer: Initializer;
35+
_logger: LoggerSink | undefined;
3336

34-
constructor(parent: ChannelOwner | Connection, guid: string, initializer: Initializer, isScope?: boolean) {
37+
constructor(parent: ChannelOwner | Connection, type: string, guid: string, initializer: Initializer, isScope?: boolean) {
3538
super();
36-
3739
this._connection = parent instanceof Connection ? parent : parent._connection;
40+
this._type = type;
3841
this._guid = guid;
3942
this._isScope = !!isScope;
4043
this._parent = parent instanceof Connection ? undefined : parent;
4144

4245
this._connection._objects.set(guid, this);
43-
if (this._parent)
46+
if (this._parent) {
4447
this._parent._objects.set(guid, this);
48+
this._logger = this._parent._logger;
49+
}
4550

4651
const base = new EventEmitter();
4752
this._channel = new Proxy(base, {
@@ -60,7 +65,23 @@ export abstract class ChannelOwner<T extends Channel = Channel, Initializer = {}
6065
return obj.addListener;
6166
if (prop === 'removeEventListener')
6267
return obj.removeListener;
63-
return (params: any) => this._connection.sendMessageToServer({ guid, method: String(prop), params });
68+
69+
return async (params: any) => {
70+
const method = String(prop);
71+
const apiName = this._type + '.' + method;
72+
if (this._logger && this._logger.isEnabled('api', 'info'))
73+
this._logger.log('api', 'info', `=> ${apiName} started`, [], { color: 'cyan' });
74+
try {
75+
const result = await this._connection.sendMessageToServer({ guid, method: String(prop), params });
76+
if (this._logger && this._logger.isEnabled('api', 'info'))
77+
this._logger.log('api', 'info', `=> ${apiName} succeeded`, [], { color: 'cyan' });
78+
return result;
79+
} catch (e) {
80+
if (this._logger && this._logger.isEnabled('api', 'info'))
81+
this._logger.log('api', 'info', `=> ${apiName} failed`, [], { color: 'cyan' });
82+
throw e;
83+
}
84+
};
6485
},
6586
});
6687
(this._channel as any)._object = this;

src/rpc/client/connection.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { Channel } from '../channels';
3636

3737
class Root extends ChannelOwner<Channel, {}> {
3838
constructor(connection: Connection) {
39-
super(connection, '', {}, true);
39+
super(connection, '', '', {}, true);
4040
}
4141
}
4242

@@ -131,59 +131,58 @@ export class Connection {
131131
initializer = this._replaceGuidsWithChannels(initializer);
132132
switch (type) {
133133
case 'bindingCall':
134-
result = new BindingCall(parent, guid, initializer);
134+
result = new BindingCall(parent, type, guid, initializer);
135135
break;
136136
case 'browser':
137-
result = new Browser(parent, guid, initializer);
137+
result = new Browser(parent, type, guid, initializer);
138138
break;
139139
case 'browserServer':
140-
result = new BrowserServer(parent, guid, initializer);
140+
result = new BrowserServer(parent, type, guid, initializer);
141141
break;
142142
case 'browserType':
143-
result = new BrowserType(parent, guid, initializer);
143+
result = new BrowserType(parent, type, guid, initializer);
144144
break;
145145
case 'cdpSession':
146-
// Chromium-specific.
147-
result = new CDPSession(parent, guid, initializer);
146+
result = new CDPSession(parent, type, guid, initializer);
148147
break;
149148
case 'context':
150-
result = new BrowserContext(parent, guid, initializer);
149+
result = new BrowserContext(parent, type, guid, initializer);
151150
break;
152151
case 'consoleMessage':
153-
result = new ConsoleMessage(parent, guid, initializer);
152+
result = new ConsoleMessage(parent, type, guid, initializer);
154153
break;
155154
case 'dialog':
156-
result = new Dialog(parent, guid, initializer);
155+
result = new Dialog(parent, type, guid, initializer);
157156
break;
158157
case 'download':
159-
result = new Download(parent, guid, initializer);
158+
result = new Download(parent, type, guid, initializer);
160159
break;
161160
case 'elementHandle':
162-
result = new ElementHandle(parent, guid, initializer);
161+
result = new ElementHandle(parent, type, guid, initializer);
163162
break;
164163
case 'frame':
165-
result = new Frame(parent, guid, initializer);
164+
result = new Frame(parent, type, guid, initializer);
166165
break;
167166
case 'jsHandle':
168-
result = new JSHandle(parent, guid, initializer);
167+
result = new JSHandle(parent, type, guid, initializer);
169168
break;
170169
case 'page':
171-
result = new Page(parent, guid, initializer);
170+
result = new Page(parent, type, guid, initializer);
172171
break;
173172
case 'playwright':
174-
result = new Playwright(parent, guid, initializer);
173+
result = new Playwright(parent, type, guid, initializer);
175174
break;
176175
case 'request':
177-
result = new Request(parent, guid, initializer);
176+
result = new Request(parent, type, guid, initializer);
178177
break;
179178
case 'response':
180-
result = new Response(parent, guid, initializer);
179+
result = new Response(parent, type, guid, initializer);
181180
break;
182181
case 'route':
183-
result = new Route(parent, guid, initializer);
182+
result = new Route(parent, type, guid, initializer);
184183
break;
185184
case 'worker':
186-
result = new Worker(parent, guid, initializer);
185+
result = new Worker(parent, type, guid, initializer);
187186
break;
188187
default:
189188
throw new Error('Missing type ' + type);

src/rpc/client/consoleMessage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ export class ConsoleMessage extends ChannelOwner<ConsoleMessageChannel, ConsoleM
2525
return (message as any)._object;
2626
}
2727

28-
constructor(parent: ChannelOwner, guid: string, initializer: ConsoleMessageInitializer) {
29-
super(parent, guid, initializer);
28+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: ConsoleMessageInitializer) {
29+
super(parent, type, guid, initializer);
3030
}
3131

3232
type(): string {

src/rpc/client/dialog.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export class Dialog extends ChannelOwner<DialogChannel, DialogInitializer> {
2222
return (dialog as any)._object;
2323
}
2424

25-
constructor(parent: ChannelOwner, guid: string, initializer: DialogInitializer) {
26-
super(parent, guid, initializer);
25+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: DialogInitializer) {
26+
super(parent, type, guid, initializer);
2727
}
2828

2929
type(): string {

src/rpc/client/download.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export class Download extends ChannelOwner<DownloadChannel, DownloadInitializer>
2424
return (download as any)._object;
2525
}
2626

27-
constructor(parent: ChannelOwner, guid: string, initializer: DownloadInitializer) {
28-
super(parent, guid, initializer);
27+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: DownloadInitializer) {
28+
super(parent, type, guid, initializer);
2929
}
3030

3131
url(): string {

0 commit comments

Comments
 (0)