Setting Up Logging in Your Node.js TypeScript App: A Quick Start Guide with TypeScript-Node Project and Pino Logging Library,Integrated with Logrotate

Set up log handling in your Node.js app with TypeScript-Node, Pino Logging, and Logrotate โ€“ a quick and easy guide!

ยท

6 min read

In this Quick Start guide, I'll guide you through the process of establishing a logging system for your Node.js TypeScript application. For this demonstration, we'll employ the typescript-nodejs-project as the foundation of our Node.js TypeScript project, and leverage the powerful pino logging library. To ensure seamless log management, we'll integrate Logrotate with a CRON Job for automated log rotation.

Perquisite

  1. Node.js LTS Version 18.19.0 or above. If you don't have already, you can check nvm to install Node.js.

  2. Code Editor Visual Studio Code.

GitHub Repo typescript-node-project-with-logging

Now, let's dive into the setup and steps needed to add logging to your Node.js TypeScript application. Follow these steps one by one (make sure not to skip any). If you encounter any issues or errors, please let me know in the comments section below.

Configure the typescript-nodejs-project GitHub Repository

Before we proceed, make sure you've set up the typescript-nodejs-project GitHub repository. If you haven't done this yet, you can refer to my previous blog on setting up a Node.js TypeScript project. To get started, clone the GitHub repository by running the following commands in your terminal:

git clone https://github.com/shubham-sharmas/typescript-node-project.git
cd typescript-node-project
git checkout feature/pino-logging

We've set up a dedicated branch for the pino-logging feature named feature/pino-logging. Make sure to use the latest branch checkout command to switch to this branch.

Set Up and Configure the Pino Logging Library

To set up the pino library in the project, simply run the following command to install the pino logging library from npmjs.com/package/pino:

npm i pino pino-pretty @types/pino

Once you have the pino library installed, you can proceed to set up the necessary configuration for pino logging. Simply create a dedicated file for the pino logger, named pino-logger.ts, within the src/utils/ directory.

Add the below code inside your pino-logger.ts file:

import fs from 'fs';
import { join, resolve } from 'path';
import { default as Pino } from 'pino';

const logDirectory = join(resolve(`${__dirname}/../../`), 'logs');

if (!fs.existsSync(logDirectory)) {
  fs.mkdirSync(logDirectory);
}

const logFile = `${logDirectory}/app.log`;
const transport = Pino.transport({
  targets: [
    {
      level: 'info',
      target: 'pino/file',
      options: {
        destination: logFile,
      },
    },
    ...(process.env.NODE_ENV !== 'production'
      ? [
          {
            ...{
              level: 'info',
              target: 'pino-pretty',
              options: {
                colorizeObjects: true, //--colorizeObjects
                messageFormat: true, // --messageFormat
                timestampKey: 'time', // --timestampKey
                // include: 'level,time', // --include
                translateTime: `UTC:yyyy-mm-dd'T'HH:MM:ss'Z'`,
              },
            },
          },
        ]
      : []),
  ],
});

const pinoLogger = Pino(transport);
export default pinoLogger;

Let's break down the code to understand how pino logging works. Firstly, we import the necessary Node.js and pino module.

 import fs from 'fs';
 import { join, resolve } from 'path';
 import { default as Pino } from 'pino';

After creating the log file directory and ensuring its existence, we append a log file named app.log to the log directory.

const logDirectory = join(resolve(`${__dirname}/../../`), 'logs');

if (!fs.existsSync(logDirectory)) {
  fs.mkdirSync(logDirectory);
}

const logFile = `${logDirectory}/app.log`;

Now that we have the log file in hand, the next step is to create a Pino Transport. This is a crucial aspect of the Pino configuration file, where we are utilizing two types of transport targets:

  • pino/file transport direct logs to a file.

  • pino-pretty transport prettifies logs and logs to STDOUT(console).

In pino-pretty transport, we have included various options in pino-pretty for formatting output.

If you've noticed, we've included a check, process.env.NODE_ENV !== 'production'. It's an important condition that improves the app's performance by preventing log printing to the console in the production environment.

