title: logger implementation using winston , morgan and winston-daily-rotate-file
body: <p>I am trying to implement a logger in node js which will create a new log file every day on a custom format for logs</p>
  
  <p>for this i have used three packages </p>
  
  <ol>
  <li><a href="https://www.npmjs.com/package/winston" rel="noreferrer">winston</a>    </li>
  <li><a href="https://www.npmjs.com/package/morgan" rel="noreferrer">morgan</a>  </li>
  <li><a href="https://www.npmjs.com/package/winston-daily-rotate-file" rel="noreferrer">winston-daily-rotate-file</a></li>
  </ol>
  
  <p>so the final output should every day a new log file should create in logs folder and it should log all the http(morgan logs) and typed logs (winston logs) into a single file in the below format</p>
  
  <p><strong>Date || Filename || statusCode || logMessage || uuid (for tracing)</strong></p>
  
  <p>eg: <strong>Fri Jan 18 2019 13:48:18 GMT+0530 (IST) || [index.js] || 200 || calling the new route || 287dccb0-1afa-11e9-88a0-dfb1c665be9d</strong></p>
  
  <p>so for this i have written three files index.js(root file of nodejs) logger.js(logger implementation and configuration) and logger.test.js(test cases for logger using jest)</p>
  
  <p>additional packages</p>
  
  <ol>
  <li><a href="https://www.npmjs.com/package/cors" rel="noreferrer">cors</a></li>
  <li><a href="https://www.npmjs.com/package/uuid" rel="noreferrer">uuid</a></li>
  <li><a href="https://www.npmjs.com/package/http-context" rel="noreferrer">http-context</a></li>
  <li><a href="https://www.npmjs.com/package/app-root-path" rel="noreferrer">app-root-path</a></li>
  <li><a href="https://www.npmjs.com/package/express-http-context" rel="noreferrer">express-http-context</a></li>
  <li><a href="https://www.npmjs.com/package/jest" rel="noreferrer">jest</a></li>
  </ol>
  
  <p>the problems that i have</p>
  
  <ol>
  <li>if i put a <strong>logger.error({message: {statusCode:200, logMsg: "the server will be starting in port 3000"}})</strong> in the <strong>index.js</strong> on <strong>app.listen</strong> before to console.log() the uuid is null </li>
  <li>the test cases that i have written is wrong, i am new to jest i just want to know how can i check all that cases. </li>
  <li>why when i test the suits uuid is null, how can i pass the uuid for test cases also</li>
  <li>how can i check whether new folder will be created and if already logs folder are there new file is created kind of test cases.</li>
  <li>How can i add other levels , info, debuge , warn based on the env. How can i improve this code to implement the logger functionality</li>
  </ol>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>// index.js
  
  const app = require('express')();
  const cors = require('cors')
  const morgan = require('morgan') // HTTP request logger middleware 
  const logger = require('./config/logger')(module) //Logger
  const uuid = require('uuid')
  const httpContext = require('express-http-context')
  
  // Use any third party middleware that does not need access to the context here
  // app.use(some3rdParty.middleware);
  
  app.use(httpContext.middleware);
  // all code from here on has access to the same context for each request
  
  // Run the context for each request.
  // Assigning a unique identifier to each request
  app.use((req, res, next) =&gt; {
    httpContext.set('reqId', uuid.v1());
    next()
  })
  
  
  // using morgan with winston(logger)
  app.use(morgan('combined', {
    stream: {
      write: (message) =&gt; logger.error(message)
    }
  }))
  
  
  app.use(cors());
  
  app.use("/new", (req, res) =&gt; {
    logger.error({
      message: {
        statusCode: 400,
        logMsg: "hitting new route"
      }
    })
    nextLayer(res)
  })
  
  const nextLayer = (res) =&gt; {
    logger.error({
      message: {
        statusCode: 400,
        logMsg: "hitting in nextLayer function"
      }
    })
    res.send("OK")
  }
  
  app.listen(4000, () =&gt; {
    console.log('Server running on port 4000');
  })
  
  
  
  
  
  // Logger.js
  
  const appRoot = require('app-root-path')
  const {
    createLogger,
    format,
    transports
  } = require('winston')
  const {
    combine,
    timestamp,
    label,
    printf
  } = format
  const path = require('path')
  require('winston-daily-rotate-file');
  const httpContext = require('express-http-context')
  
  /**
   * @method checkMessageProp
   * @param {message} can be object if developer defined, else it will be string
   *                  if its a network request
   * @returns a fixed format how the status code and message should show
   */
  const checkMessageProp = (message) =&gt; {
    switch (typeof message) {
      case "object":
        const {
          statusCode,
          logMsg
        } = message
        return `${statusCode ? statusCode : "Not Defined"} || ${logMsg ? logMsg : "Not Defined"}`;
      case "string":
        let messageSplit = message.split(`"`)
        var message = messageSplit ? `${messageSplit[2].trim().split(" ")[0]} || ${messageSplit[1]}` : null
        return message
      default:
        return message
    }
  }
  
  /**
   * @method customFormat
   * @param {log} the log passed by the developer or based on network requests
   * @returns a customFormat how it should be logged to the log files
   */
  const customFormat = printf(log =&gt; {
    const now = new Date();
    const reqId = httpContext.get('reqId');
    return `${log.timestamp ? new Date(log.timestamp) : now} || [${log.label}] || ${checkMessageProp(log.message)} || ${reqId ? reqId : null}`
  });
  
  /**
   * @method getFileName
   * @param {moduleObj} the module realted object passed from the require of logger file 
   * @returns the file name where the logger was invoked
   */
  const getFileName = moduleObj =&gt; {
    if (Object.keys(moduleObj).length &gt; 0) {
      let parts = moduleObj.filename.split(path.sep)
      return parts.pop()
    } else {
      return "Module not passed while requiring the logger"
    }
  }
  
  // Custom settings for each transport 
  const options = moduleObj =&gt; {
    return {
      dailyRotateFile: {
        filename: `${appRoot}/logs/TPS-UI-%DATE%.log`,
        datePattern: 'YYYY-MM-DD',
        prepend: true,
        level: "error",
        timestamp: new Date(),
        localTime: true
      }
    }
  }
  
  // Instantiate a Winston Logger with the settings
  let logger = moduleObj =&gt; {
    return createLogger({
      format: combine(
        label({
          label: getFileName(moduleObj)
        }),
        customFormat
      ),
      transports: [
        new transports.DailyRotateFile(options(moduleObj).dailyRotateFile)
      ],
      exitOnError: false // do not exit on handled exceptions
    })
  }
  
  
  
  module.exports = logger
  
  
  
  
  
  
  
  
  // logger.test.js
  
  const logger = require('./logger')
  
  beforeEach(() =&gt; {
    mockLoggerMessageObject = {
      message: {
        statusCode: 400,
        logMsg: "Calling in test suite"
      }
    }
    mockLoggerMessageString = `::ffff:127.0.0.1 - - [18/Jan/2019:04:50:57 +0000] 
                                 "GET /new HTTP/1.1" 200 2 "http://localhost/" "Mozilla/5.0 
                                 (linux) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/11.12.0"`
    mockLoggerMessageNumberFormat = 123
    mockLoggerMessageArrayFormat = ["data", "test", 123]
  })
  
  describe(`Logger test cases`, () =&gt; {
    test('should invoke the logger function with the mock Logger message object', () =&gt; {
      expect(logger(module).error(mockLoggerMessageObject)).toBeDefined()
    })
    test(`should invoke the logger function with empty object`, () =&gt; {
      expect(logger(module).error({})).toBeDefined()
    })
    test(`should invoke the logger function without any module object`, () =&gt; {
      expect(logger({}).error(mockLoggerMessageObject)).toBeDefined()
    })
    test(`should invoke the logger function without any module and message object`, () =&gt; {
      expect(logger({}).error({})).toBeDefined()
    })
    test(`should invoke the logger function with the http request`, () =&gt; {
      expect(logger(module).error(mockLoggerMessageString)).toBeDefined()
    })
    test(`should invoke the logger function with the number format`, () =&gt; {
      expect(logger(module).error(mockLoggerMessageNumberFormat)).toBeDefined()
    })
    test(`should invoke the logger function with the array format`, () =&gt; {
      expect(logger(module).error(mockLoggerMessageArrayFormat)).toBeDefined()
    })
  
  })</code></pre>
  </div>
  </div>
  </p>