From a003d261ea31e5d1d9a6a4da41e2b52b68ee2a27 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Thu, 27 Feb 2014 23:38:31 -0800 Subject: [PATCH] Log: New logging system. Right now, all I've done is move the logging function from utils to its own file. I've also refactored it and created some classes. Next step would be to somehow get rid of the trace setting. The Remote class should have nothing to do with logging settings. Perhaps we can keep the setting for backwards compatibility only, but the main way to turn logging on/off or filter logging messages should be through the Log class. --- src/js/ripple/log.js | 116 +++++++++++++++++++++++++++++++++++++ src/js/ripple/log.web.js | 30 ++++++++++ src/js/ripple/remote.js | 3 +- src/js/ripple/server.js | 23 ++++---- src/js/ripple/utils.js | 13 ----- src/js/ripple/utils.web.js | 19 ------ 6 files changed, 160 insertions(+), 44 deletions(-) create mode 100644 src/js/ripple/log.js create mode 100644 src/js/ripple/log.web.js delete mode 100644 src/js/ripple/utils.web.js diff --git a/src/js/ripple/log.js b/src/js/ripple/log.js new file mode 100644 index 00000000..0073f7bc --- /dev/null +++ b/src/js/ripple/log.js @@ -0,0 +1,116 @@ +/** + * Logging functionality for ripple-lib and any applications built on it. + */ +var Log = function (namespace) { + if (!namespace) { + this._namespace = []; + } else if (Array.isArray(namespace)) { + this._namespace = namespace; + } else { + this._namespace = [""+namespace]; + } + + this._prefix = this._namespace.concat(['']).join(': '); +}; + +/** + * Create a sub-logger. + * + * You can have a hierarchy of loggers. + * + * @example + * + * var log = require('ripple').log.sub('server'); + * + * log.info('connection successful'); + * // prints: "server: connection successful" + */ +Log.prototype.sub = function (namespace) { + var subNamespace = this._namespace.slice(); + if (namespace && "string" === typeof namespace) subNamespace.push(namespace); + var subLogger = new Log(subNamespace); + subLogger._setParent(this); + return subLogger; +}; + +Log.prototype._setParent = function (parentLogger) { + this._parent = parentLogger; +}; + +Log.makeLevel = function (level) { + return function () { + arguments[0] = this._prefix + arguments[0]; + + Log.engine.logObject.apply(Log, Array.prototype.slice.call(arguments)); + }; +}; + +Log.prototype.debug = Log.makeLevel(1); +Log.prototype.info = Log.makeLevel(2); +Log.prototype.warn = Log.makeLevel(3); +Log.prototype.error = Log.makeLevel(4); + +/** + * Basic logging connector. + * + * This engine has no formatting and works with the most basic of "console.log" + * implementations. This is the logging engine used in Node.js. + */ +var BasicLogEngine = { + logObject: function logObject(msg) { + var args = Array.prototype.slice.call(arguments, 1); + + args = args.map(function(arg) { + return JSON.stringify(arg, null, 2); + }); + + args.unshift(msg); + + console.log.apply(console, args); + } +}; + +/** + * Null logging connector. + * + * This engine simply swallows all messages. Used when console.log is not + * available. + */ +var NullLogEngine = { + logObject: function () {} +}; + +Log.engine = NullLogEngine; + +if (console && console.log) Log.engine = BasicLogEngine; + +/** + * Provide a root logger as our main export. + * + * This means you can use the logger easily on the fly: + * ripple.log.debug('My object is', myObj); + */ +module.exports = new Log(); + +/** + * This is the logger for ripple-lib internally. + */ +module.exports.internal = module.exports.sub(); + +/** + * Expose the class as well. + */ +module.exports.Log = Log; + +/** + * An easy way to create a new logger for a namespace. + * + * Browser: + * var log = ripple.log.called('client.id'); + * log.debug('My object is', myObj); + * + * Node.js: + * var log = require('ripple').log.called('gateway.main'); + * log.debug('My object is', myObj); + */ +module.exports.called = Log.makeLogger; diff --git a/src/js/ripple/log.web.js b/src/js/ripple/log.web.js new file mode 100644 index 00000000..74f93899 --- /dev/null +++ b/src/js/ripple/log.web.js @@ -0,0 +1,30 @@ +var exports = module.exports = require('./log.js'); + +/** + * Log engine for browser consoles. + * + * Browsers tend to have better consoles that support nicely formatted + * JavaScript objects. This connector passes objects through to the logging + * function without any stringification. + */ +var InteractiveLogEngine = { + logObject: function (msg, obj) { + var args = Array.prototype.slice.call(arguments, 1); + + args = args.map(function(arg) { + if (/MSIE/.test(navigator.userAgent)) { + return JSON.stringify(arg, null, 2); + } else { + return arg; + } + }); + + args.unshift(msg); + + console.log.apply(console, args); + } +}; + +if (window.console && window.console.log) { + exports.Log.engine = InteractiveLogEngine; +} diff --git a/src/js/ripple/remote.js b/src/js/ripple/remote.js index 957eb5fb..ae0928e1 100644 --- a/src/js/ripple/remote.js +++ b/src/js/ripple/remote.js @@ -30,6 +30,7 @@ var RippleError = require('./rippleerror').RippleError; var utils = require('./utils'); var sjcl = require('./utils').sjcl; var config = require('./config'); +var log = require('./log').internal.sub('remote'); /** Interface to manage the connection to a Ripple server. @@ -358,7 +359,7 @@ Remote.prototype.setSecret = function(account, secret) { Remote.prototype._trace = function() { if (this.trace) { - utils.logObject.apply(utils, arguments); + log.info.apply(log, arguments); } }; diff --git a/src/js/ripple/server.js b/src/js/ripple/server.js index ab3aa3c9..02aff8cd 100644 --- a/src/js/ripple/server.js +++ b/src/js/ripple/server.js @@ -3,6 +3,7 @@ var EventEmitter = require('events').EventEmitter; var Transaction = require('./transaction').Transaction; var Amount = require('./amount').Amount; var utils = require('./utils'); +var log = require('./log').internal.sub('server'); /** * @constructor Server @@ -104,7 +105,7 @@ Server.onlineStates = [ Server.prototype._setState = function(state) { if (state !== this._state) { - this._remote._trace('server: set_state:', state); + this._remote.trace && log.info('set_state:', state); this._state = state; this.emit('state', state); @@ -189,7 +190,7 @@ Server.prototype.connect = function() { // we will automatically reconnect. if (this._connected) return; - this._remote._trace('server: connect:', this._opts.url); + this._remote.trace && log.info('connect:', this._opts.url); // Ensure any existing socket is given the command to close first. if (this._ws) this._ws.close(); @@ -223,7 +224,7 @@ Server.prototype.connect = function() { // If we are no longer the active socket, simply ignore any event if (ws === self._ws) { self.emit('socket_error'); - self._remote._trace('server: onerror:', self._opts.url, e.data || e); + self._remote.trace && log.info('onerror:', self._opts.url, e.data || e); // Most connection errors for WebSockets are conveyed as 'close' events with // code 1006. This is done for security purposes and therefore unlikely to @@ -247,7 +248,7 @@ Server.prototype.connect = function() { ws.onclose = function onClose() { // If we are no longer the active socket, simply ignore any event if (ws === self._ws) { - self._remote._trace('server: onclose:', self._opts.url, ws.readyState); + self._remote.trace && log.info('onclose:', self._opts.url, ws.readyState); self._handleClose(); } }; @@ -274,7 +275,7 @@ Server.prototype._retryConnect = function() { function connectionRetry() { if (self._shouldConnect) { - self._remote._trace('server: retry', self._opts.url); + self._remote.trace && log.info('retry', self._opts.url); self.connect(); } }; @@ -351,9 +352,9 @@ Server.prototype._handleMessage = function(message) { delete self._requests[message.id]; if (!request) { - this._remote._trace('server: UNEXPECTED:', self._opts.url, message); + this._remote.trace && log.info('UNEXPECTED:', self._opts.url, message); } else if (message.status === 'success') { - this._remote._trace('server: response:', self._opts.url, message); + this._remote.trace && log.info('response:', self._opts.url, message); request.emit('success', message.result); @@ -361,7 +362,7 @@ Server.prototype._handleMessage = function(message) { emitter.emit('response_' + request.message.command, message.result, request, message); }); } else if (message.error) { - this._remote._trace('server: error:', self._opts.url, message); + this._remote.trace && log.info('error:', self._opts.url, message); request.emit('error', { error : 'remoteError', @@ -372,7 +373,7 @@ Server.prototype._handleMessage = function(message) { break; case 'path_find': - this._remote._trace('server: path_find:', self._opts.url, message); + this._remote.trace && log.info('path_find:', self._opts.url, message); break; } @@ -431,7 +432,7 @@ Server.prototype._handleResponseSubscribe = function(message) { Server.prototype.sendMessage = function(message) { if (this._ws) { - this._remote._trace('server: request:', this._opts.url, message); + this._remote.trace && log.info('request:', this._opts.url, message); this._ws.send(JSON.stringify(message)); } }; @@ -451,7 +452,7 @@ Server.prototype.request = function(request) { // Only bother if we are still connected. if (!this._ws) { - this._remote._trace('server: request: DROPPING:', self._opts.url, request.message); + this._remote.trace && log.info('request: DROPPING:', self._opts.url, request.message); return; } diff --git a/src/js/ripple/utils.js b/src/js/ripple/utils.js index 966d9d18..4304f661 100644 --- a/src/js/ripple/utils.js +++ b/src/js/ripple/utils.js @@ -92,18 +92,6 @@ function chunkString(str, n, leftAlign) { return ret; }; -function logObject(msg) { - var args = Array.prototype.slice.call(arguments, 1); - - args = args.map(function(arg) { - return JSON.stringify(arg, null, 2); - }); - - args.unshift(msg); - - console.log.apply(console, args); -}; - function assert(assertion, msg) { if (!assertion) { throw new Error("Assertion failed" + (msg ? ": "+msg : ".")); @@ -157,7 +145,6 @@ exports.hexToArray = hexToArray; exports.stringToArray = stringToArray; exports.stringToHex = stringToHex; exports.chunkString = chunkString; -exports.logObject = logObject; exports.assert = assert; exports.arrayUnique = arrayUnique; exports.toTimestamp = toTimestamp; diff --git a/src/js/ripple/utils.web.js b/src/js/ripple/utils.web.js deleted file mode 100644 index b9ba6444..00000000 --- a/src/js/ripple/utils.web.js +++ /dev/null @@ -1,19 +0,0 @@ -var exports = module.exports = require('./utils.js'); - -// We override this function for browsers, because they print objects nicer -// natively than JSON.stringify can. -exports.logObject = function (msg, obj) { - var args = Array.prototype.slice.call(arguments, 1); - - args = args.map(function(arg) { - if (/MSIE/.test(navigator.userAgent)) { - return JSON.stringify(arg, null, 2); - } else { - return arg; - } - }); - - args.unshift(msg); - - console.log.apply(console, args); -};