Skip to content

Commit

Permalink
Update react-server to write server side timings/logs to the response
Browse files Browse the repository at this point in the history
document.

See: redfin#272
  • Loading branch information
Vincent Chang committed Aug 26, 2016
1 parent d0cbf3a commit a2b327d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 2 deletions.
79 changes: 79 additions & 0 deletions packages/react-server/core/logging/response.js
Original file line number Diff line number Diff line change
@@ -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("<script>");
res.write("window.serverLogs = window.serverLogs || {};");
res.write(`window.serverLogs.${this.name}Logs = ${JSON.stringify(queue())};\n`);
res.write("</script>");
}
}
}

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};
5 changes: 3 additions & 2 deletions packages/react-server/core/logging/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -23,6 +23,7 @@ var makeLogger = function(group, opts){
var logger = new (winston.Logger)({
transports: [
fileTransport,
responseTransport.getTransportForGroup(group, opts),
],
});

Expand Down
9 changes: 9 additions & 0 deletions packages/react-server/core/renderMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down

0 comments on commit a2b327d

Please sign in to comment.