Skip to main content

Parseable with NodeJS

· 5 min read
Surya Shakti

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

  1. What is logging?
  2. Using winston in a Node.js application
  3. 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 analysing 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 sturcture 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 insall 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 applicationand 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 transport 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

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 th efunction directly if you dont 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.')

Conslusion

Hurray!!🥳🥳🥳

You have successfuly 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.