builder_apm 0.4.2 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,274 +2,267 @@
2
2
  <%= render 'builder_apm/js/compress' %>
3
3
 
4
4
  <script>
5
- var allData = null;
6
- var isAsc = false;
7
- var current_sort_field = 'real_start_time';
8
- var start_time = Date.now();
9
- var data_gathered_counter = 0;
10
- var time_cursor = <%= Time.now.to_f * 1000 %>;
11
- var limit = 500;
12
-
13
- function fetchDataAndUpdateStorage(onSuccess) {
14
- start_time = Date.now();
15
- var initialCursor = time_cursor || start_time;
16
- if(allData == null) {
17
- allData = [];
18
- }
19
- // Start fetching data
20
- fetchAndProcessData(initialCursor, onSuccess);
21
-
22
- }
23
- function fetchAndProcessData(cursor, onSuccess) {
24
- var type = typeof fetch_type !== 'undefined' ? fetch_type : 'timestamps';
25
-
26
- // Make an AJAX request to fetch latest data
27
- $.get('/builder_apm/request_data?cursor=' + cursor + '&limit=' + limit + '&type=' +type, function(newData) {
28
- data_gathered_counter++;
29
- newData = newData.map(request => calcDurations(request));
30
-
31
- processData(allData, null, newData, onSuccess);
32
-
33
- });
34
- }
35
-
36
- function loadLocalData(onSuccess) {
37
- if(allData) {
38
- onSuccess(allData);
39
- } else {
40
- var storedData = localStorage.getItem('builder_apm_requests');
41
- if (storedData) {
42
- var dataCompressed = decompress(storedData, function(result, error) {
43
- processData(JSON.parse(result), error, [], onSuccess);
44
- });
45
- } else {
46
- allData = [];
47
- fetchDataAndUpdateStorage(onSuccess);
5
+ var allData = null;
6
+ var isAsc = false;
7
+ var current_sort_field = 'real_start_time';
8
+ var start_time = Date.now();
9
+ var data_gathered_counter = 0;
10
+ var time_cursor = <%= Time.now.to_f * 1000 %>;
11
+ var limit = 500;
12
+
13
+ function fetchDataAndUpdateStorage(onSuccess) {
14
+ start_time = Date.now();
15
+ var initialCursor = time_cursor || start_time;
16
+ if (allData == null) {
17
+ allData = [];
18
+ }
19
+ // Start fetching data
20
+ fetchAndProcessData(initialCursor, onSuccess);
21
+
48
22
  }
49
- }
50
- }
51
-
52
- function processData(storedData, error, newData, onSuccess) {
53
-
54
- // Append the new data to the stored data
55
- allData = storedData.concat(newData);
56
- try {
57
-
58
- // Update the cursor
59
- if (newData.length > 0) {
60
- var lastRequest = newData[newData.length - 1];
61
- time_cursor = lastRequest.end_time-0.001;
62
- }
63
- } catch(e) {
64
- console.error("Error storing data to local storage. Might be out of storage space.", e);
65
- alert('not enough local storage')
23
+
24
+ function fetchAndProcessData(cursor, onSuccess) {
25
+ var type = typeof fetch_type !== 'undefined' ? fetch_type : 'timestamps';
26
+
27
+ // Make an AJAX request to fetch latest data
28
+ $.get('/builder_apm/request_data?cursor=' + cursor + '&limit=' + limit + '&type=' + type, function (newData) {
29
+ data_gathered_counter++;
30
+ newData = newData.map(request => calcDurations(request));
31
+
32
+ processData(allData, null, newData, onSuccess);
33
+
34
+ });
66
35
  }
67
- // Callback function after data fetched and stored
68
- console.log(allData);
69
- onSuccess(allData);
70
- }
71
-
72
- function loadRequest(request_id) {
73
- var requestData = allData.find(function(item) {
74
- return item['request_id'] == request_id;
75
- });
76
-
77
- return requestData;
78
- }
79
-
80
- function autoFetchDataAndUpdateStorage(onSuccess) {
81
- var autoUpdate = $('#autoUpdate').is(':checked');
82
- if (autoUpdate) {
83
- fetchDataAndUpdateStorage(function(updatedData) {
84
- onSuccess(updatedData);
85
- // Schedule the next run
86
- setTimeout(function() {
87
- autoFetchDataAndUpdateStorage(onSuccess);
88
- }, 5000);
36
+
37
+ function loadLocalData(onSuccess) {
38
+ if (allData) {
39
+ onSuccess(allData);
40
+ } else {
41
+ var storedData = localStorage.getItem('builder_apm_requests');
42
+ if (storedData) {
43
+ var dataCompressed = decompress(storedData, function (result, error) {
44
+ processData(JSON.parse(result), error, [], onSuccess);
45
+ });
46
+ } else {
47
+ allData = [];
48
+ fetchDataAndUpdateStorage(onSuccess);
49
+ }
50
+ }
51
+ }
52
+
53
+ function processData(storedData, error, newData, onSuccess) { // Append the new data to the stored data
54
+ allData = storedData.concat(newData);
55
+ try { // Update the cursor
56
+ if (newData.length > 0) {
57
+ var lastRequest = newData[newData.length - 1];
58
+ time_cursor = lastRequest.end_time - 0.001;
59
+ }
60
+ } catch (e) {
61
+ console.error("Error storing data to local storage. Might be out of storage space.", e);
62
+ alert('not enough local storage')
63
+ }
64
+ // Callback function after data fetched and stored
65
+ console.log(allData);
66
+ onSuccess(allData);
67
+ }
68
+
69
+ function loadRequest(request_id) {
70
+ var requestData = allData.find(function (item) {
71
+ return item['request_id'] == request_id;
89
72
  });
90
- } else {
91
- // If auto update is not checked, schedule the next check
92
- setTimeout(function() {
93
- autoFetchDataAndUpdateStorage(onSuccess);
94
- }, 5000);
73
+
74
+ return requestData;
95
75
  }
96
- }
97
76
 
98
- function processStack(stack, processSqlEvent) {
99
- if(stack == null) {
100
- stack = {}
77
+ function autoFetchDataAndUpdateStorage(onSuccess) {
78
+ var autoUpdate = $('#autoUpdate').is(':checked');
79
+ if (autoUpdate) {
80
+ fetchDataAndUpdateStorage(function (updatedData) {
81
+ onSuccess(updatedData);
82
+ // Schedule the next run
83
+ setTimeout(function () {
84
+ autoFetchDataAndUpdateStorage(onSuccess);
85
+ }, 5000);
86
+ });
87
+ } else { // If auto update is not checked, schedule the next check
88
+ setTimeout(function () {
89
+ autoFetchDataAndUpdateStorage(onSuccess);
90
+ }, 5000);
91
+ }
101
92
  }
102
- let sqlEvents = stack.sql_events || [];
103
- let sqlQueries = sqlEvents.map((event, index) => ({
104
- sql: event.sql,
105
- triggeringLine: event.triggering_line,
106
- index: index
107
- }));
108
-
109
- let queriesCount = {};
110
- sqlQueries.forEach(queryData => {
111
- let table = queryData.sql.match(/FROM ['`"]*([^ '`"]+)['`"]*/i)
112
- if (table) {
113
- let tableAndLine = `${table[1]}|${queryData.triggeringLine}`;
114
- queriesCount[tableAndLine] = queriesCount[tableAndLine] || {count: 0, indices: []};
115
- queriesCount[tableAndLine].count += 1;
116
- queriesCount[tableAndLine].indices.push(queryData.index);
93
+
94
+ function processStack(stack, processSqlEvent) {
95
+ if (stack == null) {
96
+ stack = {}
117
97
  }
118
- });
98
+ let sqlEvents = stack.sql_events || [];
99
+ let sqlQueries = sqlEvents.map((event, index) => ({sql: event.sql, triggeringLine: event.triggering_line, index: index}));
100
+
101
+ let queriesCount = {};
102
+ sqlQueries.forEach(queryData => {
103
+ let table = queryData.sql.match(/FROM ['`"]*([^ '`"]+)['`"]*/i)
104
+ if (table) {
105
+ let tableAndLine = `${
106
+ table[1]
107
+ }|${
108
+ queryData.triggeringLine
109
+ }`;
110
+ queriesCount[tableAndLine] = queriesCount[tableAndLine] || {
111
+ count: 0,
112
+ indices: []
113
+ };
114
+ queriesCount[tableAndLine].count += 1;
115
+ queriesCount[tableAndLine].indices.push(queryData.index);
116
+ }
117
+ });
119
118
 
120
- processSqlEvent(sqlEvents, queriesCount);
119
+ processSqlEvent(sqlEvents, queriesCount);
121
120
 
122
- (stack.children || []).forEach(child => processStack(child, processSqlEvent));
123
- }
121
+ (stack.children || []).forEach(child => processStack(child, processSqlEvent));
122
+ }
124
123
 
125
- function tagQueriesWithNPlusOne(requestInput) {
126
- // If requestInput is not an array, convert it into an array
127
- const requestArray = Array.isArray(requestInput) ? requestInput : [requestInput];
124
+ function tagQueriesWithNPlusOne(requestInput) { // If requestInput is not an array, convert it into an array
125
+ const requestArray = Array.isArray(requestInput) ? requestInput : [requestInput];
128
126
 
129
- requestArray.forEach(requestObject => {
130
- // Assume no N+1 issues are found initially
131
- requestObject.has_n_plus_one = false;
127
+ requestArray.forEach(requestObject => { // Assume no N+1 issues are found initially
128
+ requestObject.has_n_plus_one = false;
132
129
 
133
- processStack(requestObject.stack[0], function(sqlEvents, queriesCount) {
134
- // By default, add 'possibleNPlusOne: false' tag to all SQL events
135
- sqlEvents.forEach(event => {
136
- event.possibleNPlusOne = false;
137
- });
130
+ processStack(requestObject.stack[0], function (sqlEvents, queriesCount) { // By default, add 'possibleNPlusOne: false' tag to all SQL events
131
+ sqlEvents.forEach(event => {
132
+ event.possibleNPlusOne = false;
133
+ });
138
134
 
139
- for (let tableAndLine in queriesCount) {
140
- if (queriesCount[tableAndLine].count > 1) {
141
- // If any N+1 issue is found, set 'has_n_plus_one' to true
142
- requestObject.has_n_plus_one = true;
135
+ for (let tableAndLine in queriesCount) {
136
+ if (queriesCount[tableAndLine].count > 1) { // If any N+1 issue is found, set 'has_n_plus_one' to true
137
+ requestObject.has_n_plus_one = true;
143
138
 
144
- queriesCount[tableAndLine].indices.forEach(index => {
145
- sqlEvents[index].possibleNPlusOne = true; // Update the tag to true for N+1 issues
146
- });
139
+ queriesCount[tableAndLine].indices.forEach(index => {
140
+ sqlEvents[index].possibleNPlusOne = true; // Update the tag to true for N+1 issues
141
+ });
142
+ }
147
143
  }
148
- }
144
+ });
149
145
  });
150
- });
151
- }
152
-
153
- function addSortingClick() {
154
- // Assuming your table headers have a class name "sortable"
155
- $('.sortable').click(function() {
156
- if (current_sort_field == $(this).data('field')) {
157
- isAsc = !isAsc;
158
- } else {
159
- isAsc = false;
160
146
  }
161
- current_sort_field = $(this).data('field'); // Assuming data-field attribute contains the name of the field to sort
162
147
 
163
- renderTable(allData);
164
- });
165
- }
148
+ function addSortingClick() { // Assuming your table headers have a class name "sortable"
149
+ $('.sortable').click(function () {
150
+ if (current_sort_field == $(this).data('field')) {
151
+ isAsc = ! isAsc;
152
+ } else {
153
+ isAsc = false;
154
+ } current_sort_field = $(this).data('field'); // Assuming data-field attribute contains the name of the field to sort
166
155
 
167
- function sortDataBy(field, data) {
168
- data.sort(function(a, b) {
169
- if (a[field] < b[field]) {
170
- return isAsc ? -1 : 1;
156
+ renderTable(allData);
157
+ });
171
158
  }
172
- if (a[field] > b[field]) {
173
- return isAsc ? 1 : -1;
159
+
160
+ function sortDataBy(field, data) {
161
+ data.sort(function (a, b) {
162
+ if (a[field] < b[field]) {
163
+ return isAsc ? -1 : 1;
164
+ }
165
+ if (a[field] > b[field]) {
166
+ return isAsc ? 1 : -1;
167
+ }
168
+ return 0; // equal
169
+ });
170
+
171
+ return data;
174
172
  }
175
- return 0; // equal
176
- });
177
173
 
178
- return data;
179
- }
174
+ function getNPlusOneRequests(requestArray) {
175
+ let requestsWithIssues = [];
180
176
 
181
- function getNPlusOneRequests(requestArray) {
182
- let requestsWithIssues = [];
177
+ requestArray.forEach(requestObject => {
178
+ let hasNPlusOne = false;
183
179
 
184
- requestArray.forEach(requestObject => {
185
- let hasNPlusOne = false;
180
+ processStack(requestObject.stack[0], function (sqlEvents) {
181
+ if (sqlEvents.some(event => event.possibleNPlusOne)) {
182
+ hasNPlusOne = true;
183
+ }
184
+ });
186
185
 
187
- processStack(requestObject.stack[0], function(sqlEvents) {
188
- if (sqlEvents.some(event => event.possibleNPlusOne)) {
189
- hasNPlusOne = true;
186
+ if (hasNPlusOne) {
187
+ requestsWithIssues.push(requestObject);
190
188
  }
191
189
  });
192
190
 
193
- if (hasNPlusOne) {
194
- requestsWithIssues.push(requestObject);
195
- }
196
- });
197
-
198
- return requestsWithIssues;
199
- }
200
- function aggregateRequests(requestArray) {
201
- let aggregates = {};
202
- requestArray.forEach(request => {
203
- let key = `${request.controller_action}`;
204
- if (!aggregates[key]) {
205
- aggregates[key] = {
206
- count: 0,
207
- method: request.method,
208
- path: request.path,
209
- totalDuration: 0,
210
- totalDbRuntime: 0,
211
- totalViewRuntime: 0,
212
- slowestDuration: 0,
213
- slowestDbRuntime: 0,
214
- slowestViewRuntime: 0
215
- };
216
- }
217
-
218
- aggregates[key].count++;
219
- aggregates[key].totalDuration += request.duration;
220
- aggregates[key].totalDbRuntime += request.db_runtime || 0;
221
- aggregates[key].totalViewRuntime += request.view_runtime || 0;
222
- aggregates[key].slowestDuration = Math.max(aggregates[key].slowestDuration, request.duration);
223
- aggregates[key].slowestDbRuntime = Math.max(aggregates[key].slowestDbRuntime, request.db_runtime || 0);
224
- aggregates[key].slowestViewRuntime = Math.max(aggregates[key].slowestViewRuntime, request.view_runtime || 0);
225
- });
226
-
227
- let results = [];
228
- for (let key in aggregates) {
229
- let controller = key;
230
- results.push({
231
- controller,
232
- method: aggregates[key].method,
233
- path: aggregates[key].path,
234
- count: aggregates[key].count,
235
- averageDuration: aggregates[key].totalDuration / aggregates[key].count,
236
- averageDbRuntime: aggregates[key].totalDbRuntime / aggregates[key].count,
237
- averageViewRuntime: aggregates[key].totalViewRuntime / aggregates[key].count,
238
- slowestDuration: aggregates[key].slowestDuration,
239
- slowestDbRuntime: aggregates[key].slowestDbRuntime,
240
- slowestViewRuntime: aggregates[key].slowestViewRuntime,
241
- });
191
+ return requestsWithIssues;
242
192
  }
243
- return results;
244
- }
193
+ function aggregateRequests(requestArray) {
194
+ let aggregates = {};
195
+ requestArray.forEach(request => {
196
+ let key = `${
197
+ request.controller_action
198
+ }`;
199
+ if (! aggregates[key]) {
200
+ aggregates[key] = {
201
+ count: 0,
202
+ method: request.method,
203
+ path: request.path,
204
+ totalDuration: 0,
205
+ totalDbRuntime: 0,
206
+ totalViewRuntime: 0,
207
+ slowestDuration: 0,
208
+ slowestDbRuntime: 0,
209
+ slowestViewRuntime: 0
210
+ };
211
+ }
245
212
 
213
+ aggregates[key].count ++;
214
+ aggregates[key].totalDuration += request.duration;
215
+ aggregates[key].totalDbRuntime += request.db_runtime || 0;
216
+ aggregates[key].totalViewRuntime += request.view_runtime || 0;
217
+ aggregates[key].slowestDuration = Math.max(aggregates[key].slowestDuration, request.duration);
218
+ aggregates[key].slowestDbRuntime = Math.max(aggregates[key].slowestDbRuntime, request.db_runtime || 0);
219
+ aggregates[key].slowestViewRuntime = Math.max(aggregates[key].slowestViewRuntime, request.view_runtime || 0);
220
+ });
246
221
 
247
- function calcDurations(requestObj) {
248
- function iterateStack(stack) {
249
- for (let item of stack) {
250
- item.calc_duration = item.duration;
251
- item.calc_db_runtime = 0;
252
- for (let event of item.sql_events) {
253
- item.calc_db_runtime += event.duration;
254
- }
255
- if (item.children.length > 0) {
256
- let childrenTotals = iterateStack(item.children);
257
- item.calc_duration += childrenTotals.duration;
258
- item.calc_db_runtime += childrenTotals.dbRuntime;
259
- }
222
+ let results = [];
223
+ for (let key in aggregates) {
224
+ let controller = key;
225
+ results.push({
226
+ controller,
227
+ method: aggregates[key].method,
228
+ path: aggregates[key].path,
229
+ count: aggregates[key].count,
230
+ averageDuration: aggregates[key].totalDuration / aggregates[key].count,
231
+ averageDbRuntime: aggregates[key].totalDbRuntime / aggregates[key].count,
232
+ averageViewRuntime: aggregates[key].totalViewRuntime / aggregates[key].count,
233
+ slowestDuration: aggregates[key].slowestDuration,
234
+ slowestDbRuntime: aggregates[key].slowestDbRuntime,
235
+ slowestViewRuntime: aggregates[key].slowestViewRuntime
236
+ });
260
237
  }
261
- let totalDuration = stack.reduce((acc, curr) => acc + curr.calc_duration, 0);
262
- let totalDbRuntime = stack.reduce((acc, curr) => acc + curr.calc_db_runtime, 0);
263
- return {duration: totalDuration, dbRuntime: totalDbRuntime};
264
- }
265
- if(requestObj.stack == null) {
266
- requestObj.stack = [];
238
+ return results;
267
239
  }
268
- let totals = iterateStack(requestObj.stack);
269
- requestObj.calc_duration = totals.duration;
270
- requestObj.calc_db_runtime = totals.dbRuntime;
271
- return requestObj;
272
- }
273
240
 
274
241
 
275
- </script>
242
+ function calcDurations(requestObj) {
243
+ function iterateStack(stack) {
244
+ for (let item of stack) {
245
+ item.calc_duration = item.duration;
246
+ item.calc_db_runtime = 0;
247
+ for (let event of item.sql_events) {
248
+ item.calc_db_runtime += event.duration;
249
+ }
250
+ if (item.children.length > 0) {
251
+ let childrenTotals = iterateStack(item.children);
252
+ item.calc_duration += childrenTotals.duration;
253
+ item.calc_db_runtime += childrenTotals.dbRuntime;
254
+ }
255
+ }
256
+ let totalDuration = stack.reduce((acc, curr) => acc + curr.calc_duration, 0);
257
+ let totalDbRuntime = stack.reduce((acc, curr) => acc + curr.calc_db_runtime, 0);
258
+ return {duration: totalDuration, dbRuntime: totalDbRuntime};
259
+ }
260
+ if (requestObj.stack == null) {
261
+ requestObj.stack = [];
262
+ }
263
+ let totals = iterateStack(requestObj.stack);
264
+ requestObj.calc_duration = totals.duration;
265
+ requestObj.calc_db_runtime = totals.dbRuntime;
266
+ return requestObj;
267
+ }
268
+ </script>