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

I have a Express server and recently I would like to integrate Hexo blog framework into the existing Express.js site, for example, make the blog accessable on express_site/blog. While exploring the feasibility, I realise that it is hardly to find a thorough tutorial of how to achieve it. There are some information can be found on:
Hexo Github Issue 3644 and x-heco-app-connect middleware. However, none of them offer a complete guide of seamlessly integrating Hexo blog into an Express server. For example, following the instruction provided by x-heco-app-connect middleware, the app will generate issues regarding routing. Therefore, I decide to write this blog to offer a tutorial of integrating Hexo into Express, i.e. make a Hexo blog as a route of existing Express server.

Trulli
Hexo under localhost:3000/blog

My way of achieving it is based on x-hexo-app-connect and some of the steps describe blow are based on the instruction provided by x-hexo-app-connect.

Getting Started

1.1 Make a sub-directory under the express project directory. e.g. we make it under experss_proj/blog, use this command under the project directory:

1
$ mkdir blog

1.2 Enter the command to install hexo-cli globally

1
$ sudo npm i hexo-cli -g

1.3 Go to the blog’s directory (created in step 1.1) and enter the following command in terminal to init Hexo:

1
$ hexo init

1.4 In the blog directory, enter the command in terminal to install x-hexo-app-connect package:

1
$ npm i x-hexo-app-connect --save

1.5 Create a index.js file in the blog’s root directory. e.g. if you want to make it using a bash terminal (Mac platform), enter this command below:

1
$ touch index.js

1.6 Fill in the index.js with the following code:

  • for the return part, it is to config the Hexo blog, the route is the most important, that is where you will configure how the Hexo blog should be visited from the original Express server. In my case, the blog will be visited via route express_site/blog
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const Hexo = require('hexo');

    module.exports = (app) => {
    const blogDirPath = __dirname;
    const hexo = new Hexo(blogDirPath, {});
    // eslint-disable-next-line global-require
    return require('x-hexo-app-connect')(app, hexo, {
    // The Configs/Options
    log: false,
    compress: false,
    header: true,
    serveStatic: false,
    route: "/blog"
    });
    };
    1.7 In the app.js (the home js file of Express), add the following to use the x-hexo-app-connect in Express
    1
    2
    3
    4
    5
    6
    7
    const app = express();
    // Put it after app is initialized, as the app will be used as parameter
    const blogRouter = require('./blog/index')(app);

    ...
    app.use(blogRouter);

    1.8 This step is very important, it is to set “root” parameter in the _config.yml file under the root directory of Hexo. And set the root as /blog. If not set this root, the Hexo blog home page can be visited via express_site/blog, but when click the link of articles, categories, archives etc. the express will report 404 as the routes cannot be found
    1
    2
    3
    4
    # URL
    ## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
    url: http://localhost:3000/
    root: /blog # for locating themes static files (.css etc)
  • If do not set root as /blog, the following url will be generated as localhost:3000/all-categories which will lead to 404 error.
    Trulli
    ./blog/all-categories

1.9 Apply themes. You can just following the set up instruction of each specific theme, then it should be okay.
1.10 After all the above steps, run the Express server, if the terminal showes the following, then it means you are good:)

1
2
3
INFO  Validating config
INFO Start processing
INFO [Hexo-App-Connect] is running on route /blog

Hope this blog helps you in some way :)

Comment and share

  • page 1 of 1
Author's picture

Jingjie Jiang


Find a place I love the most