Files
meteor/tools/runners/run-log.js
Ben Newman e85c69680d Combine all isopackets to share transitive dependencies. (#9168)
By my calculations, the sum of the sizes of the individual isopackets was
152MB, and the size of the combined isopacket is now just 36MB. That
remarkable difference goes to show how much duplication of transitive
dependencies was happening before this change.

That's a savings of 116MB for the (uncompressed) size of the meteor-tool
package. In Meteor 1.5.x, the meteor-tool package is about 544MB, but in
Meteor 1.6 it's considerably smaller: 373MB. In other words, this change
should reduce those sizes to 428MB (-21%) and 257MB (-31%), respectively.
2017-10-02 13:41:39 -04:00

231 lines
6.9 KiB
JavaScript

var _ = require('underscore');
var Console = require('../console/console.js').Console;
var fiberHelpers = require('../utils/fiber-helpers.js');
// runLog is primarily used by the parts of the tool which run apps locally. It
// writes to standard output (and standard error, if rawLogs is set), and allows
// special output forms like "write this line, but let the next line overwrite
// it". It also makes its output available to the proxy, to be displayed to web
// browsers if the app fails to run.
//
// It's not the only mechanism used for gathering messages! buildmessage is a
// more structured way of gathering messages, but unlike log, it does not print
// messages immediately.
//
// Some other parts of the code (eg commands and warehouse) write directly to
// process.std{out,err} or to console.log; we should be careful to not do that
// anywhere that may overlap with use of runLog.
let _Log;
function getLoggingPackage() {
if (! _Log) {
_Log = require("../tool-env/isopackets.js")
.loadIsopackage('logging')
.Log;
}
// Since no other process will be listening to stdout and parsing it,
// print directly in the same format as log messages from other apps
_Log.outputFormat = 'colored-text';
return _Log;
};
var RunLog = function () {
var self = this;
self.rawLogs = false;
self.messages = []; // list of log objects
self.maxLength = 100;
// If non-null, the last thing logged was "server restarted"
// message, and the value will be the number of consecutive such
// messages that have been logged with no other intervening messages
self.consecutiveRestartMessages = null;
self.consecutiveClientRestartMessages = null;
// If non-null, the last thing that was logged was a temporary
// message (with a carriage return but no newline), and this is its
// length.
self.temporaryMessageLength = null;
};
_.extend(RunLog.prototype, {
_record: function (msg) {
var self = this;
self.messages.push(msg);
if (self.messages.length > self.maxLength) {
self.messages.shift();
}
},
_clearSpecial: function () {
var self = this;
if (self.consecutiveRestartMessages) {
self.consecutiveRestartMessages = null;
Console.info();
}
if (self.consecutiveClientRestartMessages) {
self.consecutiveClientRestartMessages = null;
Console.info();
}
if (self.temporaryMessageLength) {
var spaces = new Array(self.temporaryMessageLength + 1).join(' ');
process.stdout.write(spaces + Console.CARRIAGE_RETURN);
self.temporaryMessageLength = null;
}
},
setRawLogs: function (rawLogs) {
this.rawLogs = !!rawLogs;
},
logAppOutput: function (line, isStderr) {
var self = this;
var Log = getLoggingPackage();
var obj = (isStderr ?
Log.objFromText(line, { level: 'warn', stderr: true }) :
Log.parse(line) || Log.objFromText(line));
self._record(obj);
self._clearSpecial();
if (self.rawLogs) {
Console[isStderr ? "rawError" : "rawInfo"](line + "\n");
} else {
// XXX deal with test server logging differently?!
return fiberHelpers.noYieldsAllowed(function() {
Console.rawInfo(Log.format(obj, { color: true }) + "\n");
});
}
},
// Log the message.
// msg: message
// options:
// - arrow: if true, preface with => and wrap accordingly.
log: function (msg, options) {
var self = this;
options = options || {};
var obj = {
time: new Date,
message: msg
// in the future, might want to add something else to
// distinguish messages from runner from message from the app,
// but for now, nothing would use it, so we'll keep it simple
};
self._record(obj);
self._clearSpecial();
// Process the options. By default, we want to wordwrap the message with
// Console.info. If we ask for raw output, then we don't want to do that. If
// we ask for an arrow, we want to wrap around with => as the bulletPoint.
Console[options.arrow ? 'arrowInfo' : 'info'](msg);
},
// Write a message to the terminal that will get overwritten by the
// next message logged. (Don't put it in the log that getLog
// returns.)
// XXX Maybe this should return an object that you have to pass to the
// subsequent log call, and only such a log call will overwrite it (and an
// intervening log call will cause this to stay on the screen)?
// eg, a log call from the updater can interweave with the logTemporary
// calls in run-all.js
logTemporary: function (msg) {
var self = this;
self._clearSpecial();
process.stdout.write(msg + Console.CARRIAGE_RETURN);
self.temporaryMessageLength = msg.length;
},
logRestart: function () {
var self = this;
if (self.consecutiveRestartMessages) {
// replace old message in place. this assumes that the new restart message
// is not shorter than the old one.
process.stdout.write(Console.CARRIAGE_RETURN);
self.messages.pop();
self.consecutiveRestartMessages ++;
} else {
self._clearSpecial();
self.consecutiveRestartMessages = 1;
}
var message = "=> Meteor server restarted";
if (self.consecutiveRestartMessages > 1) {
message += " (x" + self.consecutiveRestartMessages + ")";
}
// no newline, so that we can overwrite it if we get another
// restart message right after this one
process.stdout.write(message);
self._record({
time: new Date,
message: message
});
},
logClientRestart: function () {
var self = this;
if (self.consecutiveClientRestartMessages) {
// replace old message in place. this assumes that the new restart message
// is not shorter than the old one.
process.stdout.write(Console.CARRIAGE_RETURN);
self.messages.pop();
self.consecutiveClientRestartMessages ++;
} else {
self._clearSpecial();
self.consecutiveClientRestartMessages = 1;
}
var message = "=> Client modified -- refreshing";
if (self.consecutiveClientRestartMessages > 1) {
message += " (x" + self.consecutiveClientRestartMessages + ")";
}
// no newline, so that we can overwrite it if we get another
// restart message right after this one
process.stdout.write(message);
self._record({
time: new Date,
message: message
});
},
finish: function () {
var self = this;
self._clearSpecial();
},
clearLog: function () {
var self = this;
self.messages = [];
},
getLog: function () {
var self = this;
return self.messages;
}
});
// Create a singleton instance of RunLog. Expose its public methods on the
// object you get with require('./run-log.js').
var runLogInstance = new RunLog;
_.each(
['log', 'logTemporary', 'logRestart', 'logClientRestart', 'logAppOutput',
'setRawLogs', 'finish', 'clearLog', 'getLog'],
function (method) {
exports[method] = _.bind(runLogInstance[method], runLogInstance);
});