const transport = Pino.transport({
  targets: [
    {
      level: 'info',
      target: 'pino/file',
      options: {
        destination: logFile,
      },
    },
    ...(process.env.NODE_ENV !== 'production'
      ? [
          {
            ...{
              level: 'info',
              target: 'pino-pretty',
              options: {
                colorizeObjects: true, //--colorizeObjects
                messageFormat: true, // --messageFormat
                timestampKey: 'time', // --timestampKey
                // include: 'level,time', // --include
                translateTime: `UTC:yyyy-mm-dd'T'HH:MM:ss'Z'`,
              },
            },
          },
        ]
      : []),
  ],
});

For more details on pino transport, follow this link: pino transports

Now open src/server.ts file and add pinoLogger.info() log with message in app.listen() function, Don't forget to import pinoLogger:

// `src/server.ts`
import pinoLogger from './utils/pino-logger';
...
app.listen(port, () => {
  pinoLogger.info(`PINO_LOGGER_PRINT: Server is listening on port ${port}`);
  console.log(`CONSOLE_PRINT: Server is listening on port ${port}`);
});

Execute the application by first installing the node_modules and then running command below:

npm i
npm run local

Now you can see the pino logs on the console and in file(/typescript-nodejs-project/logs/app.log).

Configuring and Setting Up Logrotate

As the application continues to generate logs, we need a way to rotate and compress the old logs. This is where Logrotate plays an important role. As the name implies, it's a tool used to rotate log files. Let's install Logrotate on Ubuntu:

sudo apt update
sudo apt install logrotate
logrotate --version

For other operating systems, you can check the links below:

Next, create a new file typescript-node-project inside the directory /etc/logrotate.d/typescript-node-project and add the Logrotate configuration:

```
$HOME/typescript-node-project/logs/*.log
{
   su USER GROUP
   hourly
   maxsize 2k
   delaycompress
   rotate 10
   compress
   notifempty
   missingok
   copytruncate
}
```

The first line contains the log file path. Add your log file path, ending with *.log. Inside the curly braces, on the first line, please specify the USER and GROUP after the su USER GROUP command. To find the user and group, go to the log file directory and run below command(check the attached screenshot):

ls -l

To execute the file, run the following command:

sudo logrotate -f -v /etc/logrotate.d/typescript-node-project

For now, no log file will be rotated as we have specified the maxsize 2k. It will not rotate until the file size reaches 2 KB. So, let's fill the log file to test logrotate configuration. Just add below code inside src/utils/pino-logger.ts before export statement and save the file:

for (let index = 0; index < 100000; index++) {
  pinoLogger.info(`Log File >>> ${logFile}`);
}

Again, execute the logrotate file, run the following command:

sudo logrotate -f -v /etc/logrotate.d/typescript-node-project

Now, you will see that a new file is generated inside typescript-node-project/logs/ directory with name app.log.1. Again, if you generate more logs and repeat the process of running the logrotate command, then you will see an app.log.2.gz file.

Setting Up and Configuring CRON Job

As we are using the logrotate command to manually execute the logrotate configuration file each time. We need a way to automate this process based on the time configuration. For this task, we can utilize the cron job in Linux.

Create a new file logrotate-typescript-node-project-job inside /etc/cron.d/logrotate-typescript-node-project-job that will contain the cron rule and logroate command. Copy the content below into the file:

*/1 * * * * USER /usr/sbin/logrotate -f /etc/logrotate.d/typescript-node-project

This file will run every minute. The cron rule: */1 * * * * is used to specify that in the configuration file. Specify the root user in place of the user(as we previously did in the logrotate configuration).

Now, if everything is works as expected, you will see the log files being generated by logroate in /src/logs/ directory.

You can also watch the cron job logs using the following command:

tail -f  cron /var/log/syslog

tail is utility to display end of file, and with -f flag we are following the logs.

GitHub Repo typescript-node-project-with-logging


In this Quick Start guide, we've walked through setting up a logging system for your Node.js TypeScript application. Using the typescript-nodejs-project and the pino logging library. To keep things organized and efficient, we've integrated Logrotate with a scheduled CRON Job for automated log rotation. This ensures that your logs are well-managed, making troubleshooting and maintenance more straightforward.

If you find yourself encountering any challenges along the way, don't hesitate to do some quick googling. Should you still be stuck, feel free to drop a comment below, and I'll be more than happy to assist. Give a ๐Ÿ‘ if you found this guide helpful. Happy coding! ๐Ÿ˜Š

ย