diff --git a/CHANGELOG.md b/CHANGELOG.md index 31274993ab5..fc70c26042a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ -# [Latest](https://github.com/browserless/chrome/compare/v2.7.1...main) +# [Latest](https://github.com/browserless/chrome/compare/v2.8.0...main) +- Dependency updates. + +# [v2.8.0](https://github.com/browserless/chrome/compare/v2.7.1...v2.8.0) +**April 12, 2024** +**Potentially Breaking** +- New `Logger` class and SDK primitives in support of that. +- Routes now get an instance of `Logger`, before the `browser` argument or as the last argument for HTTP routes. + +**Updates** +- Numerous SDK fixes and updates in the CLI. - Dependency updates. # [v2.7.1](https://github.com/browserless/chrome/compare/v2.7.0...v2.7.1) diff --git a/bin/browserless.js b/bin/browserless.js index 375d82dc173..719981507cc 100755 --- a/bin/browserless.js +++ b/bin/browserless.js @@ -27,7 +27,7 @@ if (typeof process.env.DEBUG === 'undefined') { const log = debug('browserless.io:sdk:log'); const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const cmd = process.argv[2]; +const cmd = process.argv[2] ?? 'help'; const subCMD = process.argv[3]; const allowedCMDs = [ 'build', @@ -143,7 +143,7 @@ const isConstructor = (reference) => typeof reference === 'function'; const start = async (dev = false) => { const { httpRoutes, webSocketRoutes, files } = dev ? await build() - : await getSourceFiles(); + : await getSourceFiles(projectDir); log(`Importing all class overrides if present`); @@ -152,6 +152,7 @@ const start = async (dev = false) => { Config, FileSystem, Limiter, + Logger, Metrics, Monitoring, Router, @@ -164,6 +165,7 @@ const start = async (dev = false) => { importDefault(files, 'config'), importDefault(files, 'file-system'), importDefault(files, 'limiter'), + importDefault(files, 'logger'), importDefault(files, 'metrics'), importDefault(files, 'monitoring'), importDefault(files, 'router'), @@ -197,6 +199,7 @@ const start = async (dev = false) => { : Router; const browserless = new Browserless({ + Logger, browserManager, config, fileSystem, @@ -369,7 +372,7 @@ const create = async () => { } } - log(`Installing npm modules...`); + log(`Installing browsers and npm modules, this might take a few minutes...`); await installDependencies(installPath); log( @@ -377,7 +380,8 @@ const create = async () => { ); }; -const help = () => { +const help = async () => { + console.log(`Version: ${(await browserlessPackageJSON).version}`); if (subCMD) { if (!allowedCMDs.includes(subCMD)) { throw new Error(`Unknown command of "${subCMD}" passed.`); diff --git a/bin/scaffold/README.md b/bin/scaffold/README.md index 90d354fe18a..2c0584e1a7e 100644 --- a/bin/scaffold/README.md +++ b/bin/scaffold/README.md @@ -128,6 +128,7 @@ Internally, we use this same class-based system, so feel free to see how those w import { APITags, HTTPRoute, + Logger, Methods, contentTypes, writeResponse, @@ -174,7 +175,7 @@ export default class HelloWorldRoute extends HTTPRoute { // Handler is a function, getting the request and response objects, and is where you'll write the // core logic behind this route. Use utilities like writeResponse or writeJSONResponse to help // return the appropriate response. - handler = async (_req, res): Promise => { + handler = async (_req, res, _logger: Logger): Promise => { const response: ResponseSchema = 'Hello World!'; return writeResponse(res, 200, ResponseSchema, contentTypes.text); }; @@ -189,6 +190,7 @@ import { BrowserWebsocketRoute, ChromiumCDP, CDPLaunchOptions, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -226,7 +228,7 @@ export default class ChromiumWebSocketRoute extends BrowserWebsocketRoute { // Routes with a browser type get a browser argument of the Browser instance, otherwise // request, socket, and head are the other 3 arguments. Here we pass them through // and proxy the request into Chromium to handle. - handler = async (req, socket, head, chromium): Promise => + handler = async (req, socket, head, logger, chromium): Promise => chromium.proxyWebSocket(req, socket, head); } ``` @@ -293,7 +295,7 @@ Then, later, in your route you can define some functionality and load the config ```ts // src/pdf.http.ts -import { BrowserHTTPRoute } from '@browserless.io/browserless'; +import { BrowserHTTPRoute, Logger } from '@browserless.io/browserless'; import MyConfig from './config'; // Export the BodySchema for documentation site to parse, plus @@ -342,7 +344,7 @@ export default class PDFToS3Route extends BrowserHTTPRoute { tags = [APITags.browserAPI]; // Handler's are where we embed the logic that facilitates this route. - handler = async (req, res, browser): Promise => { + handler = async (req, res, logger, browser): Promise => { // Modules like Config are injected via this internal methods. // Use them to load core modules within the platform. const config = this.config() as MyConfig; diff --git a/bin/scaffold/src/hello-world.http.ts b/bin/scaffold/src/hello-world.http.ts index 82a96cc52aa..af6a30b3dd9 100644 --- a/bin/scaffold/src/hello-world.http.ts +++ b/bin/scaffold/src/hello-world.http.ts @@ -1,6 +1,7 @@ import { APITags, HTTPRoute, + Logger, Methods, Request, Response, @@ -21,7 +22,11 @@ export default class HelloWorldHTTPRoute extends HTTPRoute { method = Methods.get; path = '/hello'; tags = [APITags.management]; - handler = async (_req: Request, res: Response): Promise => { + handler = async ( + _req: Request, + _logger: Logger, + res: Response, + ): Promise => { const response: ResponseSchema = 'Hello World!'; return writeResponse(res, 200, response, contentTypes.text); }; diff --git a/package-lock.json b/package-lock.json index 6bbe1da02db..de74e2f13a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@browserless.io/browserless", - "version": "2.7.1", + "version": "2.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@browserless.io/browserless", - "version": "2.7.1", + "version": "2.8.0", "license": "SSPL", "dependencies": { "debug": "^4.3.2", @@ -19,7 +19,7 @@ "lighthouse": "^11.1.0", "micromatch": "^4.0.4", "playwright-core": "1.43.0", - "puppeteer-core": "^22.6.3", + "puppeteer-core": "^22.6.4", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "queue": "^7.0.0", @@ -4647,12 +4647,12 @@ } }, "node_modules/puppeteer-core": { - "version": "22.6.3", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.6.3.tgz", - "integrity": "sha512-YrTAak5zCTWVTnVaCK1b7FD1qFCCT9bSvQhLzamnIsJ57/tfuXiT8ZvPJR2SBfahyFTYFCcaZAd/Npow3lmDGA==", + "version": "22.6.4", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-22.6.4.tgz", + "integrity": "sha512-QtfJwPmqQec3EHc6LqbEz03vSiuVAr9bYp0TV87dLoreev6ZevsXdLgOfQgoA3GocrsSe/eUf7NRPQ1lQfsc3w==", "dependencies": { "@puppeteer/browsers": "2.2.1", - "chromium-bidi": "0.5.16", + "chromium-bidi": "0.5.17", "debug": "4.3.4", "devtools-protocol": "0.0.1262051", "ws": "8.16.0" @@ -4662,9 +4662,9 @@ } }, "node_modules/puppeteer-core/node_modules/chromium-bidi": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.16.tgz", - "integrity": "sha512-IT5lnR44h/qZQ4GaCHvBxYIl4cQL2i9UvFyYeRyVdcpY04hx5H720HQfe/7Oz7ndxaYVLQFGpCO71J4X2Ye/Gw==", + "version": "0.5.17", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.17.tgz", + "integrity": "sha512-BqOuIWUgTPj8ayuBFJUYCCuwIcwjBsb3/614P7tt1bEPJ4i1M0kCdIl0Wi9xhtswBXnfO2bTpTMkHD71H8rJMg==", "dependencies": { "mitt": "3.0.1", "urlpattern-polyfill": "10.0.0", diff --git a/package.json b/package.json index 728390b42e4..ab9eff9723d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@browserless.io/browserless", - "version": "2.7.1", + "version": "2.8.0", "license": "SSPL", "description": "The browserless platform", "author": "browserless.io", @@ -58,7 +58,7 @@ "lighthouse": "^11.1.0", "micromatch": "^4.0.4", "playwright-core": "1.43.0", - "puppeteer-core": "^22.6.3", + "puppeteer-core": "^22.6.4", "puppeteer-extra": "^3.3.6", "puppeteer-extra-plugin-stealth": "^2.11.2", "queue": "^7.0.0", diff --git a/src/browserless.ts b/src/browserless.ts index 12b8e4ca7e9..14d7b2ee751 100644 --- a/src/browserless.ts +++ b/src/browserless.ts @@ -1,5 +1,6 @@ import * as path from 'path'; import { + Logger as BlessLogger, BrowserHTTPRoute, BrowserManager, BrowserWebsocketRoute, @@ -51,6 +52,7 @@ export class Browserless extends EventEmitter { protected fileSystem: FileSystem; protected hooks: Hooks; protected limiter: Limiter; + protected Logger: typeof BlessLogger; protected metrics: Metrics; protected monitoring: Monitoring; protected router: Router; @@ -71,12 +73,14 @@ export class Browserless extends EventEmitter { fileSystem, hooks, limiter, + Logger: LoggerOverride, metrics, monitoring, router, token, webhooks, }: { + Logger?: Browserless['Logger']; browserManager?: Browserless['browserManager']; config?: Browserless['config']; fileSystem?: Browserless['fileSystem']; @@ -89,6 +93,7 @@ export class Browserless extends EventEmitter { webhooks?: Browserless['webhooks']; } = {}) { super(); + this.Logger = LoggerOverride ?? BlessLogger; this.config = config || new Config(); this.metrics = metrics || new Metrics(); this.token = token || new Token(this.config); @@ -108,7 +113,8 @@ export class Browserless extends EventEmitter { this.hooks, ); this.router = - router || new Router(this.config, this.browserManager, this.limiter); + router || + new Router(this.config, this.browserManager, this.limiter, this.Logger); } protected saveMetrics = async (): Promise => { @@ -235,7 +241,6 @@ export class Browserless extends EventEmitter { ...internalHttpRouteFiles, ]) { if (httpRoute.endsWith('js')) { - const { name } = path.parse(httpRoute); const [bodySchema, querySchema] = await Promise.all( routeSchemas.map(async (schemaType) => { const schemaPath = path.parse(httpRoute); @@ -249,7 +254,6 @@ export class Browserless extends EventEmitter { const routeImport = `${ this.config.getIsWin() ? 'file:///' : '' }${httpRoute}`; - const logger = createLogger(`http:${name}`); const { default: Route, }: { default: Implements | Implements } = @@ -258,7 +262,6 @@ export class Browserless extends EventEmitter { this.browserManager, this.config, this.fileSystem, - logger, this.metrics, this.monitoring, this.staticSDKDir, @@ -271,7 +274,6 @@ export class Browserless extends EventEmitter { route.metrics = () => this.metrics; route.monitoring = () => this.monitoring; route.fileSystem = () => this.fileSystem; - route.debug = () => logger; route.staticSDKDir = () => this.staticSDKDir; httpRoutes.push(route); @@ -285,7 +287,6 @@ export class Browserless extends EventEmitter { ...internalWsRouteFiles, ]) { if (wsRoute.endsWith('js')) { - const { name } = path.parse(wsRoute); const [, querySchema] = await Promise.all( routeSchemas.map(async (schemaType) => { const schemaPath = path.parse(wsRoute); @@ -299,7 +300,6 @@ export class Browserless extends EventEmitter { const wsImport = `${ this.config.getIsWin() ? 'file:///' : '' }${wsRoute}`; - const logger = createLogger(`ws:${name}`); const { default: Route, }: { @@ -311,7 +311,6 @@ export class Browserless extends EventEmitter { this.browserManager, this.config, this.fileSystem, - logger, this.metrics, this.monitoring, this.staticSDKDir, @@ -323,7 +322,6 @@ export class Browserless extends EventEmitter { route.metrics = () => this.metrics; route.monitoring = () => this.monitoring; route.fileSystem = () => this.fileSystem; - route.debug = () => logger; route.staticSDKDir = () => this.staticSDKDir; wsRoutes.push(route); @@ -368,6 +366,7 @@ export class Browserless extends EventEmitter { this.token, this.router, this.hooks, + this.Logger, ); await this.server.start(); diff --git a/src/exports.ts b/src/exports.ts index 75ba7b07170..8e21c41b693 100644 --- a/src/exports.ts +++ b/src/exports.ts @@ -6,6 +6,7 @@ export * from './file-system.js'; export * from './hooks.js'; export * from './http.js'; export * from './limiter.js'; +export * from './logger.js'; export * from './metrics.js'; export * from './mime-types.js'; export * from './monitoring.js'; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 00000000000..aea8957705d --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,31 @@ +import { Request, createLogger } from '@browserless.io/browserless'; + +export class Logger { + protected _log: ReturnType; + protected _verbose: ReturnType; + protected _error: ReturnType; + + constructor( + protected prefix: string, + protected request: Request, + ) { + this._log = createLogger(prefix); + this._verbose = this._log.extend('verbose'); + this._error = this._log.extend('error'); + } + + public verbose(...messages: string[]) { + const ip = this.request.socket.remoteAddress ?? 'Unknown'; + this._verbose(ip, ...messages); + } + + public log(...messages: string[]) { + const ip = this.request.socket.remoteAddress ?? 'Unknown'; + this._log(ip, ...messages); + } + + public error(...messages: string[]) { + const ip = this.request.socket.remoteAddress ?? 'Unknown'; + this._error(ip, ...messages); + } +} diff --git a/src/router.ts b/src/router.ts index d6c4aa24d8b..0da470bdf53 100644 --- a/src/router.ts +++ b/src/router.ts @@ -6,6 +6,7 @@ import { HTTPManagementRoutes, HTTPRoute, Limiter, + Logger, Methods, PathTypes, Request, @@ -30,6 +31,7 @@ export class Router extends EventEmitter { protected config: Config, protected browserManager: BrowserManager, protected limiter: Limiter, + protected logger: typeof Logger, ) { super(); } @@ -70,7 +72,7 @@ export class Router extends EventEmitter { this.log(`HTTP Request has closed prior to running`); return Promise.resolve(); } - + const logger = new this.logger(route.name, req); if ('browser' in route && route.browser) { const browser = await this.browserManager.getBrowserForRequest( req, @@ -90,7 +92,7 @@ export class Router extends EventEmitter { try { this.verbose(`Running found HTTP handler.`); return await Promise.race([ - handler(req, res, browser), + handler(req, res, logger, browser), new Promise((resolve, reject) => { res.once('close', () => { if (!res.writableEnded) { @@ -107,7 +109,7 @@ export class Router extends EventEmitter { } } - return (handler as HTTPRoute['handler'])(req, res); + return (handler as HTTPRoute['handler'])(req, res, logger); }; protected wrapWebSocketHandler = @@ -120,7 +122,7 @@ export class Router extends EventEmitter { this.log(`WebSocket Request has closed prior to running`); return Promise.resolve(); } - + const logger = new this.logger(route.name, req); if ('browser' in route && route.browser) { const browser = await this.browserManager.getBrowserForRequest( req, @@ -139,14 +141,14 @@ export class Router extends EventEmitter { try { this.verbose(`Running found WebSocket handler.`); - await handler(req, socket, head, browser); + await handler(req, socket, head, logger, browser); } finally { this.verbose(`WebSocket Request handler has finished.`); this.browserManager.complete(browser); } return; } - return (handler as WebSocketRoute['handler'])(req, socket, head); + return (handler as WebSocketRoute['handler'])(req, socket, head, logger); }; public registerHTTPRoute( @@ -230,7 +232,7 @@ export class Router extends EventEmitter { micromatch.isMatch(req.parsed.pathname, p), ) && r.method === (req.method?.toLocaleLowerCase() as Methods) && - (accepts.some((a) => a.startsWith('*/*')) || + (accepts.some((a) => a.includes('*/*')) || r.contentTypes.some((contentType) => accepts.includes(contentType), )) && diff --git a/src/routes/firefox/ws/playwright.ts b/src/routes/firefox/ws/playwright.ts index 99799b348e4..4a603c40219 100644 --- a/src/routes/firefox/ws/playwright.ts +++ b/src/routes/firefox/ws/playwright.ts @@ -5,6 +5,7 @@ import { BrowserWebsocketRoute, BrowserlessRoutes, FirefoxPlaywright, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -29,6 +30,7 @@ export default class FirefoxPlaywrightWebSocketRoute extends BrowserWebsocketRou req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: FirefoxPlaywright, ): Promise => { const isPlaywright = req.headers['user-agent'] diff --git a/src/routes/management/http/static.get.ts b/src/routes/management/http/static.get.ts index e1bd514308f..71c161f4ab9 100644 --- a/src/routes/management/http/static.get.ts +++ b/src/routes/management/http/static.get.ts @@ -3,6 +3,7 @@ import { BrowserlessRoutes, HTTPManagementRoutes, HTTPRoute, + Logger, Methods, NotFound, Request, @@ -23,21 +24,21 @@ const pathMap: Map< > = new Map(); const streamFile = ( - debug: debug.Debugger, + logger: Logger, res: ServerResponse, file: string, contentType?: string, ) => new Promise((resolve, reject) => { if (contentType) { - debug(`Setting content-type ${contentType}`); + logger.log(`Setting content-type ${contentType}`); res.setHeader('Content-Type', contentType); } return createReadStream(file) .on('error', (error) => { if (error) { - debug(`Error finding file ${file}, sending 404`); + logger.error(`Error finding file ${file}, sending 404`); pathMap.delete(file); return reject( new NotFound(`Request for file "${file}" was not found`), @@ -59,14 +60,16 @@ export default class StaticGetRoute extends HTTPRoute { method = Methods.get; path = HTTPManagementRoutes.static; tags = [APITags.management]; - handler = async (req: Request, res: ServerResponse): Promise => { + handler = async ( + req: Request, + res: ServerResponse, + logger: Logger, + ): Promise => { const { pathname } = req.parsed; const fileCache = pathMap.get(pathname); - const debug = this.debug(); - const verbose = debug.extend('verbose'); if (fileCache) { - return streamFile(verbose, res, fileCache.path, fileCache.contentType); + return streamFile(logger, res, fileCache.path, fileCache.contentType); } const config = this.config(); @@ -93,13 +96,15 @@ export default class StaticGetRoute extends HTTPRoute { } if (foundFilePaths.length > 1) { - debug( + logger.log( `Multiple files found for request to "${pathname}". Only the first file is served, so please name your files uniquely.`, ); } const [foundFilePath] = foundFilePaths; - verbose(`Found new file "${foundFilePath}", caching path and serving`); + logger.verbose( + `Found new file "${foundFilePath}", caching path and serving`, + ); const contentType = mimeTypes.get(path.extname(foundFilePath)); @@ -113,6 +118,6 @@ export default class StaticGetRoute extends HTTPRoute { path: foundFilePath, }); - return streamFile(verbose, res, foundFilePath, contentType); + return streamFile(logger, res, foundFilePath, contentType); }; } diff --git a/src/routes/webkit/ws/playwright.ts b/src/routes/webkit/ws/playwright.ts index 10be06a42fc..1cf968a8ed0 100644 --- a/src/routes/webkit/ws/playwright.ts +++ b/src/routes/webkit/ws/playwright.ts @@ -4,6 +4,7 @@ import { BrowserServerOptions, BrowserWebsocketRoute, BrowserlessRoutes, + Logger, Request, SystemQueryParameters, WebkitPlaywright, @@ -27,6 +28,7 @@ export default class WebKitPlaywrightWebSocketRoute extends BrowserWebsocketRout req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: WebkitPlaywright, ): Promise => { const isPlaywright = req.headers['user-agent'] diff --git a/src/sdk-utils.ts b/src/sdk-utils.ts index e1292c24576..d8bfde8d591 100644 --- a/src/sdk-utils.ts +++ b/src/sdk-utils.ts @@ -83,8 +83,8 @@ export const prompt = async (question: string) => { export const installDependencies = async ( workingDirectory: string, -): Promise => - new Promise((resolve, reject) => { +): Promise => { + await new Promise((resolve, reject) => { spawn('npm', ['i'], { cwd: workingDirectory, stdio: 'inherit', @@ -97,6 +97,24 @@ export const installDependencies = async ( ); }); }); + await new Promise((resolve, reject) => { + spawn( + 'npx', + 'playwright-core install --with-deps chromium firefox webkit'.split(' '), + { + cwd: workingDirectory, + stdio: 'inherit', + }, + ).once('close', (code) => { + if (code === 0) { + return resolve(); + } + return reject( + `Error when installing dependencies, see output for more details`, + ); + }); + }); +}; export const buildDockerImage = async ( cmd: string, diff --git a/src/server.ts b/src/server.ts index 45d4508938c..52c53fd7300 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,6 +2,7 @@ import * as http from 'http'; import * as stream from 'stream'; import { BadRequest, + Logger as BlessLogger, Config, HTTPRoute, Hooks, @@ -49,6 +50,7 @@ export class HTTPServer extends EventEmitter { protected token: Token, protected router: Router, protected hooks: Hooks, + protected Logger: typeof BlessLogger, ) { super(); this.host = config.getHost(); @@ -263,7 +265,7 @@ export class HTTPServer extends EventEmitter { } return (route as HTTPRoute) - .handler(req, res) + .handler(req, res, new this.Logger(route.name, req)) .then(() => { this.verbose('HTTP connection complete'); }) @@ -368,7 +370,7 @@ export class HTTPServer extends EventEmitter { } return (route as WebSocketRoute) - .handler(req, socket, head) + .handler(req, socket, head, new this.Logger(route.name, req)) .then(() => { this.verbose('Websocket connection complete'); }) diff --git a/src/shared/browser.ws.ts b/src/shared/browser.ws.ts index a2c9db9c3ca..a58243d0a60 100644 --- a/src/shared/browser.ws.ts +++ b/src/shared/browser.ws.ts @@ -4,6 +4,7 @@ import { BrowserlessRoutes, CDPLaunchOptions, ChromiumCDP, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -31,6 +32,7 @@ export default class ChromiumBrowserWebSocketRoute extends BrowserWebsocketRoute req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: ChromiumCDP, ): Promise => browser.proxyWebSocket(req, socket, head); } diff --git a/src/shared/chromium.playwright.ws.ts b/src/shared/chromium.playwright.ws.ts index c0898bfc7cf..5477f9a90ef 100644 --- a/src/shared/chromium.playwright.ws.ts +++ b/src/shared/chromium.playwright.ws.ts @@ -5,6 +5,7 @@ import { BrowserWebsocketRoute, BrowserlessRoutes, ChromiumPlaywright, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -30,6 +31,7 @@ export default class ChromiumPlaywrightWebSocketRoute extends BrowserWebsocketRo req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: ChromiumPlaywright, ): Promise => { const isPlaywright = req.headers['user-agent'] diff --git a/src/shared/chromium.ws.ts b/src/shared/chromium.ws.ts index efccdcb611c..010d683b3dd 100644 --- a/src/shared/chromium.ws.ts +++ b/src/shared/chromium.ws.ts @@ -4,6 +4,7 @@ import { BrowserlessRoutes, CDPLaunchOptions, ChromiumCDP, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -26,6 +27,7 @@ export default class ChromiumCDPWebSocketRoute extends BrowserWebsocketRoute { req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: ChromiumCDP, ): Promise => browser.proxyWebSocket(req, socket, head); } diff --git a/src/shared/content.http.ts b/src/shared/content.http.ts index d950e05bb3c..dc3b597f6e7 100644 --- a/src/shared/content.http.ts +++ b/src/shared/content.http.ts @@ -7,6 +7,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, Request, SystemQueryParameters, @@ -77,6 +78,7 @@ export default class ChromiumContentPostRoute extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + _logger: Logger, browser: BrowserInstance, ): Promise => { const contentType = diff --git a/src/shared/download.http.ts b/src/shared/download.http.ts index e3c65fbf617..a2b147f247d 100644 --- a/src/shared/download.http.ts +++ b/src/shared/download.http.ts @@ -6,6 +6,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, NotFound, Request, @@ -61,21 +62,21 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + logger: Logger, browser: BrowserInstance, ): Promise => new Promise(async (resolve, reject) => { - const debug = this.debug(); const config = this.config(); const downloadPath = path.join( await config.getDownloadsDir(), `.browserless.download.${id()}`, ); - debug(`Generating a download directory at "${downloadPath}"`); + logger.log(`Generating a download directory at "${downloadPath}"`); await mkdir(downloadPath); - const handler = functionHandler(config, debug, { downloadPath }); + const handler = functionHandler(config, logger, { downloadPath }); const response = await handler(req, browser).catch((e) => { - debug(`Error running download code handler: "${e}"`); + logger.log(`Error running download code handler: "${e}"`); reject(e); return null; }); @@ -85,10 +86,12 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute { } const { page } = response; - debug(`Download function has returned, finding downloads...`); + logger.log(`Download function has returned, finding downloads...`); async function checkIfDownloadComplete(): Promise { if (res.headersSent) { - debug(`Request headers have been sent, terminating download watch.`); + logger.log( + `Request headers have been sent, terminating download watch.`, + ); return null; } const [fileName] = await readdir(downloadPath); @@ -97,13 +100,13 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute { return checkIfDownloadComplete(); } - debug(`All files have finished downloading`); + logger.log(`All files have finished downloading`); return path.join(downloadPath, fileName); } const filePath = await checkIfDownloadComplete(); - debug(`Closing pages.`); + logger.log(`Closing pages.`); page.close(); page.removeAllListeners(); @@ -112,12 +115,12 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute { filePath && deleteAsync(filePath, { force: true }) .then(() => { - debug( + logger.log( `Successfully deleted downloads from disk at "${filePath}"`, ); }) .catch((err) => { - debug( + logger.log( `Error cleaning up downloaded files: "${err}" at "${filePath}"`, ); }), @@ -144,7 +147,7 @@ export default class ChromiumDownloadPostRoute extends BrowserHTTPRoute { } }) .on('end', () => { - debug(`Downloads successfully sent`); + logger.log(`Downloads successfully sent`); rmDownload(); return resolve(); }) diff --git a/src/shared/function.http.ts b/src/shared/function.http.ts index 956b4800dcf..08975848119 100644 --- a/src/shared/function.http.ts +++ b/src/shared/function.http.ts @@ -7,6 +7,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, Request, SystemQueryParameters, @@ -57,14 +58,14 @@ export default class ChromiumFunctionPostRoute extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + logger: Logger, browser: BrowserInstance, ): Promise => { - const debug = this.debug(); const config = this.config(); - const handler = functionHandler(config, debug); + const handler = functionHandler(config, logger); const { contentType, payload, page } = await handler(req, browser); - debug(`Got function response of "${contentType}"`); + logger.log(`Got function response of "${contentType}"`); page.close(); page.removeAllListeners(); @@ -76,7 +77,7 @@ export default class ChromiumFunctionPostRoute extends BrowserHTTPRoute { if (!type) { throw new BadRequest(`Couldn't determine function's response type.`); } else { - debug(`Sending file-type response of "${type}"`); + logger.log(`Sending file-type response of "${type}"`); const readStream = new Stream.PassThrough(); readStream.end(response); res.setHeader('Content-Type', type); diff --git a/src/shared/page.ws.ts b/src/shared/page.ws.ts index d5aac9cc5a1..e2260133d0a 100644 --- a/src/shared/page.ws.ts +++ b/src/shared/page.ws.ts @@ -4,6 +4,7 @@ import { BrowserlessRoutes, CDPLaunchOptions, ChromiumCDP, + Logger, Request, SystemQueryParameters, WebsocketRoutes, @@ -32,6 +33,7 @@ export default class ChromiumPageWebSocketRoute extends BrowserWebsocketRoute { req: Request, socket: Duplex, head: Buffer, + _logger: Logger, browser: ChromiumCDP, ): Promise => browser.proxyPageWebSocket(req, socket, head); } diff --git a/src/shared/pdf.http.ts b/src/shared/pdf.http.ts index 74876da236c..5453b0c0206 100644 --- a/src/shared/pdf.http.ts +++ b/src/shared/pdf.http.ts @@ -7,6 +7,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, Request, SystemQueryParameters, @@ -83,6 +84,7 @@ export default class ChromiumPDFPostRoute extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + _logger: Logger, browser: BrowserInstance, ): Promise => { const contentType = diff --git a/src/shared/performance.http.ts b/src/shared/performance.http.ts index 302f1f4f90f..615df422e37 100644 --- a/src/shared/performance.http.ts +++ b/src/shared/performance.http.ts @@ -6,6 +6,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, Request, SystemQueryParameters, @@ -47,6 +48,7 @@ export default class PerformancePost extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + _logger: Logger, browser: BrowserInstance, ): Promise => { const config = this.config(); diff --git a/src/shared/scrape.http.ts b/src/shared/scrape.http.ts index ebcdcfce5b3..1eca7502d7d 100644 --- a/src/shared/scrape.http.ts +++ b/src/shared/scrape.http.ts @@ -8,6 +8,7 @@ import { ChromiumCDP, HTTPRoutes, InBoundRequest, + Logger, Methods, OutBoundRequest, Request, @@ -218,6 +219,7 @@ export default class ChromiumScrapePostRoute extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + _logger: Logger, browser: BrowserInstance, ) => { const contentType = diff --git a/src/shared/screenshot.http.ts b/src/shared/screenshot.http.ts index 24407454df8..f37e6c09dd8 100644 --- a/src/shared/screenshot.http.ts +++ b/src/shared/screenshot.http.ts @@ -7,6 +7,7 @@ import { CDPLaunchOptions, ChromiumCDP, HTTPRoutes, + Logger, Methods, Request, SystemQueryParameters, @@ -85,6 +86,7 @@ export default class ScreenshotPost extends BrowserHTTPRoute { handler = async ( req: Request, res: ServerResponse, + _logger: Logger, browser: BrowserInstance, ): Promise => { const contentType = diff --git a/src/shared/utils/function/handler.ts b/src/shared/utils/function/handler.ts index ac8ce20be8e..81ffdc2d287 100644 --- a/src/shared/utils/function/handler.ts +++ b/src/shared/utils/function/handler.ts @@ -4,6 +4,7 @@ import { ChromiumCDP, Config, HTTPRoutes, + Logger, Request, UnwrapPromise, contentTypes, @@ -16,7 +17,6 @@ import { } from '@browserless.io/browserless'; import { FunctionRunner } from './client.js'; import { Page } from 'puppeteer-core'; -import debug from 'debug'; import fs from 'fs'; import path from 'path'; @@ -35,11 +35,7 @@ interface HandlerOptions { downloadPath?: string; } -export default ( - config: Config, - debug: debug.Debugger, - options: HandlerOptions = {}, - ) => +export default (config: Config, logger: Logger, options: HandlerOptions = {}) => async ( req: Request, browser: BrowserInstance, @@ -88,7 +84,7 @@ export default ( */ page.on('request', async (request) => { const requestUrl = request.url(); - debug(`Outbound Page Request: "${requestUrl}"`); + logger.log(`Outbound Page Request: "${requestUrl}"`); if (requestUrl.startsWith(functionRequestPath)) { const filename = path.basename(requestUrl); if (filename === functionCodeJS) { @@ -107,7 +103,7 @@ export default ( status: 200, }); } - debug( + logger.log( `Static asset request to "${requestUrl}" couldn't be found, 404-ing`, ); return request.respond({ @@ -116,18 +112,18 @@ export default ( status: 404, }); } - debug(`Request: "${requestUrl}" no responder found, continuing...`); + logger.log(`Request: "${requestUrl}" no responder found, continuing...`); return request.continue(); }); page.on('response', (res) => { if (res.status() !== 200) { - debug(`Received a non-200 response for request "${res.url()}"`); + logger.log(`Received a non-200 response for request "${res.url()}"`); } }); page.on('console', (event) => { - debug(`${event.type()}: ${event.text()}`); + logger.log(`${event.type()}: ${event.text()}`); }); await page.goto(functionIndexHTML); @@ -164,7 +160,7 @@ export default ( JSON.stringify(options), ) .catch((e) => { - debug(`Error running code: ${e}`); + logger.log(`Error running code: ${e}`); throw new BadRequest(e.message); }); diff --git a/src/types.ts b/src/types.ts index 9437cf27c85..b83b3ff783a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,7 @@ import { FirefoxPlaywright, HTTPManagementRoutes, HTTPRoutes, + Logger, Methods, Metrics, Request, @@ -99,7 +100,6 @@ abstract class Route { protected _browserManager: Browserless['browserManager'], protected _config: Browserless['config'], protected _fileSystem: Browserless['fileSystem'], - protected _debug: Browserless['debug'], protected _metrics: Browserless['metrics'], protected _monitoring: Browserless['monitoring'], protected _staticSDKDir: Browserless['staticSDKDir'], @@ -164,14 +164,6 @@ abstract class Route { */ config = () => this._config; - /** - * Helper function that loads the debug module, useful - * for logging messages scoped to the routes path. Defined - * and injected by browserless after initialization. - * @returns Debug - */ - debug = () => this._debug; - /** * Helper function that loads the file-system module * for interacting with file-systems. Defined and injected by @@ -254,6 +246,7 @@ export abstract class HTTPRoute extends BasicHTTPRoute { abstract handler: ( req: Request, res: http.ServerResponse, + logger: Logger, ) => Promise; } @@ -274,6 +267,7 @@ export abstract class BrowserHTTPRoute extends BasicHTTPRoute { abstract handler: ( req: Request, res: http.ServerResponse, + logger: Logger, browser: BrowserInstance, ) => Promise; @@ -298,6 +292,7 @@ export abstract class WebSocketRoute extends Route { req: Request, socket: stream.Duplex, head: Buffer, + logger: Logger, ) => Promise; } @@ -319,6 +314,7 @@ export abstract class BrowserWebsocketRoute extends Route { req: Request, socket: stream.Duplex, head: Buffer, + logger: Logger, browser: BrowserInstance, ): Promise;