builder_apm 0.4.2 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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>