@browserless.io/browserless 2.8.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/README.md +41 -3
  3. package/assets/debugger.png +0 -0
  4. package/bin/scaffold/src/hello-world.http.ts +3 -2
  5. package/build/browserless.d.ts +5 -4
  6. package/build/browserless.js +19 -13
  7. package/build/browsers/chrome.cdp.d.ts +2 -2
  8. package/build/browsers/chrome.cdp.js +2 -2
  9. package/build/browsers/chrome.playwright.d.ts +2 -2
  10. package/build/browsers/chrome.playwright.js +2 -2
  11. package/build/browsers/chromium.cdp.d.ts +4 -4
  12. package/build/browsers/chromium.cdp.js +49 -32
  13. package/build/browsers/chromium.playwright.d.ts +4 -4
  14. package/build/browsers/chromium.playwright.js +14 -13
  15. package/build/browsers/firefox.playwright.d.ts +4 -4
  16. package/build/browsers/firefox.playwright.js +14 -13
  17. package/build/browsers/index.d.ts +24 -8
  18. package/build/browsers/index.js +20 -15
  19. package/build/browsers/webkit.playwright.d.ts +4 -4
  20. package/build/browsers/webkit.playwright.js +14 -13
  21. package/build/config.d.ts +3 -0
  22. package/build/config.js +3 -0
  23. package/build/file-system.d.ts +2 -3
  24. package/build/file-system.js +2 -2
  25. package/build/http.d.ts +1 -0
  26. package/build/http.js +1 -0
  27. package/build/index.js +7 -7
  28. package/build/limiter.d.ts +2 -3
  29. package/build/limiter.js +11 -11
  30. package/build/logger.d.ts +16 -9
  31. package/build/logger.js +32 -16
  32. package/build/monitoring.d.ts +2 -3
  33. package/build/monitoring.js +4 -4
  34. package/build/router.d.ts +1 -3
  35. package/build/router.js +21 -22
  36. package/build/routes/chrome/http/content.post.body.json +8 -8
  37. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  38. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  39. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  40. package/build/routes/chromium/http/content.post.body.json +8 -8
  41. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  42. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  43. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  44. package/build/routes/management/http/pressure.get.d.ts +63 -0
  45. package/build/routes/management/http/pressure.get.js +56 -0
  46. package/build/routes/management/http/pressure.get.response.json +76 -0
  47. package/build/routes/management/http/static.get.js +3 -3
  48. package/build/routes/management/tests/management.spec.js +16 -0
  49. package/build/server.d.ts +1 -3
  50. package/build/server.js +33 -30
  51. package/build/shared/content.http.d.ts +1 -1
  52. package/build/shared/content.http.js +4 -1
  53. package/build/shared/download.http.js +9 -9
  54. package/build/shared/function.http.js +2 -2
  55. package/build/shared/json-protocol.http.d.ts +3 -3
  56. package/build/shared/json-protocol.http.js +2 -2
  57. package/build/shared/json-version.http.d.ts +3 -3
  58. package/build/shared/json-version.http.js +2 -2
  59. package/build/shared/pdf.http.d.ts +1 -1
  60. package/build/shared/pdf.http.js +6 -4
  61. package/build/shared/performance.http.js +1 -0
  62. package/build/shared/scrape.http.d.ts +1 -1
  63. package/build/shared/scrape.http.js +4 -1
  64. package/build/shared/screenshot.http.d.ts +1 -1
  65. package/build/shared/screenshot.http.js +4 -1
  66. package/build/shared/utils/function/handler.js +7 -7
  67. package/build/shared/utils/performance/child.js +4 -4
  68. package/build/shared/utils/performance/main.d.ts +1 -1
  69. package/build/shared/utils/performance/main.js +5 -7
  70. package/build/shared/utils/performance/types.d.ts +2 -1
  71. package/build/types.d.ts +10 -1
  72. package/build/types.js +10 -1
  73. package/build/utils.d.ts +1 -1
  74. package/build/utils.js +6 -2
  75. package/package.json +18 -15
  76. package/scripts/install-debugger.js +55 -15
  77. package/src/browserless.ts +25 -12
  78. package/src/browsers/chrome.cdp.ts +2 -5
  79. package/src/browsers/chrome.playwright.ts +2 -5
  80. package/src/browsers/chromium.cdp.ts +84 -35
  81. package/src/browsers/chromium.playwright.ts +26 -13
  82. package/src/browsers/firefox.playwright.ts +28 -13
  83. package/src/browsers/index.ts +24 -16
  84. package/src/browsers/webkit.playwright.ts +28 -13
  85. package/src/config.ts +4 -0
  86. package/src/file-system.ts +2 -7
  87. package/src/http.ts +1 -0
  88. package/src/index.ts +7 -7
  89. package/src/limiter.ts +13 -11
  90. package/src/logger.ts +39 -18
  91. package/src/monitoring.ts +6 -8
  92. package/src/router.ts +20 -20
  93. package/src/routes/management/http/pressure.get.ts +135 -0
  94. package/src/routes/management/http/static.get.ts +3 -5
  95. package/src/routes/management/tests/management.spec.ts +26 -0
  96. package/src/server.ts +43 -30
  97. package/src/shared/content.http.ts +5 -1
  98. package/src/shared/download.http.ts +9 -9
  99. package/src/shared/function.http.ts +2 -2
  100. package/src/shared/json-protocol.http.ts +8 -3
  101. package/src/shared/json-version.http.ts +8 -4
  102. package/src/shared/pdf.http.ts +6 -4
  103. package/src/shared/performance.http.ts +1 -0
  104. package/src/shared/scrape.http.ts +5 -1
  105. package/src/shared/screenshot.http.ts +4 -1
  106. package/src/shared/utils/function/handler.ts +7 -7
  107. package/src/shared/utils/performance/child.ts +4 -4
  108. package/src/shared/utils/performance/main.ts +5 -6
  109. package/src/shared/utils/performance/types.ts +2 -1
  110. package/src/types.ts +9 -0
  111. package/src/utils.ts +7 -2
  112. package/static/docs/swagger.json +138 -10
  113. package/static/docs/swagger.min.json +137 -9
  114. package/static/function/client.js +4290 -5542
  115. package/static/function/index.html +4290 -5542
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  BrowserServerOptions,
3
3
  Config,
