morgan-body icon indicating copy to clipboard operation
morgan-body copied to clipboard

Response body not logged when used with swagger generated nodejs

Open tgt87 opened this issue 3 years ago • 4 comments

Hi, I'm using morgan-body together with winston in my nodejs server code generated using swagger editor (https://editor.swagger.io/) but the response body doesn't seems to get logged. image

Postman result shows a response object being returned: image

Below is my implementation: index.js

'use strict';

var path = require('path');
var http = require('http');
var express = require('express');
var cors = require('cors')
const bodyParser = require("body-parser");
const morganBody = require('morgan-body');
var logger = require('./utils/logger')

var oas3Tools = require('oas3-tools');
var serverPort = process.env.PORT || 3002;

// swaggerRouter configuration
var options = {
    routing: {
        controllers: path.join(__dirname, './controllers')
    },
};

var expressAppConfig = oas3Tools.expressAppConfig(path.join(__dirname, 'api/openapi.yaml'), options);
// var app = expressAppConfig.getApp();

const openApiApp = expressAppConfig.getApp();

const app = express();

app.use(cors())
app.use(bodyParser.json({limit: '5mb'}));

const loggerStream = {
  write: message => {
    logger.info(message);
  },
};

morganBody(app, {
  noColors: true,
  prettify: false,
  stream: loggerStream,
  timezone: 'Asia/Singapore',
  dateTimeFormat: 'iso'
});


// workaround for middleware: https://github.com/bug-hunters/oas3-tools/issues/19
for (let i = 2; i < openApiApp._router.stack.length; i++) {
  app._router.stack.push(openApiApp._router.stack[i])
}

// Initialize the Swagger middleware
http.createServer(app).listen(serverPort, function () {
    console.log('Your server is listening on port %d (http://localhost:%d)', serverPort, serverPort);
    console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
});

logger.js

const winston = require('winston');
const fs = require('fs')
const DailyRotateFile = require('winston-daily-rotate-file');
const logDir = "./logs";

if (!fs.existsSync(logDir)) {
    fs.mkdirSync(logDir);
}

const timezoned = () => {
    return new Date().toLocaleString('en-GB', {
      timeZone: 'Asia/Singapore'
    });
};

const logFormat = winston.format.combine(
    winston.format.timestamp({format: timezoned}),
    winston.format.align(),
    winston.format.printf(
     info => `${info.timestamp} [${info.level.toUpperCase()}] ${info.message}`,
   )
);

const transport = new DailyRotateFile({
    filename: logDir + '/test-%DATE%.log',
    datePattern: 'YYYY-MM-DD',
    level: 'info',
   });   

const logger = winston.createLogger({
    format: logFormat,
    transports: [
         transport,
         new winston.transports.Console({
               level: 'info',}),
    ]});
    
module.exports = logger;

Any idea how to get the response body logged?

tgt87 avatar Nov 27 '21 14:11 tgt87

You have not shown your 'companies' route handler code to confirm, but I have noticed morgan-body only logs the response body for json responses if you use express' response.json(...) to generate it:

(req, res) => {
    res.json(...);
} 

If instead you manually build the response, like below, then morgan-body will NOT log the response body:

(req, res) => {
    res.writeHead(code, { 'Content-Type': 'application/json' });
    res.end(...);
} 

mlilley avatar Dec 28 '21 03:12 mlilley

Hi @mlilley, thanks for the clarification. The response is indeed the latter so I guess it expected that the response body won't be logged. Below is the controller code (Company.js) for reference:

var utils = require('../utils/writer.js');
var Company = require('../service/CompanyService');

module.exports.addCompany = function addCompany (req, res, next, body) {
  Company.addCompany(body)
    .then(function (response) {
      utils.writeJson(res, response);
    })
    .catch(function (response) {
      utils.writeJson(res, response, 500);
    });
};

writer.js

var ResponsePayload = function(code, payload) {
  this.code = code;
  this.payload = payload;
}

exports.respondWithCode = function(code, payload) {
  return new ResponsePayload(code, payload);
}

var writeJson = exports.writeJson = function(response, arg1, arg2) {
  var code;
  var payload;

  if(arg1 && arg1 instanceof ResponsePayload) {
    writeJson(response, arg1.payload, arg1.code);
    return;
  }

  if(arg2 && Number.isInteger(arg2)) {
    code = arg2;
  }
  else {
    if(arg1 && Number.isInteger(arg1)) {
      code = arg1;
    }
  }
  if(code && arg1) {
    payload = arg1;
  }
  else if(arg1) {
    payload = arg1;
  }

  if(!code) {
    // if no response code given, we default to 200
    code = 200;
  }
  if(typeof payload === 'object') {
    payload = JSON.stringify(payload, null, 2);
  }
  response.writeHead(code, {'Content-Type': 'application/json'});
  response.end(payload);
}

tgt87 avatar Dec 28 '21 15:12 tgt87

So it's not clear to me why morgan-body would require the use of .json() - perhaps this should be logged as a bug.

In the mean time, I suggest you replace your last 2 lines with response.status(code).json(payload); and remove any stringification of payload... and you should be good to go.

mlilley avatar Dec 28 '21 19:12 mlilley

thank you @mlilley, this is totally fair! Honestly morgan-body is something I originally published for myself to use across projects and it has since become way more popular than I could have imagined :)

right now I don't have time to work on it, but this would probably be an easy fix, appreciate you pitching in with helping answer issues

sirrodgepodge avatar Mar 15 '22 04:03 sirrodgepodge