快速测井响应体快速测井响应体

标题应该是不言而喻的。

出于调试的目的,我希望 Express 为每个服务的请求打印响应代码和正文。打印响应代码非常容易,但是打印响应主体则比较棘手,因为似乎响应主体并不容易作为属性使用。

以下内容不起作用:

var express = require('express');
var app = express();


// define custom logging format
express.logger.format('detailed', function (token, req, res) {
return req.method + ': ' + req.path + ' -> ' + res.statusCode + ': ' + res.body + '\n';
});


// register logging middleware and use custom logging format
app.use(express.logger('detailed'));


// setup routes
app.get(..... omitted ...);


// start server
app.listen(8080);

当然,我可以很容易地在发出请求的客户端打印响应,但是我更喜欢在服务器端打印响应。

PS: 如果有帮助的话,我所有的回复都是 json,但是希望有一个解决方案可以用于一般的回复。

102663 次浏览

不确定这是否是最简单的解决方案,但是您可以编写一个中间件来拦截写入响应的数据。确保禁用 app.compress()

function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;


var chunks = [];


res.write = function (chunk) {
chunks.push(chunk);


return oldWrite.apply(res, arguments);
};


res.end = function (chunk) {
if (chunk)
chunks.push(chunk);


var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);


oldEnd.apply(res, arguments);
};


next();
}


app.use(logResponseBody);

您可以使用 Express-Winston并使用以下方法进行配置:

expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');

咖啡中的例子脚本:

expressWinston.requestWhitelist.push('body')
expressWinston.responseWhitelist.push('body')
app.use(expressWinston.logger({
transports: [
new winston.transports.Console({
json: true,
colorize: true
})
],
meta: true, // optional: control whether you want to log the meta data about the request (default to true)
msg: "HTTP \{\{req.method}} \{\{req.url}}", // optional: customize the default logging message. E.g. "\{\{res.statusCode}} \{\{req.method}} \{\{res.responseTime}}ms \{\{req.url}}"
expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
}));

我发现这个问题最简单的解决方案是在发送响应时向 res 对象添加一个 body属性,日志记录器以后可以访问这个属性。我将它添加到我自己的名称空间中,我对 req 和 res 对象进行维护,以避免命名冲突。例如:。

res[MY_NAMESPACE].body = ...

我有一个实用工具方法,可以将所有响应格式化为标准化的 API/JSON 响应,因此添加这一行将在 res 的 onFinished事件触发日志记录时暴露响应主体。

我使用 Laurent 建议的方法遇到了一个问题。有时块是一个字符串,因此在调用 Buffer.concat ()时会出现问题。不管怎样,我发现了一个小小的修改:

function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;


var chunks = [];


res.write = function (chunk) {
chunks.push(new Buffer(chunk));


oldWrite.apply(res, arguments);
};


res.end = function (chunk) {
if (chunk)
chunks.push(new Buffer(chunk));


var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);


oldEnd.apply(res, arguments);
};


next();
}


app.use(logResponseBody);

以上接受的代码与 ES6有问题。 使用下面的代码

function logReqRes(req, res, next) {
const oldWrite = res.write;
const oldEnd = res.end;


const chunks = [];


res.write = (...restArgs) => {
chunks.push(Buffer.from(restArgs[0]));
oldWrite.apply(res, restArgs);
};


res.end = (...restArgs) => {
if (restArgs[0]) {
chunks.push(Buffer.from(restArgs[0]));
}
const body = Buffer.concat(chunks).toString('utf8');


console.log({
time: new Date().toUTCString(),
fromIP: req.headers['x-forwarded-for'] ||
req.connection.remoteAddress,
method: req.method,
originalUri: req.originalUrl,
uri: req.url,
requestData: req.body,
responseData: body,
referer: req.headers.referer || '',
ua: req.headers['user-agent']
});


// console.log(body);
oldEnd.apply(res, restArgs);
};


next();
}


module.exports = logReqRes;

我其实做了这个漂亮的小 npm 来解决这个确切的问题,希望你喜欢它!

Https://www.npmjs.com/package/morgan-body

morgan-body in action!

