Recently, I need to do load tests for a set of REST APIs under Node.js environment. After studying some online articles, I decided to give it a go with AutoCannon. My scenario is that I have already had a sets of requests setup in Postman, and I do not want to rewrite everything for the load test. Luckily, I found a solution that exactly match my requirement (listed below), but I still want to write my own version from a AutoCannon fresher’s perspective and hopefully will be useful for future readers.

Step.1 Export A Collection of Requests from Postman

As shown below, left click the “…” button at the right side of the requests collection. Then choose “Export” in the popup menu.

Trulli
Fig.1 - Export Requests Collection
Afer this step, we should receive a JON file contains all the information of the REST APIs we would like to test.

Step.2 Write Code for Load Testing

We need to create a sepearte xxx.js file that tells AutoCannon what to do.

  • Load request data from exported JSON file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const autocannon = require('autocannon');
const fs = require('fs/promises');

// read array of items from exported .json file from postman
let entries = undefined;

async function getRequests() {
const data = await fs.readFile(
'./youtube_clone_proj.postman_collection.json',
'UTF-8'
);

entries = JSON.parse(data).item;
return true;
}
  • Set up AutoCannon for Each Request
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
entries.map(async (entry) => {
// there are multi request in item
entry.item.filter((ele) => {
// filter the empty request
return ele.request.url !== undefined;
}).map(async (ele) => {
console.log(ele.request.method + " " + ele.request.url.raw);
const result = await autocannon({
url: ele.request.url.raw,
method: ele.request.method,
connections: 100,
workers: 50,
duration: 5,
body: ele.request.body === undefined? null : JSON.stringify(ele.request.body),
// read other options here: https://github.com/mcollina/autocannon#autocannonopts-cb
}, finishedBench);

// track process
autocannon.track(result, {renderProgressBar: false});

// this is used to kill the instance on CTRL-C
process.once('SIGINT', () => {
result.stop()
})

function finishedBench (err, res) {
console.log('finished bench', err, res)
}
});

Launch the test

In the terminal window, run the following

1
node xxx.js

Then we should able to see output like this for each invidual request:

Trulli
Fig.2 - API1 result
Trulli
Fig.3 - API2 result
For sure, there are more details left to discover, e.g. settings of _autocannon_, but that is left for reading and searching the official document :)

reference

Benchmark express apis with autocannon from postman collection

Comment and share

Using Winston Logger in Node.js

in Technology, Nodejs Sitevisits: -- Pageviews: --

Winston logger is commonly used logger in Node.js. Most of the case, I use winston logger and also integration outputs of morgan into winston. I recalled that the first time I used winston, I followed some of the online tutorials regarding how to config and use it, but there are always problems here and there for my scenarios. Therefore, I decide to write this to blog to introduce how I config winston logger in my projects.

Project Structure

I normally put the configuration file of winston logger like the following:

1
2
3
4
5
6
7
8
9
10
Project
| ...
├───middleware
├───models
├───config
│ ├───winston.js
│ ...
├───app.js
├───package.json
| ...

Then in *.js files, it will use the winston config file like the following:

1
2
3
4
const winstonLogger = require('./config/winston');
...
winstonLogger.error(`${err.status || 500} - ${err.message} - ${req.originalUrl} - ${req.method} - ${req.ip}`);
...

Winston Configuration File Explained

The conent of the configuration file winston.js mentioned in the previous section is shown below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
const appRoot = require('app-root-path');
const winston = require('winston');

const { format, transports } = winston;
const path = require('path');

const colors = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'white',
};

winston.addColors(colors);

// define the custom settings for each transport (file, console)
const logger = winston.createLogger({
level: 'http',
format: format.combine(
format.timestamp({
format: 'YYY-MM-DD HH:mm:ss',
}),
format.errors({ stack: true }),
format.splat(),
format.printf(
(info) => `${info.timestamp} ${info.level}: ${info.message}`,
),
format.json(),
),
defaultMeta: { service: 'quickpost' },
transports: [
new transports.File({ filename: path.join(appRoot.toString(), '/logs/error.log'), level: 'error' }),
new transports.File({ filename: path.join(appRoot.toString(), '/logs/combined.log') }),
],
});

if (process.env.NODE_ENV !== 'production') {
logger.add(new transports.Console({
format: format.combine(
// print all the message colored
format.colorize({ all: true }),
format.printf(
(info) => `${info.timestamp} ${info.level}: ${info.message}`,
),
format.simple(),
),
}));
}

logger.stream = {
write: (message) => logger.http(message),
};

module.exports = logger;

To explain the above sourcecode in details:

Set Colors for Logger Levels

1
2
3
4
5
6
7
8
9
10
const colors = {
error: 'red',
warn: 'yellow',
info: 'green',
http: 'magenta',
debug: 'white',
};

winston.addColors(colors);

Config Other Settings of Winston

For the following settings, please refer to the official site of winston logger. I would emphasize two things:

  • On the transports part. In the following configuration, I put loggers into files under the directory /log/*.log files. That is a very basic settings, you can of course adding settings like, generate new log files after the current log file reach to certain size, and further config organizing log files according to their generated date.

  • If not in “production” environment, I set the logger still output on the console for facilitating debugging.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// define the custom settings for each transport (file, console)
const logger = winston.createLogger({
level: 'http',
format: format.combine(
format.timestamp({
format: 'YYY-MM-DD HH:mm:ss',
}),
format.errors({ stack: true }),
format.splat(),
format.printf(
(info) => `${info.timestamp} ${info.level}: ${info.message}`,
),
format.json(),
),
defaultMeta: { service: 'quickpost' },
transports: [
new transports.File({ filename: path.join(appRoot.toString(), '/logs/error.log'), level: 'error' }),
new transports.File({ filename: path.join(appRoot.toString(), '/logs/combined.log') }),
],
});

if (process.env.NODE_ENV !== 'production') {
logger.add(new transports.Console({
format: format.combine(
// print all the message colored
format.colorize({ all: true }),
format.printf(
(info) => `${info.timestamp} ${info.level}: ${info.message}`,
),
format.simple(),
),
}));
}

Merge Morgan into Winston

The following code will merge morgan into winston.

1
2
3
logger.stream = {
write: (message) => logger.http(message),
};
Config Morgan

To make winston working with morgan, we will need to add the following morgan settings:

  • File directory:
    1
    2
    3
    4
    5
    6
    7
    8
    Project
    | ...
    ├───middleware
    │ ├───morgan.js
    │ ...
    ├───app.js
    ├───package.json
    | ...
  • Config morgan:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    const morgan = require('morgan');
    const logger = require('../config/winston');

    const morganMiddleware = morgan(
    // Define message format string (this is the default one).
    // The message format is made from tokens, and each token is
    // defined inside the Morgan library.
    // You can create your custom token to show what do you want from a request.
    'combined',
    // Options: in this case, I overwrote the stream and the skip logic.
    // See the methods above.
    { stream: logger.stream },
    );

    module.exports = morganMiddleware;

  • Use in app.js
    1
    2
    3
    4
    5
    6
    ...
    const morganMiddleware = require('./middleware/morgan');
    ...
    app.use(morganMiddleware);
    ...

If things go well …

You should be able to see logger in the console like the following:
Image

And also the same records can be find in the log files: /log/*.log files.

Comment and share

  • page 1 of 1
Author's picture

Jingjie Jiang


Find a place I love the most