4
+ Logger,
4
5
  Request,
5
6
  ServerError,
6
- createLogger,
7
7
  } from '@browserless.io/browserless';
8
8
  import playwright, { Page } from 'playwright-core';
9
9
  import { Duplex } from 'stream';
@@ -15,24 +15,27 @@ export class FirefoxPlaywright extends EventEmitter {
15
15
  protected config: Config;
16
16
  protected userDataDir: string | null;
17
17
  protected running = false;
18
+ protected logger: Logger;
18
19
  protected proxy = httpProxy.createProxyServer();
19
20
  protected browser: playwright.BrowserServer | null = null;
20
21
  protected browserWSEndpoint: string | null = null;
21
- protected debug = createLogger('browsers:firefox:playwright');
22
22
 
23
23
  constructor({
24
24
  config,
25
25
  userDataDir,
26
+ logger,
26
27
  }: {
27
28
  config: Config;
29
+ logger: Logger;
28
30
  userDataDir: FirefoxPlaywright['userDataDir'];
29
31
  }) {
30
32
  super();
31
33
 
32
34
  this.userDataDir = userDataDir;
33
35
  this.config = config;
36
+ this.logger = logger;
34
37
 
35
- this.debug(`Starting new browser instance`);
38
+ this.logger.info(`Starting new ${this.constructor.name} instance`);
36
39
  }
37
40
 
38
41
  protected cleanListeners() {
@@ -43,7 +46,9 @@ export class FirefoxPlaywright extends EventEmitter {
43
46
 
44
47
  public close = async (): Promise<void> => {
45
48
  if (this.browser) {
46
- this.debug(`Closing browser process and all listeners`);
49
+ this.logger.trace(
50
+ `Closing ${this.constructor.name} process and all listeners`,
51
+ );
47
52
  this.emit('close');
48
53
  this.cleanListeners();
49
54
  this.browser.close();
@@ -56,21 +61,27 @@ export class FirefoxPlaywright extends EventEmitter {
56
61
  public pages = async (): Promise<[]> => [];
57
62
 
58
63
  public getPageId = (): string => {
59
- throw new ServerError(`#getPageId is not yet supported with this browser.`);
64
+ throw new ServerError(
65
+ `#getPageId is not yet supported with ${this.constructor.name}.`,
66
+ );
60
67
  };
61
68
 
62
69
  public makeLiveURL = (): void => {
63
- throw new ServerError(`Live URLs are not yet supported with this browser.`);
70
+ throw new ServerError(
71
+ `Live URLs are not yet supported with ${this.constructor.name}.`,
72
+ );
64
73
  };
65
74
 
66
75
  public newPage = async (): Promise<Page> => {
67
- throw new ServerError(`Can't create new page with this browser`);
76
+ throw new ServerError(
77
+ `Can't create new page with ${this.constructor.name}`,
78
+ );
68
79
  };
69
80
 
70
81
  public launch = async (
71
82
  options: BrowserServerOptions = {},
72
83
  ): Promise<playwright.BrowserServer> => {
73
- this.debug(`Launching Playwright Handler`);
84
+ this.logger.info(`Launching ${this.constructor.name} Handler`);
74
85
 
75
86
  this.browser = await playwright.firefox.launchServer({
76
87
  ...options,
@@ -83,7 +94,9 @@ export class FirefoxPlaywright extends EventEmitter {
83
94
 
84
95
  const browserWSEndpoint = this.browser.wsEndpoint();
85
96
 
86
- this.debug(`Browser is running on ${browserWSEndpoint}`);
97
+ this.logger.info(
98
+ `${this.constructor.name} is running on ${browserWSEndpoint}`,
99
+ );
87
100
  this.browserWSEndpoint = browserWSEndpoint;
88
101
  this.running = true;
89
102
 
@@ -110,7 +123,7 @@ export class FirefoxPlaywright extends EventEmitter {
110
123
  };
111
124
 
112
125
  public proxyPageWebSocket = async () => {
113
- console.warn(`Not yet implemented`);
126
+ this.logger.warn(`Not yet implemented in ${this.constructor.name}`);
114
127
  };
115
128
 
116
129
  public proxyWebSocket = async (
@@ -126,8 +139,8 @@ export class FirefoxPlaywright extends EventEmitter {
126
139
  }
127
140
  socket.once('close', resolve);
128
141
 
129
- this.debug(
130
- `Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`,
142
+ this.logger.info(
143
+ `Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`,
131
144
  );
132
145
 
133
146
  // Delete headers known to cause issues
@@ -144,7 +157,9 @@ export class FirefoxPlaywright extends EventEmitter {
144
157
  target: this.browserWSEndpoint,
145
158
  },
146
159
  (error) => {
147
- this.debug(`Error proxying session: ${error}`);
160
+ this.logger.error(
161
+ `Error proxying session to ${this.constructor.name}: ${error}`,
162
+ );
148
163
  this.close();
149
164
  return reject(error);
150
165
  },
@@ -17,13 +17,13 @@ import {
17
17
  FirefoxPlaywright,
18
18
  HTTPManagementRoutes,
19
19
  Hooks,
20
+ Logger,
20
21
  NotFound,
21
22
  Request,
22
23
  ServerError,
23
24
  WebkitPlaywright,
24
25
  availableBrowsers,
25
26
  convertIfBase64,
26
- createLogger,
27
27
  exists,
28
28
  generateDataDir,
29
29
  makeExternalURL,
@@ -38,7 +38,7 @@ export class BrowserManager {
38
38
  protected browsers: Map<BrowserInstance, BrowserlessSession> = new Map();
39
39
  protected launching: Map<string, Promise<unknown>> = new Map();
40
40
  protected timers: Map<string, number> = new Map();
41
- protected debug = createLogger('browser-manager');
41
+ protected log = new Logger('browser-manager');
42
42
  protected chromeBrowsers = [ChromiumCDP, ChromeCDP];
43
43
  protected playwrightBrowserNames = [
44
44
  ChromiumPlaywright.name,
@@ -52,14 +52,14 @@ export class BrowserManager {
52
52
  protected hooks: Hooks,
53
53
  ) {}
54
54
 
55
- private browserIsChrome = (b: BrowserInstance) =>
55
+ protected browserIsChrome = (b: BrowserInstance) =>
56
56
  this.chromeBrowsers.some((chromeBrowser) => b instanceof chromeBrowser);
57
57
 
58
58
  protected removeUserDataDir = async (userDataDir: string | null) => {
59
59
  if (userDataDir && (await exists(userDataDir))) {
60
- this.debug(`Deleting data directory "${userDataDir}"`);
60
+ this.log.info(`Deleting data directory "${userDataDir}"`);
61
61
  await deleteAsync(userDataDir, { force: true }).catch((err) => {
62
- this.debug(
62
+ this.log.error(
63
63
  `Error cleaning up user-data-dir "${err}" at ${userDataDir}`,
64
64
  );
65
65
  });
@@ -75,7 +75,7 @@ export class BrowserManager {
75
75
  * and modifies URLs to set them to the appropriate addresses configured.
76
76
  * When both Chrome and Chromium are installed, defaults to Chromium.
77
77
  */
78
- public getProtocolJSON = async (): Promise<object> => {
78
+ public getProtocolJSON = async (logger: Logger): Promise<object> => {
79
79
  const Browser = (await availableBrowsers).find((InstalledBrowser) =>
80
80
  this.chromeBrowsers.some(
81
81
  (ChromeBrowser) => InstalledBrowser === ChromeBrowser,
@@ -87,6 +87,7 @@ export class BrowserManager {
87
87
  const browser = new Browser({
88
88
  blockAds: false,
89
89
  config: this.config,
90
+ logger,
90
91
  userDataDir: null,
91
92
  });
92
93
  await browser.launch();
@@ -110,8 +111,8 @@ export class BrowserManager {
110
111
  * and modifies URLs to set them to the appropriate addresses configured.
111
112
  * When both Chrome and Chromium are installed, defaults to Chromium.
112
113
  */
113
- public getVersionJSON = async (): Promise<CDPJSONPayload> => {
114
- this.debug(`Launching Chromium to generate /json/version results`);
114
+ public getVersionJSON = async (logger: Logger): Promise<CDPJSONPayload> => {
115
+ this.log.info(`Launching Chromium to generate /json/version results`);
115
116
  const Browser = (await availableBrowsers).find((InstalledBrowser) =>
116
117
  this.chromeBrowsers.some(
117
118
  (ChromeBrowser) => InstalledBrowser === ChromeBrowser,
@@ -124,6 +125,7 @@ export class BrowserManager {
124
125
  const browser = new Browser({
125
126
  blockAds: false,
126
127
  config: this.config,
128
+ logger,
127
129
  userDataDir: null,
128
130
  });
129
131
  await browser.launch();
@@ -214,7 +216,7 @@ export class BrowserManager {
214
216
  .filter((_) => _ !== null) as Array<CDPJSONPayload>;
215
217
  };
216
218
 
217
- private generateSessionJson = async (
219
+ protected generateSessionJson = async (
218
220
  browser: BrowserInstance,
219
221
  session: BrowserlessSession,
220
222
  ) => {
@@ -267,18 +269,18 @@ export class BrowserManager {
267
269
  session: BrowserlessSession,
268
270
  ): Promise<void> => {
269
271
  const cleanupACtions: Array<() => Promise<void>> = [];
270
- this.debug(`${session.numbConnected} Client(s) are currently connected`);
272
+ this.log.info(`${session.numbConnected} Client(s) are currently connected`);
271
273
 
272
274
  // Don't close if there's clients still connected
273
275
  if (session.numbConnected > 0) {
274
276
  return;
275
277
  }
276
278
 
277
- this.debug(`Closing browser session`);
279
+ this.log.info(`Closing browser session`);
278
280
  cleanupACtions.push(() => browser.close());
279
281
 
280
282
  if (session.isTempDataDir) {
281
- this.debug(
283
+ this.log.info(
282
284
  `Deleting "${session.userDataDir}" user-data-dir and session from memory`,
283
285
  );
284
286
  this.browsers.delete(browser);
@@ -302,7 +304,9 @@ export class BrowserManager {
302
304
  public complete = async (browser: BrowserInstance): Promise<void> => {
303
305
  const session = this.browsers.get(browser);
304
306
  if (!session) {
305
- this.debug(`Couldn't locate session for browser, proceeding with close`);
307
+ this.log.info(
308
+ `Couldn't locate session for browser, proceeding with close`,
309
+ );
306
310
  return browser.close();
307
311
  }
308
312
 
@@ -321,6 +325,7 @@ export class BrowserManager {
321
325
  public getBrowserForRequest = async (
322
326
  req: Request,
323
327
  router: BrowserHTTPRoute | BrowserWebsocketRoute,
328
+ logger: Logger,
324
329
  ): Promise<BrowserInstance> => {
325
330
  const { browser: Browser } = router;
326
331
  const blockAds = parseBooleanParam(
@@ -344,7 +349,7 @@ export class BrowserManager {
344
349
  if (found) {
345
350
  const [browser, session] = found;
346
351
  ++session.numbConnected;
347
- this.debug(`Located browser with ID ${id}`);
352
+ this.log.debug(`Located browser with ID ${id}`);
348
353
  return browser;
349
354
  }
350
355
 
@@ -387,6 +392,8 @@ export class BrowserManager {
387
392
  const found = allPages.flat().find((b) => b.id === id);
388
393
 
389
394
  if (found) {
395
+ const session = this.browsers.get(found.browser)!;
396
+ ++session.numbConnected;
390
397
  return found.browser;
391
398
  }
392
399
 
@@ -446,6 +453,7 @@ export class BrowserManager {
446
453
  const browser = new Browser({
447
454
  blockAds,
448
455
  config: this.config,
456
+ logger,
449
457
  userDataDir,
450
458
  });
451
459
 
@@ -477,7 +485,7 @@ export class BrowserManager {
477
485
  };
478
486
 
479
487
  public shutdown = async (): Promise<void> => {
480
- this.debug(`Closing down browser instances`);
488
+ this.log.info(`Closing down browser instances`);
481
489
  const sessions = Array.from(this.browsers);
482
490
  await Promise.all(sessions.map(([b]) => b.close()));
483
491
  const timers = Array.from(this.timers);
@@ -486,7 +494,7 @@ export class BrowserManager {
486
494
  this.browsers = new Map();
487
495
  this.timers = new Map();
488
496
  await this.stop();
489
- this.debug(`Shutdown complete`);
497
+ this.log.info(`Shutdown complete`);
490
498
  };
491
499
 
492
500
  /**
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  BrowserServerOptions,
3
3
  Config,
4
+ Logger,
4
5
  Request,
5
6
  ServerError,
6
- createLogger,
7
7
  } from '@browserless.io/browserless';
8
8
  import playwright, { Page } from 'playwright-core';
9
9
  import { Duplex } from 'stream';
@@ -18,21 +18,24 @@ export class WebkitPlaywright extends EventEmitter {
18
18
  protected proxy = httpProxy.createProxyServer();
19
19
  protected browser: playwright.BrowserServer | null = null;
20
20
  protected browserWSEndpoint: string | null = null;
21
- protected debug = createLogger('browsers:webkit:playwright');
21
+ protected logger: Logger;
22
22
 
23
23
  constructor({
24
24
  config,
25
25
  userDataDir,
26
+ logger,
26
27
  }: {
27
28
  config: Config;
29
+ logger: Logger;
28
30
  userDataDir: WebkitPlaywright['userDataDir'];
29
31
  }) {
30
32
  super();
31
33
 
32
34
  this.userDataDir = userDataDir;
33
35
  this.config = config;
36
+ this.logger = logger;
34
37
 
35
- this.debug(`Starting new browser instance`);
38
+ this.logger.info(`Starting new ${this.constructor.name} instance`);
36
39
  }
37
40
 
38
41
  protected cleanListeners() {
@@ -43,7 +46,9 @@ export class WebkitPlaywright extends EventEmitter {
43
46
 
44
47
  public close = async (): Promise<void> => {
45
48
  if (this.browser) {
46
- this.debug(`Closing browser process and all listeners`);
49
+ this.logger.info(
50
+ `Closing ${this.constructor.name} process and all listeners`,
51
+ );
47
52
  this.emit('close');
48
53
  this.cleanListeners();
49
54
  this.browser.close();
@@ -56,21 +61,27 @@ export class WebkitPlaywright extends EventEmitter {
56
61
  public pages = async (): Promise<[]> => [];
57
62
 
58
63
  public getPageId = (): string => {
59
- throw new ServerError(`#getPageId is not yet supported with this browser.`);
64
+ throw new ServerError(
65
+ `#getPageId is not yet supported with ${this.constructor.name}.`,
66
+ );
60
67
  };
61
68
 
62
69
  public makeLiveURL = (): void => {
63
- throw new ServerError(`Live URLs are not yet supported with this browser.`);
70
+ throw new ServerError(
71
+ `Live URLs are not yet supported with ${this.constructor.name}.`,
72
+ );
64
73
  };
65
74
 
66
75
  public newPage = async (): Promise<Page> => {
67
- throw new ServerError(`Can't create new page with this browser`);
76
+ throw new ServerError(
77
+ `Can't create new page with ${this.constructor.name}`,
78
+ );
68
79
  };
69
80
 
70
81
  public launch = async (
71
82
  options: BrowserServerOptions = {},
72
83
  ): Promise<playwright.BrowserServer> => {
73
- this.debug(`Launching Playwright Handler`);
84
+ this.logger.info(`Launching ${this.constructor.name} Handler`);
74
85
 
75
86
  this.browser = await playwright.webkit.launchServer({
76
87
  ...options,
@@ -83,7 +94,9 @@ export class WebkitPlaywright extends EventEmitter {
83
94
 
84
95
  const browserWSEndpoint = this.browser.wsEndpoint();
85
96
 
86
- this.debug(`Browser is running on ${browserWSEndpoint}`);
97
+ this.logger.info(
98
+ `${this.constructor.name} is running on ${browserWSEndpoint}`,
99
+ );
87
100
  this.browserWSEndpoint = browserWSEndpoint;
88
101
  this.running = true;
89
102
 
@@ -110,7 +123,7 @@ export class WebkitPlaywright extends EventEmitter {
110
123
  };
111
124
 
112
125
  public proxyPageWebSocket = async () => {
113
- console.warn(`Not yet implemented`);
126
+ this.logger.warn(`Not yet implemented`);
114
127
  };
115
128
 
116
129
  public proxyWebSocket = async (
@@ -126,8 +139,8 @@ export class WebkitPlaywright extends EventEmitter {
126
139
  }
127
140
  socket.once('close', resolve);
128
141
 
129
- this.debug(
130
- `Proxying ${req.parsed.href} to browser ${this.browserWSEndpoint}`,
142
+ this.logger.info(
143
+ `Proxying ${req.parsed.href} to ${this.constructor.name} ${this.browserWSEndpoint}`,
131
144
  );
132
145
 
133
146
  // Delete headers known to cause issues
@@ -144,7 +157,9 @@ export class WebkitPlaywright extends EventEmitter {
144
157
  target: this.browserWSEndpoint,
145
158
  },
146
159
  (error) => {
147
- this.debug(`Error proxying session: ${error}`);
160
+ this.logger.error(
161
+ `Error proxying session to ${this.constructor.name}: ${error}`,
162
+ );
148
163
  this.close();
149
164
  return reject(error);
150
165
  },
package/src/config.ts CHANGED
@@ -148,6 +148,7 @@ export class Config extends EventEmitter {
148
148
  '30000'
149
149
  );
150
150
  protected static = process.env.STATIC ?? path.join(__dirname, '..', 'static');
151
+ protected debuggerDir = path.join(this.static, 'debugger');
151
152
  protected retries = +(process.env.RETRIES ?? '5');
152
153
  protected allowFileProtocol = !!parseEnvVars(false, 'ALLOW_FILE_PROTOCOL');
153
154
  protected allowGet = !!parseEnvVars(false, 'ALLOW_GET', 'ENABLE_API_GET');
@@ -187,6 +188,7 @@ export class Config extends EventEmitter {
187
188
  public getQueued = (): number => this.queued;
188
189
  public getTimeout = (): number => this.timeout;
189
190
  public getStatic = (): string => this.static;
191
+ public getDebuggerDir = (): string => this.debuggerDir;
190
192
  public getRetries = (): number => this.retries;
191
193
  public getAllowFileProtocol = (): boolean => this.allowFileProtocol;
192
194
  public getCPULimit = (): number => this.maxCpu;
@@ -198,6 +200,8 @@ export class Config extends EventEmitter {
198
200
  public getTimeoutAlertURL = () => this.timeoutAlertURL;
199
201
  public getErrorAlertURL = () => this.errorAlertURL;
200
202
 
203
+ public hasDebugger = (): Promise<boolean> => exists(this.debuggerDir);
204
+
201
205
  /**
202
206
  * If true, allows GET style calls on our browser-based APIs, using
203
207
  * ?body=JSON format.
@@ -1,16 +1,11 @@
1
- import {
2
- Config,
3
- createLogger,
4
- decrypt,
5
- encrypt,
6
- } from '@browserless.io/browserless';
1
+ import { Config, Logger, decrypt, encrypt } from '@browserless.io/browserless';
7
2
  import { readFile, writeFile } from 'fs/promises';
8
3
  import { EventEmitter } from 'events';
9
4
 
10
5
  export class FileSystem extends EventEmitter {
11
6
  protected fsMap: Map<string, string[]> = new Map();
12
7
  protected currentAESKey: Buffer;
13
- protected log = createLogger('file-system');
8
+ protected logger = new Logger('file-system');
14
9
 
15
10
  constructor(protected config: Config) {
16
11
  super();
package/src/http.ts CHANGED
@@ -128,6 +128,7 @@ export enum HTTPManagementRoutes {
128
128
  config = '/config',
129
129
  metrics = '/metrics',
130
130
  metricsTotal = '/metrics/total',
131
+ pressure = '/pressure',
131
132
  sessions = '/sessions',
132
133
  static = '/',
133
134
  }
package/src/index.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Browserless, createLogger } from '@browserless.io/browserless';
1
+ import { Browserless, Logger } from '@browserless.io/browserless';
2
2
 
3
3
  (async () => {
4
4
  const browserless = new Browserless();
5
- const debug = createLogger('index.js');
5
+ const logger = new Logger('index.js');
6
6
  browserless.start();
7
7
 
8
8
  process
@@ -15,27 +15,27 @@ import { Browserless, createLogger } from '@browserless.io/browserless';
15
15
  process.exit(1);
16
16
  })
17
17
  .once('SIGTERM', async () => {
18
- debug(`SIGTERM received, saving and closing down`);
18
+ logger.info(`SIGTERM received, saving and closing down`);
19
19
  await browserless.stop();
20
20
  process.exit(0);
21
21
  })
22
22
  .once('SIGINT', async () => {
23
- debug(`SIGINT received, saving and closing down`);
23
+ logger.info(`SIGINT received, saving and closing down`);
24
24
  await browserless.stop();
25
25
  process.exit(0);
26
26
  })
27
27
  .once('SIGHUP', async () => {
28
- debug(`SIGHUP received, saving and closing down`);
28
+ logger.info(`SIGHUP received, saving and closing down`);
29
29
  await browserless.stop();
30
30
  process.exit(0);
31
31
  })
32
32
  .once('SIGUSR2', async () => {
33
- debug(`SIGUSR2 received, saving and closing down`);
33
+ logger.info(`SIGUSR2 received, saving and closing down`);
34
34
  await browserless.stop();
35
35
  process.exit(0);
36
36
  })
37
37
  .once('exit', () => {
38
- debug(`Process is finished, exiting`);
38
+ logger.info(`Process is finished, exiting`);
39
39
  process.exit(0);
40
40
  });
41
41
  })();
package/src/limiter.ts CHANGED
@@ -2,11 +2,11 @@ import {
2
2
  AfterResponse,
3
3
  Config,
4
4
  Hooks,
5
+ Logger,
5
6
  Metrics,
6
7
  Monitoring,
7
8
  TooManyRequests,
8
9
  WebHooks,
9
- createLogger,
10
10
  } from '@browserless.io/browserless';
11
11
  import q from 'queue';
12
12
 
@@ -26,7 +26,7 @@ interface Job {
26
26
 
27
27
  export class Limiter extends q {
28
28
  protected queued: number;
29
- protected debug = createLogger('limiter');
29
+ protected logger = new Logger('limiter');
30
30
 
31
31
  constructor(
32
32
  protected config: Config,
@@ -42,22 +42,22 @@ export class Limiter extends q {
42
42
  });
43
43
  this.queued = config.getQueued();
44
44
 
45
- this.debug(
45
+ this.logger.info(
46
46
  `Concurrency: ${this.concurrency} queue: ${this.queued} timeout: ${this.timeout}ms`,
47
47
  );
48
48
 
49
49
  config.on('concurrent', (concurrency: number) => {
50
- this.debug(`Concurrency updated to ${concurrency}`);
50
+ this.logger.info(`Concurrency updated to ${concurrency}`);
51
51
  this.concurrency = concurrency;
52
52
  });
53
53
 
54
54
  config.on('queued', (queued: number) => {
55
- this.debug(`Queue updated to ${queued}`);
55
+ this.logger.info(`Queue updated to ${queued}`);
56
56
  this.queued = queued;
57
57
  });
58
58
 
59
59
  config.on('timeout', (timeout: number) => {
60
- this.debug(`Timeout updated to ${timeout}ms`);
60
+ this.logger.info(`Timeout updated to ${timeout}ms`);
61
61
  this.timeout = timeout <= 0 ? 0 : timeout;
62
62
  });
63
63
 
@@ -83,7 +83,7 @@ export class Limiter extends q {
83
83
 
84
84
  protected handleSuccess({ detail: { job } }: { detail: { job: Job } }) {
85
85
  const timeUsed = Date.now() - job.start;
86
- this.debug(
86
+ this.logger.info(
87
87
  `Job has succeeded after ${timeUsed.toLocaleString()}ms of activity.`,
88
88
  );
89
89
  this.metrics.addSuccessful(Date.now() - job.start);
@@ -101,12 +101,12 @@ export class Limiter extends q {
101
101
  detail: { job: Job; next: Job };
102
102
  }) {
103
103
  const timeUsed = Date.now() - job.start;
104
- this.debug(
104
+ this.logger.warn(
105
105
  `Job has hit timeout after ${timeUsed.toLocaleString()}ms of activity.`,
106
106
  );
107
107
  this.metrics.addTimedout(Date.now() - job.start);
108
108
  this.webhooks.callTimeoutAlertURL();
109
- this.debug(`Calling timeout handler`);
109
+ this.logger.info(`Calling timeout handler`);
110
110
  job?.onTimeoutFn(job);
111
111
  this.jobEnd({
112
112
  req: job.args[0],
@@ -122,7 +122,9 @@ export class Limiter extends q {
122
122
  }: {
123
123
  detail: { error: unknown; job: Job };
124
124
  }) {
125
- this.debug(`Recording failed stat, cleaning up: "${error?.toString()}"`);
125
+ this.logger.info(
126
+ `Recording failed stat, cleaning up: "${error?.toString()}"`,
127
+ );
126
128
  this.metrics.addError(Date.now() - job.start);
127
129
  this.webhooks.callErrorAlertURL(error?.toString() ?? 'Unknown Error');
128
130
  this.jobEnd({
@@ -133,7 +135,7 @@ export class Limiter extends q {
133
135
  }
134
136
 
135
137
  protected logQueue(message: string) {
136
- this.debug(
138
+ this.logger.info(
137
139
  `(Running: ${this.executing}, Pending: ${this.waiting}) ${message} `,
138
140
  );
139
141
  }
package/src/logger.ts CHANGED
@@ -1,31 +1,52 @@
1
1
  import { Request, createLogger } from '@browserless.io/browserless';
2
2
 
3
3
  export class Logger {
4
- protected _log: ReturnType<typeof createLogger>;
5
- protected _verbose: ReturnType<typeof createLogger>;
6
- protected _error: ReturnType<typeof createLogger>;
4
+ protected _trace: (...args: unknown[]) => void;
5
+ protected _debug: (...args: unknown[]) => void;
6
+ protected _info: (...args: unknown[]) => void;
7
+ protected _warn: (...args: unknown[]) => void;
8
+ protected _error: (...args: unknown[]) => void;
9
+ protected _fatal: (...args: unknown[]) => void;
7
10
 
8
11
  constructor(
9
12
  protected prefix: string,
10
- protected request: Request,
13
+ protected request?: Request,
11
14
  ) {
12
- this._log = createLogger(prefix);
13
- this._verbose = this._log.extend('verbose');
14
- this._error = this._log.extend('error');
15
- }
15
+ const logger = createLogger(prefix);
16
16
 
17
- public verbose(...messages: string[]) {
18
- const ip = this.request.socket.remoteAddress ?? 'Unknown';
19
- this._verbose(ip, ...messages);
17
+ this._trace = logger.extend('trace');
18
+ this._debug = logger.extend('debug');
19
+ this._info = logger.extend('info');
20
+ this._warn = logger.extend('warn');
21
+ this._error = logger.extend('error');
22
+ this._fatal = logger.extend('fatal');
20
23
  }
21
24
 
22
- public log(...messages: string[]) {
23
- const ip = this.request.socket.remoteAddress ?? 'Unknown';
24
- this._log(ip, ...messages);
25
+ protected get reqInfo() {
26
+ return this.request ? this.request.socket.remoteAddress ?? 'Unknown' : '';
25
27
  }
26
28
 
27
- public error(...messages: string[]) {
28
- const ip = this.request.socket.remoteAddress ?? 'Unknown';
29
- this._error(ip, ...messages);
30
- }
29
+ public trace = (...messages: unknown[]) => {
30
+ this._trace(this.reqInfo, ...messages);
31
+ };
32
+
33
+ public debug = (...messages: unknown[]) => {
34
+ this._debug(this.reqInfo, ...messages);
35
+ };
36
+
37
+ public info = (...messages: unknown[]) => {
38
+ this._info(this.reqInfo, ...messages);
39
+ };
40
+
41
+ public warn = (...messages: unknown[]) => {
42
+ this._warn(this.reqInfo, ...messages);
43
+ };
44
+
45
+ public error = (...messages: unknown[]) => {
46
+ this._error(this.reqInfo, ...messages);
47
+ };
48
+
49
+ public fatal = (...messages: unknown[]) => {
50
+ this._fatal(this.reqInfo, ...messages);
51
+ };
31
52
  }