Skip to content

Commit d99043c

Browse files
committed
MAGETWO-59587: Debugging and profiling tools for UI Components and Javascript modules
1 parent 39c502c commit d99043c

File tree

8 files changed

+216
-39
lines changed

8 files changed

+216
-39
lines changed

app/code/Magento/Ui/view/base/web/js/core/renderer/layout.js

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ define([
88
'jquery',
99
'mageUtils',
1010
'uiRegistry',
11-
'./types'
12-
], function (_, $, utils, registry, types) {
11+
'./types',
12+
'../../lib/logger/console-logger'
13+
], function (_, $, utils, registry, types, consoleLogger) {
1314
'use strict';
1415

1516
var templates = registry.create(),
@@ -70,7 +71,25 @@ define([
7071
* @returns {jQueryPromise}
7172
*/
7273
function loadDeps(node) {
73-
var loaded = $.Deferred();
74+
var loaded = $.Deferred(),
75+
loggerUtils = consoleLogger.utils;
76+
77+
if (node.deps) {
78+
consoleLogger.utils.asyncLog(
79+
loaded,
80+
{
81+
data: {
82+
component: node.name,
83+
deps: node.deps
84+
},
85+
messages: loggerUtils.createMessages(
86+
'depsStartRequesting',
87+
'depsFinishRequesting',
88+
'depsLoadingFail'
89+
)
90+
}
91+
);
92+
}
7493

7594
registry.get(node.deps, function (deps) {
7695
node.provider = node.extendProvider ? deps && deps.name : node.provider;
@@ -90,8 +109,19 @@ define([
90109
var loaded = $.Deferred(),
91110
source = node.component;
92111

112+
consoleLogger.info('componentStartLoading', {
113+
component: node.component
114+
});
115+
93116
require([source], function (constr) {
117+
consoleLogger.info('componentFinishLoading', {
118+
component: node.component
119+
});
94120
loaded.resolve(node, constr);
121+
}, function (err) {
122+
consoleLogger.error('componentLoadingFail', {
123+
component: node.component
124+
});
95125
});
96126

97127
return loaded.promise();
@@ -106,6 +136,11 @@ define([
106136
function initComponent(node, Constr) {
107137
var component = new Constr(_.omit(node, 'children'));
108138

139+
consoleLogger.info('componentStartInitialization', {
140+
component: node.component,
141+
componentName: node.name
142+
});
143+
109144
registry.set(node.name, component);
110145
}
111146

app/code/Magento/Ui/view/base/web/js/lib/knockout/bindings/scope.js

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,22 @@ define([
77
'ko',
88
'uiRegistry',
99
'mage/translate',
10-
'../template/renderer'
11-
], function (ko, registry, $t, renderer) {
10+
'../template/renderer',
11+
'jquery',
12+
'../../logger/console-logger'
13+
], function (ko, registry, $t, renderer, $, consoleLogger) {
1214
'use strict';
1315

1416
/**
1517
* Creates child context with passed component param as $data. Extends context with $t helper.
1618
* Applies bindings to descendant nodes.
1719
* @param {HTMLElement} el - element to apply bindings to.
1820
* @param {ko.bindingContext} bindingContext - instance of ko.bindingContext, passed to binding initially.
21+
* @param {Promise} promise - instance of jQuery promise
1922
* @param {Object} component - component instance to attach to new context
2023
*/
21-
function applyComponents(el, bindingContext, component) {
24+
function applyComponents(el, bindingContext, promise, component) {
25+
promise.resolve();
2226
component = bindingContext.createChildContext(component);
2327

2428
ko.utils.extend(component, {
@@ -53,9 +57,25 @@ define([
5357
*/
5458
update: function (el, valueAccessor, allBindings, viewModel, bindingContext) {
5559
var component = valueAccessor(),
56-
apply = applyComponents.bind(this, el, bindingContext);
60+
promise = $.Deferred(),
61+
apply = applyComponents.bind(this, el, bindingContext, promise),
62+
loggerUtils = consoleLogger.utils;
5763

5864
if (typeof component === 'string') {
65+
loggerUtils.asyncLog(
66+
promise,
67+
{
68+
data: {
69+
component: component
70+
},
71+
messages: loggerUtils.createMessages(
72+
'requestingComponent',
73+
'requestingComponentIsLoaded',
74+
'requestingComponentIsFailed'
75+
)
76+
}
77+
);
78+
5979
registry.get(component, apply);
6080
} else if (typeof component === 'function') {
6181
component(apply);

app/code/Magento/Ui/view/base/web/js/lib/knockout/template/engine.js

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ define([
66
'ko',
77
'underscore',
88
'./observable_source',
9-
'./renderer'
10-
], function (ko, _, Source, renderer) {
9+
'./renderer',
10+
'../../logger/console-logger'
11+
], function (ko, _, Source, renderer, consoleLogger) {
1112
'use strict';
1213

1314
var RemoteTemplateEngine,
@@ -36,9 +37,12 @@ define([
3637
* Caches template after it's unique name and renders in once.
3738
* If template name is not typeof string, delegates work to knockout.templateSources.anonymousTemplate.
3839
* @param {*} template
40+
* @param {HTMLElement} templateDocument - document
41+
* @param {Object} options - options, passed to template binding
42+
* @param {ko.bindingContext} bindingContext
3943
* @returns {TemplateSource} Object with methods 'nodes' and 'data'.
4044
*/
41-
RemoteTemplateEngine.prototype.makeTemplateSource = function (template) {
45+
RemoteTemplateEngine.prototype.makeTemplateSource = function (template, templateDocument, options, bindingContext) {
4246
var source,
4347
templateId;
4448

@@ -48,13 +52,36 @@ define([
4852

4953
if (!source) {
5054
source = new Source(template);
55+
source.requestedBy = bindingContext.$data.name;
5156
sources[templateId] = source;
5257

58+
consoleLogger.info('templateStartLoading', {
59+
template: templateId,
60+
component: bindingContext.$data.name
61+
});
62+
5363
renderer.render(template).done(function (rendered) {
64+
consoleLogger.info('templateLoadedFromServer', {
65+
template: templateId,
66+
component: bindingContext.$data.name
67+
});
5468
source.nodes(rendered);
69+
}).fail(function (err) {
70+
consoleLogger.error('templateLoadingFail', {
71+
template: templateId,
72+
component: bindingContext.$data.name
73+
});
5574
});
5675
}
5776

77+
if (source.requestedBy !== bindingContext.$data.name) {
78+
consoleLogger.info('templateLoadedFromCache', {
79+
template: templateId,
80+
component: bindingContext.$data.name
81+
});
82+
}
83+
84+
5885
return source;
5986
} else if (template.nodeType === 1 || template.nodeType === 8) {
6087
source = new ko.templateSources.anonymousTemplate(template);
@@ -87,7 +114,7 @@ define([
87114
* @return {Array} - array of html elements
88115
*/
89116
RemoteTemplateEngine.prototype.renderTemplate = function (template, bindingContext, options, templateDocument) {
90-
var templateSource = this.makeTemplateSource(template, templateDocument, options);
117+
var templateSource = this.makeTemplateSource(template, templateDocument, options, bindingContext);
91118

92119
return this.renderTemplateSource(templateSource, bindingContext, options);
93120
};

app/code/Magento/Ui/view/base/web/js/lib/knockout/template/loader.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,9 @@ define([
9292

9393
require([path], function (template) {
9494
template = removeLicense(template);
95-
9695
loading.resolve(template);
96+
}, function (err) {
97+
loading.reject(err);
9798
});
9899

99100
return loading.promise();

app/code/Magento/Ui/view/base/web/js/lib/logger/console-logger.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ define([
66
'./message-poll',
77
'./levels-poll',
88
'Magento_Ui/js/lib/core/storage/local',
9-
'underscore'
10-
], function (Logger, entryFactory, ConsoleHandler, Formatter, messagePoll, levelsPoll, storage, _) {
9+
'underscore',
10+
'./logger-utils'
11+
], function (Logger, entryFactory, ConsoleHandler, Formatter, messagePoll, levelsPoll, storage, _, LoggerUtils) {
1112
'use strict';
1213

1314
var STORAGE_NAMESPACE = 'CONSOLE_LOGGER';
@@ -20,14 +21,16 @@ define([
2021
function ConsoleLogger() {
2122
var formatter = new Formatter(),
2223
consoleHandler = new ConsoleHandler(formatter),
23-
savedLevel = storage.get(STORAGE_NAMESPACE);
24+
savedLevel = storage.get(STORAGE_NAMESPACE),
25+
utils = new LoggerUtils(this);
2426

2527
Logger.call(this, consoleHandler, entryFactory);
2628

2729
if (savedLevel) {
2830
this.displayLevel_ = savedLevel;
2931
}
3032

33+
this.utils = utils;
3134
this.messages = messagePoll;
3235
this.levels = levelsPoll.getLevels();
3336
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
define([], function () {
6+
'use strict';
7+
8+
/**
9+
* Utils methods for logger
10+
* @param {Logger} logger
11+
*/
12+
function LogUtils(logger) {
13+
this.logger = logger;
14+
15+
}
16+
17+
/**
18+
* Method for logging asynchronous operations
19+
* @param {Promise} promise
20+
* @param {object} config
21+
*/
22+
LogUtils.prototype.asyncLog = function (promise, config) {
23+
var levels,
24+
messages,
25+
wait;
26+
27+
config = config || {};
28+
levels = config.levels || this.createLevels();
29+
messages = config.messages || this.createMessages();
30+
wait = config.wait || 5000;
31+
32+
33+
this.logger[levels.requested](messages.requested, config.data);
34+
setTimeout(function () {
35+
promise.state() === 'pending' ?
36+
this.logger[levels.failed](messages.failed, config.data):
37+
this.logger[levels.loaded](messages.loaded, config.data);
38+
}.bind(this), wait)
39+
};
40+
41+
/**
42+
* Method that creats object of messages
43+
* @param {String} requested - log message that showing that request for class is started
44+
* @param {String} loaded - log message that show when requested class is loaded
45+
* @param {String} failded - log message that show when requested class is failed
46+
* @returns {Object}
47+
*/
48+
LogUtils.prototype.createMessages = function (requested, loaded, failded) {
49+
return {
50+
requested: requested || '',
51+
loaded: loaded || '',
52+
failed: failded || ''
53+
}
54+
};
55+
56+
/**
57+
* Method that creats object of log levels
58+
* @param {String} requested - log message that showing that request for class is started
59+
* @param {String} loaded - log message that show when requested class is loaded
60+
* @param {String} failded - log message that show when requested class is failed
61+
* @returns {Object}
62+
*/
63+
LogUtils.prototype.createLevels = function (requested, loaded, failded) {
64+
return {
65+
requested: requested || 'info',
66+
loaded: loaded || 'info',
67+
failed: failded || 'warn'
68+
}
69+
};
70+
71+
return LogUtils;
72+
});

app/code/Magento/Ui/view/base/web/js/lib/logger/message-poll.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,26 @@
11
define(function () {
22
'use strict';
33

4-
var MESSAGES = {};
4+
var MESSAGES = {
5+
templateStartLoading:
6+
'The "${ $.template }" template requested by the "${$.component}" component started loading.',
7+
templateLoadedFromServer:
8+
'The "${ $.template }" template requested by the "${$.component}" component was loaded from server."',
9+
templateLoadedFromCache:
10+
'The "${ $.template }" template requested by the "${$.component}" component was loaded from cache."',
11+
templateLoadingFail: 'Failed to load the "${ $.template }" template requested by "${$.component}".',
12+
componentStartInitialization:
13+
'Component "${$.component}" start initialization with instance name "${$.componentName}".',
14+
componentStartLoading: ' Started loading the "${$.component}" component.',
15+
componentFinishLoading: 'The "${$.component}" component was loaded.',
16+
componentLoadingFail: 'Failed to load the "${$.component}" component.',
17+
depsLoadingFail: 'Could not get the declared "${$.deps}" dependency for the "${$.component}" instance.',
18+
depsStartRequesting: 'Requesting the "${$.deps}" dependency for the "${$.component}" instance.',
19+
depsFinishRequesting: 'The "${$.deps}" dependency for the "${$.component}" instance was received.',
20+
requestingComponent: 'Requesting the "${$.component}" component.',
21+
requestingComponentIsLoaded: 'The requested "${$.component}" component was received.',
22+
requestingComponentIsFailed: 'Could not get the requested "${$.component}" component.'
23+
};
524

625
return {
726
/**

lib/web/mage/requirejs/text.js

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -100,35 +100,35 @@ define(['module'], function (module) {
100100
}
101101
}
102102

103+
xhr.onreadystatechange = function (evt) {
104+
var status, err;
105+
//Do not explicitly handle errors, those should be
106+
//visible via console output in the browser.
107+
if (xhr.readyState === 4) {
108+
status = xhr.status || 0;
109+
if (status > 399 && status < 600) {
110+
//An http 4xx or 5xx error. Signal an error.
111+
err = new Error(url + ' HTTP status: ' + status);
112+
err.xhr = xhr;
113+
if (fail) {
114+
fail(err);
115+
}
116+
} else {
117+
callback(xhr.responseText);
118+
119+
if (defaultConfig.onXhrComplete) {
120+
defaultConfig.onXhrComplete(xhr, url);
121+
}
122+
}
123+
}
124+
};
125+
103126
/*eslint-enable max-depth */
104127

105128
if (defaultConfig.onXhr) {
106129
defaultConfig.onXhr(xhr, url);
107130
}
108131

109-
/**
110-
* onload handler
111-
*/
112-
xhr.onload = function () {
113-
114-
callback(xhr.responseText);
115-
116-
if (defaultConfig.onXhrComplete) {
117-
defaultConfig.onXhrComplete(xhr, url);
118-
}
119-
};
120-
121-
/**
122-
* onerror handler
123-
*/
124-
xhr.onerror = function (event) {
125-
errorHandler(event);
126-
127-
if (defaultConfig.onXhrFailure) {
128-
defaultConfig.onXhrFailure(xhr, url, event);
129-
}
130-
};
131-
132132
xhr.send();
133133
}
134134

0 commit comments

Comments
 (0)