这可能会帮助某人谁正在寻找获得响应记录 因此,我们使用中间件在服务到客户端之前拦截请求。然后,如果我们使用 res.send 方法发送数据,则重写中间件中的方法,并确保控制台记录主体。如果您计划单独使用 res.send,那么这应该可以正常工作,但是如果您使用 res.end 或 res.sendFile,那么覆盖这些方法并只记录所需的内容(显然,为了性能目的,不应该记录整个文件八位流。

这里我使用 pino 作为日志记录器。

//LoggingResponseRouter.js

var loggingResponseRouter = require('express').Router();
var loggingService = require('./../service/loggingService');
var appMethodInstance = require('./../constants/appMethod');
var path = require('path');
var fs = require('fs');
var timeZone = require('moment-timezone');
var pino = require('pino')();




loggingResponseRouter.use((req, res, next) => {


// set the fileName it needs to log
appMethodInstance.setFileName(__filename.substring(__filename.lastIndexOf(path.sep) + 1, __filename.length - 3));
//loggingService.debugAndInfolog().info('logging response body', appMethodInstance.getFileName());
let send = res.send;
res.send = function(body){
loggingService.debugAndInfolog().info('Response body before sending: ', body);
send.call(this, body);
}
next();
});
module.exports = loggingResponseRouter;

主文件-Main.js

const corsRouter = require('./app/modules/shared/router/corsRouter');
const logRequestRouter = require('./app/modules/shared/router/loggingRequestRouter');
const loggingResponseRouter = require('./app/modules/shared/router/loggingResponseRouter');
const express = require('express');
var path = require('path');
const app = express();




// define bodyparser middleware
const bodyParser = require('body-parser');


const port = process.env.PORT || 3000;


// Now use the middleware prior to any others
app.use(bodyParser.json());
// use this to read url form encoded values as wwell
app.use(bodyParser.urlencoded({extended:true}));
console.log('before calling cors router in main js');
app.use(corsRouter);
app.use(logRequestRouter);
app.use(loggingResponseRouter);




app.get('/api', (req, res) => {
console.log('inside api call');
res.send('aapi');
});




app.listen(port, () => {
console.log('starting the server');
});

这是 loggingService-loggingService.js

var pino = require('pino');
var os = require('os');
var appMethodInstance = require('./../constants/appMethod');
var pinoPretty = require('pino-pretty');
var moment = require('moment');
var timeZone = require('moment-timezone');




class Logger{
constructor(){
this.appName = 'Feedback-backend';
this.filenameval = '';


}


getFileName(){
console.log('inside get filename');
console.log(appMethodInstance.getFileName());
if(appMethodInstance.getFileName() === null || appMethodInstance.getFileName() === undefined){
this.filenameval = 'bootstrapping...'
}else {
this.filenameval = appMethodInstance.getFileName();
}
console.log('end');
return this.filenameval;
}


debugAndInfolog(){
return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),


hostName: os.hostname()
},
level: 'info',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
messageKey: 'logMessage',
prettyPrint: {
messageKey: 'logMessage'
}
});
}


errorAndFatalLog(){


return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
hostName: os.hostname()
},
level: 'error',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
prettyPrint: {
messageKey: 'FeedbackApp'
}
});
}
}








module.exports = new Logger();

基于 Laurent 的 回答的解决方案:

import { NextFunction, Request, Response } from 'express-serve-static-core';
//...


app.use(logResponseBody);


function logResponseBody(req: Request, res: Response, next: NextFunction | undefined) {
const [oldWrite, oldEnd] = [res.write, res.end];
const chunks: Buffer[] = [];


(res.write as unknown) = function(chunk) {
chunks.push(Buffer.from(chunk));
(oldWrite as Function).apply(res, arguments);
};


res.end = function(chunk) {
if (chunk) {
chunks.push(Buffer.from(chunk));
}
const body = Buffer.concat(chunks).toString('utf8');
console.log(new Date(), `  ↪ [${res.statusCode}]: ${body}`);
(oldEnd as Function).apply(res, arguments);
};
if (next) {
next();
}
}

大多数的建议似乎有点像大锤,今晚花了一些时间在这个问题上,并写下了我的发现后,挖掘了几本书,以帮助制作一些定制的东西。

//app.js
...
app.use(requestLoggerMiddleware({ logger: console.log }));


app.get(["/", "/api/health"], (req, res) => {
res.send({ message: "OK", uptime: process.uptime() });
...
});
// middleware.js
/**
* Interceptor function used to monkey patch the res.send until it is invoked
* at which point it intercepts the invokation, executes is logic such as res.contentBody = content
* then restores the original send function and invokes that to finalize the req/res chain.
*
* @param res Original Response Object
* @param send Original UNMODIFIED res.send function
* @return A patched res.send which takes the send content, binds it to contentBody on
* the res and then calls the original res.send after restoring it
*/
const resDotSendInterceptor = (res, send) => (content) => {
res.contentBody = content;
res.send = send;
res.send(content);
};


/**
* Middleware which takes an initial configuration and returns a middleware which will call the
* given logger with the request and response content.
*
* @param logger Logger function to pass the message to
* @return Middleware to perform the logging
*/
const requestLoggerMiddleware = ({ logger }) => (req, res, next) => {
logger("RECV <<<", req.method, req.url, req.hostname);
res.send = resDotSendInterceptor(res, res.send);
res.on("finish", () => {
logger("SEND >>>", res.contentBody);
});
next();
};


module.exports = { requestLoggerMiddleware };

完整的工作示例和文章在 git 回购 Https://github.com/jonathanturnock/reqresloggingexample

我对这个问题也有类似的需要。

根据已接受的答案,只有当它是 json 时,我才使用代理和跟踪响应主体修改它。

const traceMiddleware = (req, res, next) => {
const buffers = []
const proxyHandler = {
apply(target, thisArg, argumentsList) {
const contentType = res.getHeader('content-type')
if (
typeof contentType === 'string' && contentType.includes('json') && argumentsList[0]
) {
buffers.push(argumentsList[0])
}
return target.call(thisArg, ...argumentsList)
}
}
res.write = new Proxy(res.write, proxyHandler)
res.end = new Proxy(res.end, proxyHandler)
res.on('finish', () => {
// tracing logic inside
trace(req, res, Buffer.concat(buffers).toString('utf8'))
})
next()
}

对于某些用例来说,这个解决方案可能不够重量级,但是我认为它是最简单的。它还兼容打字机。如果您只想为 JSON 响应记录日志,那么您只需在下面的代码中用 JSON 方法替换 send 方法。注意,我从 Jonathan Turnock 的回答中获得了灵感,但是让它变得更简单。

app.use((req, res, next) => {
let send = res.send;
res.send = c => {
console.log(`Code: ${res.statusCode}`);
console.log("Body: ", c);
res.send = send;
return res.send(c);
}
next();
});