Logging for your Node.js app
Tags: javascript, node, backend, webdev
Published: 2022-09-22 07:03:31


## Introduction

In this article we will learn how we can store logs of **Node.js** application into **parseable** using [Winston](https://www.npmjs.com/package/winston) library. We'll look at what is logging and why it is important.

## Table of contents

1. What is logging?
1. Using winston in a Node.js application
1. 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.

```bash
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.  

```js
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

```js
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**
>
>> ```js 
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**
>
>> ```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**
>
>> ```js 
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**
>
>> ```js 
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.

```jsx 
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.