Mariapacana has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/117824

Change subject: WIP: Support structured logging and lazy pulling for backends.
......................................................................

WIP: Support structured logging and lazy pulling for backends.

Change-Id: I11aa47bf5ec460be35011a63b78cffe886bbfd28
---
M api/ParsoidService.js
A lib/LogData.js
M lib/Logger.js
3 files changed, 181 insertions(+), 97 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/parsoid 
refs/changes/24/117824/1

diff --git a/api/ParsoidService.js b/api/ParsoidService.js
index ed7aeb9..f826cab 100644
--- a/api/ParsoidService.js
+++ b/api/ParsoidService.js
@@ -439,32 +439,12 @@
        function parserEnvMw( req, res, next ) {
                MWParserEnvironment.getParserEnv( parsoidConfig, null, 
res.local('iwp'), res.local('pageName'), req.headers.cookie, function ( err, 
env ) {
 
-                       function generateErrCBMessage (obj) {
-                               var messageString = "";
-                               if (obj.constructor.name === "Error") {
-                                       messageString += obj.message;
-                                       messageString += 'ERROR in ' + 
res.local('iwp') + ':' + res.local('pageName');
-                                       messageString += "\n" + obj.stack;
-                               } else {
-                                       if (obj.msg) {
-                                               messageString += obj.msg;
-                                       }
-                                       if (obj.location) {
-                                               messageString += "\n" + 
obj.location;
-                                       }
-                                       if (obj.stack) {
-                                               messageString += "\n" + 
obj.stack;
-                                       }
-                               }
-                               return messageString;
-                       }
-
                        function errCB ( res, env, obj, callback ) {
                                try {
                                        if (env.responseSent) {
                                                return;
                                        } else {
-                                               var messageString = 
generateErrCBMessage(obj);
+                                               var messageString = obj.fullMsg;
                                                setHeader(res, env, 
'Content-Type', 'text/plain; charset=UTF-8' );
                                                sendResponse(res, env, 
messageString, obj.code || 500);
                                                res.on('finish', callback);
diff --git a/lib/LogData.js b/lib/LogData.js
new file mode 100644
index 0000000..207a7a9
--- /dev/null
+++ b/lib/LogData.js
@@ -0,0 +1,164 @@
+"use strict";
+require('./core-upgrade.js');
+
+var Util = require( './mediawiki.Util.js' ).Util,
+       DU = require( './mediawiki.DOMUtils.js').DOMUtils,
+       util = require('util'),
+       defines = require('./mediawiki.parser.defines.js'),
+       async = require('async');
+
+/**
+ *
+ * Extract properties and generate messages for logged objects.
+ *
+ * @class
+ * @constructor
+ * @param {MWParserEnvironment} env
+ * @param {string} logType
+ * @param {object} logObject
+ */
+
+var LogData = function (env, logType, logObject) {
+       this.env = env;
+       this.logType = logType;
+       this.logObject = logObject;
+
+       this.includeStackTrace = 
/(^(error|fatal)|(^|\/)stacktrace)(\/|$)/.test(logType);
+       this.shouldAnnounceLocation = /^(error|warning)(\/|$)/.test(logType);
+
+       // Cache log information if previously constructed.
+       this.cachedStack = null;
+       this.cachedflatLO = null;
+       this.cachedMsg = null;
+       this.cachedLoc = null;
+       this.cachedFullMsg = null;
+
+       // Expose specific logData properties so backends can access them
+       // as needed; compute them on demand with defineProperty.
+       Object.defineProperty(this,
+                                                                               
                "flatLO",
+                                                                               
                {get: this.getOrReturnCachedFlatLO });
+
+       Object.defineProperty(this,
+                                                                               
                "stack",
+                                                                               
                {get: this.getOrReturnCachedStack});
+
+       Object.defineProperty(this,
+                                                                               
                "loc",
+                                                                               
                {get: this.getOrReturnCachedLoc });
+
+       Object.defineProperty(this,
+                                                                               
                "msg",
+                                                                               
                {get: this.getOrReturnCachedMsg });
+
+       Object.defineProperty(this,
+                                                                               
                "fullMsg",
+                                                                               
                {get: this.getOrReturnCachedFullMsg });
+
+};
+
+
+LogData.prototype.getOrReturnCachedFullMsg = function(){
+       if (this.cachedFullMsg) {
+               return this.cachedFullMsg;
+       } else {
+               var messageString = this.msg;
+               messageString += "\n" + this.loc;
+               messageString += "\n" + this.stack;
+               this.cachedFullMsg = messageString;
+               return this.cachedFullMsg;
+       }
+};
+
+LogData.prototype.getOrReturnCachedMsg = function() {
+       if (this.cachedMsg) {
+               return this.cachedMsg;
+       } else {
+               this.cachedMsg = this.flatLO.msg;
+               return this.cachedMsg;
+       }
+};
+
+LogData.prototype.getOrReturnCachedLoc = function() {
+       if (this.cachedLoc) {
+               return this.cachedLoc;
+       } else {
+               this.cachedLoc = this.announceLocation();
+               return this.cachedLoc;
+       }
+};
+
+LogData.prototype.getOrReturnCachedFlatLO = function() {
+       if (this.cachedFlatLO) {
+               return this.cachedFlatLO;
+       } else {
+               this.cachedFlatLO = this.flatten(this.logObject, 'top level');
+               return this.cachedFlatLO;
+       }
+};
+
+LogData.prototype.getOrReturnCachedStack = function(){
+       if (this.cachedStack) {
+               return this.cachedStack;
+       } else {
+               // This new error's stack doesn't seem very useful,
+               // since it is buried all the way in the middle of this.
+               if (this.includeStackTrace && !this.flatLO.stack ) {
+                       this.cachedStack = new Error().stack;
+               } else if ( this.includeStackTrace && this.flatLO.stack ) {
+                       this.cachedStack = this.flatLO.stack;
+               }
+               return this.cachedStack;
+       }
+};
+
+LogData.prototype.announceLocation = function () {
+       var location = this.logType + ' in ' + this.env.conf.wiki.iwp + ':' + 
this.env.page.name;
+       if (this.env.page.revision && this.env.page.revision.revid) {
+               location += ' with oldid: ' + this.env.page.revision.revid;
+       }
+       return location;
+};
+
+LogData.prototype.flatten = function(o, topLevel) {
+       // returns an object with an arbitrary number of fields, 
+       // including "msg" (for the message, if any)
+       // and "stack" (for the stack trace, if any)
+       var f, stack, msg, longMsg,
+       self = this;
+
+       if ( Array.isArray(o) && topLevel ) {
+               // flatten components, but no longer in a top-level context.
+               f = o.map(function(oo) { return self.flatten(oo); });
+               // join all the messages with spaces or newlines between them.
+               var tobool = function(x) { return !!x; };
+               msg = f.map(function(oo) { return oo.msg; }).filter(tobool).
+               join(' ');
+               longMsg = f.map(function(oo) { return oo.msg; }).filter(tobool).
+               join('\n');
+               // merge all custom fields
+               f = f.reduce(function(prev, oo) {
+                       return Object.assign(prev, oo);
+               }, {});
+               return Object.assign(f, {
+                       msg: msg,
+                       longMsg: longMsg
+               });
+       } else if (o instanceof Error && o.code) {
+               return { msg: o.toString(), stack: o.stack, code: o.code };
+       } else if (o instanceof Error && !o.code) {
+               return { msg: o.toString(), stack: o.stack };
+       } else if (typeof(o)==='function') {
+               return self.flatten(o());
+       } else if (typeof (o) === 'object' && o.hasOwnProperty('msg')) {
+               return o;
+       } else if ( typeof(o) === 'string' ) {
+               return { msg: o };
+       } else {
+               return { msg: util.inspect(o)};
+       }
+};
+
+if (typeof module === "object") {
+       module.exports.LogData = LogData;
+}
\ No newline at end of file
diff --git a/lib/Logger.js b/lib/Logger.js
index 5eb8c9c..70cd999 100644
--- a/lib/Logger.js
+++ b/lib/Logger.js
@@ -1,7 +1,8 @@
 "use strict";
 require('./core-upgrade.js');
 
-var Util = require( './mediawiki.Util.js' ).Util,
+var LD = require('./LogData.js').LogData,
+       Util = require( './mediawiki.Util.js' ).Util,
        DU = require( './mediawiki.DOMUtils.js').DOMUtils,
        util = require('util'),
        defines = require('./mediawiki.parser.defines.js'),
@@ -48,16 +49,9 @@
        this.traceTestRegExp = this.traceTest();
 };
 
-Logger.prototype.defaultBackend = function(obj) {
+Logger.prototype.defaultBackend = function(logData) {
        try {
-               var messageString = obj.msg;
-               if (obj.location) {
-                       messageString += "\n" + obj.location;
-               }
-               if (obj.stack) {
-                       messageString += "\n" + obj.stack;
-               }
-               console.warn( messageString );
+               console.log(logData.fullMsg);
        } catch (e) {
                return;
        }
@@ -77,41 +71,9 @@
        }
 };
 
-Logger.prototype.flatten = function(o, topLevel) {
-       // returns an object with two fields, "msg" (for the message, if any)
-       // and "stack" (for the stack trace, if any)
-       var f, msg, stack,
-       self = this;
-
-       if ( Array.isArray(o) && topLevel ) {
-               // flatten components, but no longer in a top-level context.
-               f = o.map(function(oo) { return self.flatten(oo); });
-               // join all the messages with spaces between them.
-               msg = f.map(function(oo) { return oo.msg; }).join(' ');
-               // use the stack of the first item in the array with a stack
-               stack = f.reduce(function(prev, oo) {
-                       return prev || oo.stack;
-               }, undefined);
-               return { msg: msg, stack: stack };
-       } else if (o instanceof Error && o.code) {
-                       return { msg: o.toString(), stack: o.stack, code: 
o.code };
-       } else if (o instanceof Error && !o.code) {
-               return { msg: o.toString(), stack: o.stack };
-       } else if (typeof(o)==='function') {
-               f = this.flatten(o());
-               return { msg: f.msg, stack: o.stack || f.stack };
-       } else if (typeof(o)==='object' && o.hasOwnProperty('msg')) {
-               f = this.flatten(o.msg);
-               return { msg: f.msg, stack: o.stack || f.stack };
-       } else if (typeof(o)==='string') {
-               return { msg: o /* no stack */ };
-       } else {
-               return { msg: util.inspect(o) /* no stack */ };
-       }
-};
-
-Logger.prototype.getApplicableBackends = function(logType, obj) {
+Logger.prototype.getApplicableBackends = function(logData) {
        var applicableBackends = [];
+       var logType = logData.logType;
        var self = this;
        var error;
        if (this.backendRegExp && this.backendRegExp.test(logType)) {
@@ -119,7 +81,7 @@
                        if (key === logType || key.test(logType)) {
                                applicableBackends.push(function(callback){
                                        try {
-                                               backends.get(key)(obj, 
function(){
+                                               backends.get(key)(logData, 
function(){
                                                        callback(null);
                                                });
                                        } catch (e) {
@@ -135,8 +97,8 @@
        return applicableBackends;
 };
 
-Logger.prototype.routeToBackends = function(logType, obj, cb) {
-       var applicableBackends = this.getApplicableBackends(logType, obj);
+Logger.prototype.routeToBackends = function(logData, cb) {
+       var applicableBackends = this.getApplicableBackends(logData);
        var self = this;
 
        // If the logType is fatal, exits the process after logging
@@ -144,46 +106,24 @@
        // Additionally runs processFatalCB, which looks for fatal
        // events in the queue and logs them.
        async.parallel(applicableBackends, function(){
-               if (/^fatal$/.test(logType)) {
-                       self.defaultBackend(obj);
+               if (/^fatal$/.test(logData.logType)) {
+                       self.defaultBackend(logData);
                        process.exit(1);
                } else {
-                       self.defaultBackend(obj);
+                       self.defaultBackend(logData);
                }
                cb();
        });
 };
 
-Logger.prototype.announceLocation = function (logType) {
-       var location = logType + ' in ' + this.env.conf.wiki.iwp + ':' + 
this.env.page.name;
-       if (this.env.page.revision && this.env.page.revision.revid) {
-               location += ' with oldid: ' + this.env.page.revision.revid;
-       }
-       return location;
-};
-
-Logger.prototype.emitMessage = function (logType, logObject, callback) {
-       var includeStackTrace = 
/(^(error|fatal)|(^|\/)stacktrace)(\/|$)/.test(logType);
-       // XXX this should be configurable.
-       var shouldAnnounceLocation = /^(error|warning)(\/|$)/.test(logType);
-
-       // Gets everything that isn't logType
-       var obj = this.flatten(logObject, 'top level');
-       if (shouldAnnounceLocation) {
-               obj.location = this.announceLocation(logType);
-       }
-       if (includeStackTrace && !obj.stack ) {
-               obj.stack = new Error().stack;
-       }
-       this.routeToBackends(logType, obj, callback);
-};
-
 Logger.prototype.log = function (logType) {
        var self = this;
+       var traceTestRegExp = this.traceTest();
+       var logObject = Array.prototype.slice.call(arguments, 1);
+       var logData = new LD(this.env, logType, logObject);
 
        // XXX this should be configurable.
        if (this.traceTestRegExp.test(logType)) {
-               var logObject = Array.prototype.slice.call(arguments, 1);
 
                // If we are already processing a log request, but a log was 
generated
                // while processing the first request, processingLogRequest 
will be true.
@@ -201,7 +141,7 @@
                // process any fatal log events that we find on the queue.
                } else {
                        self.processingLogRequest = true;
-                       this.emitMessage(logType, logObject, function(){
+                       this.routeToBackends(logData, function(){
                                self.processingLogRequest = false;
                                if (self.logRequestQueue.length > 0) {
                                        self.log.apply(self, 
self.logRequestQueue.pop());

-- 
To view, visit https://gerrit.wikimedia.org/r/117824
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I11aa47bf5ec460be35011a63b78cffe886bbfd28
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/parsoid
Gerrit-Branch: master
Gerrit-Owner: Mariapacana <maria.pac...@gmail.com>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to