Setting Up Logging in Your Node.js TypeScript App: A Quick Start Guide with TypeScript-Node Project and Pino Logging Library,Integrated with Logrotate
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
Node.js LTS Version 18.19.0 or above. If you don't have already, you can check nvm to install Node.js.
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:
OSX/MAC:Equivalent of logrotate for OSX
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! ๐
Subscribe to my newsletter
Read articles from Shubham Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Shubham Sharma
Shubham Sharma
I am a seasoned software engineer with over 5 years of experience, specializing in JavaScript technologies like Node.js and React.js. My passion for technology fuels my drive to continuously learn, adapt, and create impactful solutions that meet the needs of users and businesses. I excel in designing and developing high-performance, secure, and scalable components and APIs. Collaboration and clean code are at the core of my approach, ensuring that I deliver value to clients through best practices and a customer-centric mindset.