diff --git a/packages/react-server/core/logging/response.js b/packages/react-server/core/logging/response.js new file mode 100644 index 000000000..eafbf9c1e --- /dev/null +++ b/packages/react-server/core/logging/response.js @@ -0,0 +1,79 @@ +var common = require('./common') +, SuperLogger = process.env.IS_SERVER ? require('winston').Transport : class {} +, RLS = require('../util/RequestLocalStorage').getNamespace(); + +// A subset of stats that are logged are not associated with requests +// or occur before the request context is initialized. Simply ignore +// those logs here. +var queue = () => { + if(RLS.isActive()) { + return RLS().queue || (RLS().queue = []); + } + else { + return []; + } +} + +class ResponseLogger extends SuperLogger { + constructor(options) { + super(); + this.name = 'ResponseLogger'; + this.level = options.level || 'debug'; + this.module = options.name; + this.lastModuleToken = options.name.split('.').pop(); + } + + log(level, msg, meta, callback) { + var tuple = [ + this.module, + msg, + meta[this.key], + this.lastModuleToken, + ]; + + queue().push(tuple); + // Yield to the next log transport. + callback(null, true); + } + + flushToResponse(res) { + if(queue().length > 0) { + res.write(""); + } + } +} + +class TimeResponseLogger extends ResponseLogger { + constructor(options){ + super(options); + this.name = 'TimeResponseLogger'; + this.level = 'fast'; + this.key = 'ms'; + } +} + +class GaugeResponseLogger extends ResponseLogger { + constructor(options){ + super(options); + this.name = 'GaugeResponseLogger'; + this.level = 'ok'; + this.key = 'val'; + } +} + +var getTransportForGroup = function(group, opts) { + if(group == "time") { + return new TimeResponseLogger(opts); + } + else if(group == "gauge") { + return new GaugeResponseLogger(opts); + } + else { + return new ResponseLogger(opts); + } +} + +module.exports = {getTransportForGroup, TimeResponseLogger, TimeResponseLogger, ResponseLogger}; diff --git a/packages/react-server/core/logging/server.js b/packages/react-server/core/logging/server.js index abb5bd059..2eed15fa5 100644 --- a/packages/react-server/core/logging/server.js +++ b/packages/react-server/core/logging/server.js @@ -7,11 +7,11 @@ var winston = require('winston') isEmpty : require("lodash/isEmpty"), trimStart : require("lodash/trimStart"), truncate : require("lodash/truncate"), -}; +} +, responseTransport = require('./response'); var makeLogger = function(group, opts){ var config = common.config[group]; - var fileTransport = new (winston.transports.File)({ name : 'file', level : config.baseLevel, @@ -23,6 +23,7 @@ var makeLogger = function(group, opts){ var logger = new (winston.Logger)({ transports: [ fileTransport, + responseTransport.getTransportForGroup(group, opts), ], }); diff --git a/packages/react-server/core/renderMiddleware.js b/packages/react-server/core/renderMiddleware.js index 58b64ae6b..0e5b09b4a 100644 --- a/packages/react-server/core/renderMiddleware.js +++ b/packages/react-server/core/renderMiddleware.js @@ -751,6 +751,15 @@ function writeBody(req, res, context, start, page) { // Don't leave dead timers hanging around. retval.promise.then(() => clearTimeout(timeout)); + // Flush timing/log data to the response document + retval.promise.then(() => { + if (req.query._debug_output_logs) { + logger.transports.ResponseLogger.flushToResponse(res); + logger.timeLogger.transports.TimeResponseLogger.flushToResponse(res); + logger.gaugeLogger.transports.GaugeResponseLogger.flushToResponse(res); + } + }); + return retval.promise; }