Puppeteer Performance Monitoring with Inspector

Published on Valerio Barbera 5 min read
In this article I'll show you how to monitor performance and errors of a browser automation script written with Puppeteer.

Most things that you can do manually in the browser can be done programmatically using Puppeteer! But implementing browser automations in a server-side script makes it difficult to understand the resource consumption and performance implications of any step of the automation.

I’m Valerio, software engineer and CTO at Inspector, a tool that helps you find bugs and bottlenecks in your applications automatically. Before your customers stumble onto the problem.

One of the most useful things when it comes to monitoring is the ability to monitor not only the general execution performance, but build the timeline of all the individual tasks that make up the automation script.

Step 1: Create the project

To configure a new project create a new directory and run the init command inside it:

Terminal window
mkdir puppeteer-performance & cd puppeteer-performance & npm init

It will prompt you for input for a few aspects of the project, just press enter if you want to use the default values.

After completing the init process a package.json file will be generated and placed in the current directory.

screenshot_1.png

Step 2: Install Puppeteer

Run the command below to install Puppeteer in your project:

Terminal window
npm install puppeteer

During the installation process Puppeteer also automatically downloads a recent version of Chromium that will be used behind the scene to execute browser commands.

Step 3: Create the script

Now we can create the index.js file in the project directory. This is where we put the code to make screenshots with Puppeteer.

// Include Puppeteer
const puppeteer = require("puppeteer");
(async () => {
// Create a browser instance
const browser = await puppeteer.launch();
// Create a new page
const page = await browser.newPage();
// Set viewport width and height
await page.setViewport({ width: 1280, height: 720 });
// Open a URL
await page.goto("https://screenshotone.com", { waitUntil: "networkidle0" });
// Capture screenshot
await page.screenshot({ path: "screenshot.jpg" });
// Close the browser instance
await browser.close();
})();

Execute the code by running node index.js in your terminal and you should see the new file “screenshot.jpg” appearing in the project directory.

We are ready to integrate performance monitoring.

Step 4: Install Inspector for Nodejs

Run the command below to install the Inspector nodejs module:

Terminal window
npm install @inspector-apm/inspector-nodejs

As recommended in the official documentation, you should initialize the module at the beginning of the script. Before requiring any other module.

To get an Ingestion Key you have to register an account on Inspector and create a new project.

Other than the initializing the module I start the transaction at line 15. This line tell Inspector to start monitoring.

/*
* -------------------------------------------
* Initialize Inspector with the Ingestion Key.
* -------------------------------------------
*/
const inspector = require("@inspector-apm/inspector-nodejs")({
ingestionKey: "9cd5715238227ada16ed0f0e2e5323434ce1437b",
});
// Include Puppeteer
const puppeteer = require("puppeteer");
(async () => {
// Start a transaction
let transaction = inspector.startTransaction("puppeteer");
// Create a browser instance
const browser = await puppeteer.launch();
// Create a new page
const page = await browser.newPage();
// Set viewport width and height
await page.setViewport({ width: 1280, height: 720 });
// Open a URL
await page.goto("https://inspector.dev", { waitUntil: "networkidle0" });
// Capture screenshot
await page.screenshot({ path: "screenshot.jpg" });
// Close the browser instance
await browser.close();
transaction.setResult("success");
})();

After running the script you should see the new transaction coming in your dashboard:

screenshot_2.png

Now we are aware that the execution of the script takes 4.5 seconds and uses 18MB of memory. But… What is the impact of each statement inside the script?

Step 5: Enrich your timeline

The inspector library has a really simple API to add custom segments during a transaction. You can wrap any line of codes you write into the addSegment() method to monitor the impact they have within the execution flow.

Here is the script with segments:

/*
* -------------------------------------------
* Initialize Inspector with the Ingestion Key.
* -------------------------------------------
*/
const inspector = require('@inspector-apm/inspector-nodejs')({
ingestionKey: 'xxxxxxxxxxxxxxxxxxx'
});
// Include Puppeteer
const puppeteer = require('puppeteer');
(async () => {
// Start a transaction
let transaction = inspector.startTransaction("puppeteer");
// Create a browser instance
const browser = await inspector.addSegment(segment => {
return puppeteer.launch();
}, 'launch');
// Create a new page
const page = await inspector.addSegment(async segment => {
console.log('Create a new page');
return await browser.newPage();
}, 'new-page');
// Set viewport width and height
await page.setViewport({width: 1280, height: 720});
// Open a URL
await inspector.addSegment(async segment => {
await page.goto('https://screenshotone.dev', {waitUntil: 'networkidle0'});
}, 'goto', 'https://screenshotone.com
// Capture screenshot
await inspector.addSegment(async segment => {
await page.screenshot({path: 'screenshot.jpg'});
}, 'screenshot', true);
// Close the browser instance
await browser.close();
transaction.setResult('success');
})();

Execute the script again running the command node index.js in your terminal.

Now you can explore the execution details of each task in the script. And you will immediately see that the most impactful task is the opening of the URL.

screenshot_3.png

Step 6: Detect Errors

It would be even more useful to receive alerts in case an exception is thrown at runtime and execution fails. The tool and the library should allow you to catch these events.

Inspector detects unhandled errors by default. You can try adding a throw statement to simulate an error making the screenshot.

/*
* -------------------------------------------
* Initialize Inspector with the Ingestion Key.
* -------------------------------------------
*/
const inspector = require('@inspector-apm/inspector-nodejs')({
ingestionKey: '9cd5715238227ada16ed0f0e2e5323434ce1437b'
});
// Include Puppeteer
const puppeteer = require('puppeteer');
(async () => {
// Start a transaction
let transaction = inspector.startTransaction("puppeteer");
// Create a browser instance
{...}
// Create a new page
{...}
// Set viewport width and height
{...}
// Open a URL
{...}
// Capture screenshot (Throw Error)
throw new Error('Unable to write file.');
{...}
})();

You will immediately receive an alert via email and a complete report will be available in project dashboard.

screenshot_4.jpg

Conclusion

I hope the code snippets have been helpful for you to better understand what performance monitoring is in practical terms.

You can use this script to compare the performance of different headless browsers for example (like, Puppeteer or Selenium) and make a better decision about which one to use in your scripts.

If you want learn more about Inspector, visit the official website—inspector.dev.