Introduction
In this article we will learn how we can store logs of Node.js application into parseable using Winston library. We'll look at what is logging and why it is important.
Table of contents
- What is logging?
- Using winston in a Node.js application
- sending those logs to the parseable
What is logging?
Logging is the process of recording application events and data to a log file or any other sources, for the purpose of analyzing the system.
Logs help developers to find the error, track the source of the bug and fix it. In Node.js it is critical to structure the application logs. when we are at the development phase we can use console.log
to find the problems and to get the information you need.
But once the application is in production we cannot use the console.log
any more.
Using winston in a Node.js application
I assume you have a node.js application, now to install winston & winston-transport in your project run the following commands.
npm install winston
npm install winston-transport
Now our aim is to replace all the console
messages from the application with the winston logger. So Just for example if you have these three event logs in your node.js application which you want to store in parseable using winston library.
connsole.warn('Got mutiple elements with same id');
console.error('Login Failed. Invalid ID');
console.info('Events posted successfully.');
Configuring Winston in Node.js app
Create a 'logger' folder in the root directory where we will configure winston and send the logs to parseable.
Now in logger/index.js
add following code
const winston = require("winston");
const { combine, timestamp, label, printf } = winston.format;
const Transport = require("winston-transport");
var axios = require("axios");
const myFormat = printf(({ level, message, label, timestamp }) => {
return JSON.stringify({
timestamp: timestamp,
level: level,
message: message,
});
});
class CustomTransport extends Transport {
constructor(opts) {
super(opts);
}
log(info, callback) {
console.info(info);
var data = JSON.stringify([info]);
var config = {
method: "post",
url: `https://demo.parseable.io/api/v1/logstream/${streamName}`,
headers: {
"X-P-META-Tag": "Owner",
"X-P-META-Tag": "Host",
"X-P-META-Tag": "Host.owner",
Authorization: `Basic ${key}`,
"Content-Type": "application/json",
},
data: data,
};
axios(config)
.then(function (response) {
console.log(response.data);
})
.catch(function (error) {
console.log(error);
});
callback();
}
}
const devLogger = () => {
const transport = new CustomTransport({});
return winston.createLogger({
level: "debug",
format: combine(label(), timestamp(), myFormat),
transports: [transport],
});
};
let logger = null;
if (process.env.NODE_ENV !== "production") {
logger = devLogger()
}
module.exports = logger;
Here we are configuring winston in our node.js application and sending the logs to parseable.
Let's discuss this code in parts for better understanding.
Initializing winston logger instance
const devLogger = () => { const transport = new CustomTransport({}); return winston.createLogger({ level: "debug", format: combine(label(), timestamp(), myFormat), transports: [transport], }); };
The snippet above contains the initialization of a Winston logger instance. Here we specify the log level for this specific logger instance using the npm log level standard, format in which logs will be stored and
that specifies where the logs data will go. In our case we will send it to the parseable.
> **Setting custom format of the log data**
>
>>
```js
const myFormat = printf(({ level, message, label, timestamp }) => {
return JSON.stringify({
timestamp: timestamp,
level: level,
message: message,
});
});
The snippet above specifies the format of the log data in which it will be stored.
Sending the log data to parseable
class CustomTransport extends Transport { constructor(opts) { super(opts); } log(info, callback) { console.info(info); var data = JSON.stringify([info]); var config = { method: "post", url: `https://demo.parseable.io/api/v1/logstream/${streamName}`, headers: { "X-P-META-Tag": "Owner", "X-P-META-Tag": "Host", "X-P-META-Tag": "Host.owner", Authorization: `Basic ${key}`, "Content-Type": "application/json", }, data: data, }; axios(config) .then(function (response) { console.log(response.data); }) .catch(function (error) { console.log(error); }); callback(); } }
The snippet above is responsible for sending the log data to the parseable. here streamName
is the log stream you have created in parseable. and the key
is the authentication key for accessing the parseable.
calling the logger function
let logger = null; if (process.env.NODE_ENV !== "production") { logger = devLogger() }
You may not want to store the logs in parseable in development phase then you can see them on console to test it and when in production mode you can send it to parseable. The snippet above calls the logger function according to your requirements. you can also call the function directly if you don't want this conditional calling.
Then we can use logger
instead of console
in our application.
const logger = require('./logger')
logger.warn('Got mutiple elements with same id')
logger.error('Login Failed. Invalid ID')
logger.info('Events posted successfully.')
Conclusion
Hurray!!🥳🥳🥳
You have successfully integrated parseable with your node.js application. now you can run your application and all the events you have replaced with logger
will be posted to parseable and you can check in the logstream
you created earlier.