builder_apm 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c694ae0d5543f44bb905abf9456e8768e3ae547bd99066fe0012f12e11aa89f
4
- data.tar.gz: 1df6c105dbe848d600f1ae5197a64c2fe2816584072d4fb82ee69850dc6f61d6
3
+ metadata.gz: 15ea10eca92143c789a8dcb4c171d5492993f85685f41c0becd0e666fcc9bc74
4
+ data.tar.gz: 765ad18f703caadf903d23f610e9b01d10a63d237871a3a6e5203e55970528bc
5
5
  SHA512:
6
- metadata.gz: 21f191411ce9106d7db006b92dc12ffd72efa414f62730f42bb26cdacc77ac0f841d9cabb2be482128ce3577ea9f67f5eadf8421c7ed6d0a826ab924cb40ef35
7
- data.tar.gz: fcc1962c887a01e84cca5b1a7afa1deced47ef7ffcadefe2d26db6126f56c9bc99b7d0b52b9e65296ccf5a4af01bd6c131d043d50c370990364c37d6e0733637
6
+ metadata.gz: eb733f08909db54b9925e5b3ec4e5c5ff426dceeb65edbf977c049da87cf4f0abca214e67da1015bf9add472b995986bc3e1c8d9633cecfa2fe0ca5a25fdc919
7
+ data.tar.gz: b38eb27b9e73dc180f75124c0328a360bab162112ad12b48e21a0b676b90e1a03182d85e17e421d0b4a616a3e21e762c93f6e26a750a3a3447fc78e6aa3ec6d7
data/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # BuilderApm
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/builder_apm`. To experiment with that code, run `bin/console` for an interactive prompt.
4
3
 
5
- TODO: Delete this and the text above, and describe your gem
6
4
 
7
5
  ## Installation
8
6
 
@@ -23,28 +21,9 @@ Or install it yourself as:
23
21
  run
24
22
  ```
25
23
  rails generate builder_apm:install
26
- rails db:migrate
27
24
  ```
28
25
 
29
26
  ## Usage
30
27
 
31
28
  TODO: Write usage instructions here
32
29
 
33
- ## Development
34
-
35
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
-
37
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
-
39
- ## Contributing
40
-
41
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/builder_apm. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/builder_apm/blob/master/CODE_OF_CONDUCT.md).
42
-
43
-
44
- ## License
45
-
46
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
47
-
48
- ## Code of Conduct
49
-
50
- Everyone interacting in the BuilderApm project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/builder_apm/blob/master/CODE_OF_CONDUCT.md).
@@ -1,41 +1,30 @@
1
1
  module BuilderApm
2
2
  class RequestDataController < ApplicationController
3
3
  def index
4
- cursor = params[:cursor].presence&.to_f || Time.now.to_f
4
+ cursor = params[:cursor].presence&.to_f || Time.now.to_f * 1000
5
5
  limit = params[:limit].presence&.to_i || 50
6
6
  type = params[:type].presence || 'timestamps'
7
7
 
8
8
  purge_old_keys
9
9
  @requests = retrieve_data_from_redis_since(cursor, limit, type)
10
-
10
+ Rails.logger.info "#{@requests.size} results gathered"
11
11
  render json: @requests
12
12
  end
13
13
 
14
14
  private
15
15
 
16
16
  def purge_old_keys
17
- time_limit = (Time.now - 1.hour).to_f * 1000
17
+ time_limit = (Time.now - 2.hour).to_f * 1000
18
18
  redis_client.zremrangebyscore('builder_apm:timestamps', '-inf', time_limit)
19
19
  redis_client.zremrangebyscore('builder_apm:errors', '-inf', time_limit)
20
20
  redis_client.zremrangebyscore('builder_apm:n_plus_one', '-inf', time_limit)
21
21
  redis_client.zremrangebyscore('builder_apm:slow', '-inf', time_limit)
22
+ redis_client.zremrangebyscore('builder_apm:dashboard', '-inf', time_limit)
22
23
  end
23
24
 
24
25
  def retrieve_data_from_redis_since(cursor, limit, type)
25
- # Use ZREVRANGEBYSCORE to get a range of keys, then get the corresponding data
26
- # if(type == 'timestamps')
27
- keys = redis_client.zrevrangebyscore("builder_apm:#{type}", cursor, "-inf", limit: [0, limit])
28
- # else
29
- # keys = redis_client.lrange("builder_apm:#{type}", 0, -1)
30
- # end
31
-
32
- keys = keys.map { |key| "builder_apm:Request:#{key}" }
33
- data = keys.empty? ? [] : redis_client.mget(*keys)
34
- begin
35
- data.map { |d| JSON.parse(d) }
36
- rescue => e
37
- raise e
38
- end
26
+ data = redis_client.zrevrangebyscore("builder_apm:#{type}", cursor, "-inf", limit: [0, limit])
27
+ data.map{ |d| JSON.parse(d) }
39
28
  end
40
29
  end
41
30
  end
@@ -4,7 +4,7 @@
4
4
  <thead>
5
5
  <tr>
6
6
  <th class="sortable" data-field="start_time">Time</th>
7
- <th class="sortable" data-field="controller">Controller#Action</th>
7
+ <th class="sortable" data-field="controller_action">Controller#Action</th>
8
8
  <th class="sortable" data-field="method">Method</th>
9
9
  <th class="sortable" data-field="status">Status</th>
10
10
  <th class="sortable" data-field="exception_class">Error Class</th>
@@ -5,6 +5,7 @@ google.charts.load('current', {packages: ['corechart', 'line']});
5
5
  google.charts.setOnLoadCallback(drawCharts);
6
6
 
7
7
  function drawCharts() {
8
+ limit = 3000
8
9
  prepareChartData(aggregationInterval);
9
10
  autoFetchDataAndUpdateStorage(function(updatedData){
10
11
  // Render the table with the updated data
@@ -39,14 +40,14 @@ function prepareChartData(aggregationInterval) {
39
40
 
40
41
  // Add current request data to interval totals
41
42
  aggregatedData[timeKey]['count'] += 1;
42
- aggregatedData[timeKey]['totalDuration'] += (item['real_duration_time']);
43
- aggregatedData[timeKey]['totalDbRuntime'] += item['calc_db_runtime'];
43
+ aggregatedData[timeKey]['totalDuration'] += (item['duration']);
44
+ aggregatedData[timeKey]['totalDbRuntime'] += item['db_runtime'];
44
45
  aggregatedData[timeKey]['totalViewRuntime'] += item['view_runtime'];
45
- aggregatedData[timeKey]['totalOther'] += item['real_duration_time'] - item['calc_db_runtime'] - item['view_runtime'];
46
+ aggregatedData[timeKey]['totalOther'] += item['duration'] - item['db_runtime'] - item['view_runtime'];
46
47
  // aggregatedData[timeKey]['durations'].push(item['duration']);
47
- // aggregatedData[timeKey]['totalDbRuntime'] += item['real_duration_time']*0.35;
48
- // aggregatedData[timeKey]['totalViewRuntime'] += item['real_duration_time']*0.45;
49
- // aggregatedData[timeKey]['totalOther'] += item['real_duration_time']*0.2;
48
+ // aggregatedData[timeKey]['totalDbRuntime'] += item['duration']*0.35;
49
+ // aggregatedData[timeKey]['totalViewRuntime'] += item['duration']*0.45;
50
+ // aggregatedData[timeKey]['totalOther'] += item['duration']*0.2;
50
51
  });
51
52
 
52
53
 
@@ -8,6 +8,7 @@ var current_sort_field = 'real_start_time';
8
8
  var start_time = Date.now();
9
9
  var data_gathered_counter = 0;
10
10
  var time_cursor = <%= Time.now.to_f * 1000 %>;
11
+ var limit = 500;
11
12
 
12
13
  function fetchDataAndUpdateStorage(onSuccess) {
13
14
  start_time = Date.now();
@@ -20,7 +21,6 @@ function fetchDataAndUpdateStorage(onSuccess) {
20
21
 
21
22
  }
22
23
  function fetchAndProcessData(cursor, onSuccess) {
23
- var limit = 1000;
24
24
  var type = typeof fetch_type !== 'undefined' ? fetch_type : 'timestamps';
25
25
 
26
26
  // Make an AJAX request to fetch latest data
@@ -29,26 +29,6 @@ function fetchAndProcessData(cursor, onSuccess) {
29
29
  newData = newData.map(request => calcDurations(request));
30
30
 
31
31
  processData(allData, null, newData, onSuccess);
32
-
33
- // Find the latest request in the newly fetched data
34
- // var latestRequest = newData.reduce((latest, current) => current.real_end_time > latest.real_end_time ? current : latest, newData[0]);
35
-
36
- // If latest request's end_time is less than start_time, and there's more data (received data is equal to limit), fetch next batch
37
- // if (data_gathered_counter < 5 && latestRequest && latestRequest.real_end_time < start_time && newData.length === limit) {
38
- // fetchAndProcessData(var time_cursor = <%= Time.now.to_f * 1000 %>;, onSuccess);
39
- // } else {
40
- // try {
41
- // // Store the updated data back to local storage
42
- // compress(JSON.stringify(allData), function(result, error) {
43
- // if(result != null) {
44
- // localStorage.setItem('builder_apm_requests', result);
45
- // }
46
- // });
47
- // } catch(e) {
48
- // console.error("Error storing data to local storage. Might be out of storage space.", e);
49
- // alert('not enough local storage')
50
- // }
51
- // onSuccess(allData);
52
32
 
53
33
  });
54
34
  }
@@ -78,7 +58,7 @@ function processData(storedData, error, newData, onSuccess) {
78
58
  // Update the cursor
79
59
  if (newData.length > 0) {
80
60
  var lastRequest = newData[newData.length - 1];
81
- time_cursor = lastRequest.real_end_time-0.001;
61
+ time_cursor = lastRequest.end_time-0.001;
82
62
  }
83
63
  } catch(e) {
84
64
  console.error("Error storing data to local storage. Might be out of storage space.", e);
@@ -220,7 +200,7 @@ function getNPlusOneRequests(requestArray) {
220
200
  function aggregateRequests(requestArray) {
221
201
  let aggregates = {};
222
202
  requestArray.forEach(request => {
223
- let key = `${request.controller}#${request.action}`;
203
+ let key = `${request.controller_action}`;
224
204
  if (!aggregates[key]) {
225
205
  aggregates[key] = {
226
206
  count: 0,
@@ -40,7 +40,7 @@ $(document).ready(function() {
40
40
  var row = $('<tr>');
41
41
 
42
42
  $('<td>').text(new Date(item['start_time']).toLocaleString()).appendTo(row);
43
- $('<td>').addClass('long_text').text(item['controller'] + '#' + item['action']).appendTo(row);
43
+ $('<td>').addClass('long_text').text(item['controller_action']).appendTo(row);
44
44
  $('<td>').text(item['method']).appendTo(row);
45
45
  $('<td>').text(item['status']).appendTo(row);
46
46
  $('<td>').append(item['exception_class']).appendTo(row);
@@ -57,13 +57,10 @@ $(document).ready(function() {
57
57
 
58
58
  // Action column
59
59
  var actionTd = $('<td>');
60
- var actionButton = $('<button>').text('Details');
61
- actionButton.on('click', function() {
62
- // Replace 'your-details-url' with the actual URL where the details are to be fetched.
63
- // It's assumed the ID is required as a URL parameter, modify as per your requirements.
64
- window.location.href = '<%= request_details_path %>?request_id=' + item['request_id'];
65
- });
66
- actionButton.appendTo(actionTd);
60
+ var actionLink = $('<a>').text('Details');
61
+ actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
62
+ actionLink.attr('target', '_blank');
63
+ actionLink.appendTo(actionTd);
67
64
  actionTd.appendTo(row);
68
65
 
69
66
  // Append the row to the table body
@@ -39,22 +39,19 @@ $(document).ready(function() {
39
39
  var row = $('<tr>');
40
40
 
41
41
  $('<td>').text(new Date(item['start_time']).toLocaleString()).appendTo(row);
42
- $('<td>').addClass('long_text').text(item['controller'] + '#' + item['action']).appendTo(row);
42
+ $('<td>').addClass('long_text').text(item['controller_action']).appendTo(row);
43
43
  $('<td>').text(item['method']).appendTo(row);
44
44
  $('<td>').text(item['status']).appendTo(row);
45
45
  $('<td>').append(renderDuration(item['duration'])).appendTo(row);
46
46
  $('<td>').append(renderDuration(item['db_runtime'])).appendTo(row);
47
47
  $('<td>').append(renderDuration(item['view_runtime'])).appendTo(row);
48
48
  // Action column
49
+
49
50
  var actionTd = $('<td>');
50
-
51
- if(item.stack && item.stack.length > 0) {
52
-
53
- var actionLink = $('<a>').text('Details');
54
- actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
55
- actionLink.attr('target', '_blank');
56
- actionLink.appendTo(actionTd);
57
- }
51
+ var actionLink = $('<a>').text('Details');
52
+ actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
53
+ actionLink.attr('target', '_blank');
54
+ actionLink.appendTo(actionTd);
58
55
  actionTd.appendTo(row);
59
56
 
60
57
  // Append the row to the table body
@@ -42,22 +42,18 @@ $(document).ready(function() {
42
42
  var row = $('<tr>');
43
43
 
44
44
  $('<td>').text(new Date(item['start_time']).toLocaleString()).appendTo(row);
45
- $('<td>').addClass('long_text').text(item['controller'] + '#' + item['action']).appendTo(row);
45
+ $('<td>').addClass('long_text').text(item['controller_action']).appendTo(row);
46
46
  $('<td>').text(item['method']).appendTo(row);
47
47
  $('<td>').text(item['status']).appendTo(row);
48
- $('<td>').append(renderDuration(item['real_duration_time'])).appendTo(row);
49
- $('<td>').append(renderDuration(item['calc_db_runtime'])).appendTo(row);
48
+ $('<td>').append(renderDuration(item['duration'])).appendTo(row);
49
+ $('<td>').append(renderDuration(item['db_runtime'])).appendTo(row);
50
50
  $('<td>').append(renderDuration(item['view_runtime'])).appendTo(row);
51
51
  // Action column
52
52
  var actionTd = $('<td>');
53
-
54
- if(item.stack && item.stack.length > 0) {
55
-
56
- var actionLink = $('<a>').text('Details');
57
- actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
58
- actionLink.attr('target', '_blank');
59
- actionLink.appendTo(actionTd);
60
- }
53
+ var actionLink = $('<a>').text('Details');
54
+ actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
55
+ actionLink.attr('target', '_blank');
56
+ actionLink.appendTo(actionTd);
61
57
  actionTd.appendTo(row);
62
58
 
63
59
  // Append the row to the table body
@@ -1,5 +1,6 @@
1
1
  <script>
2
2
  $(document).ready(function() {
3
+ limit = 1000
3
4
  var storedData = localStorage.getItem('builder_apm_requests');
4
5
  if (storedData) {
5
6
  var dataCompressed = decompress(storedData, function(result, error) {
@@ -40,7 +40,7 @@ $(document).ready(function() {
40
40
  var row = $('<tr>');
41
41
 
42
42
  $('<td>').text(new Date(item['start_time']).toLocaleString()).appendTo(row);
43
- $('<td>').addClass('long_text').text(item['controller'] + '#' + item['action']).appendTo(row);
43
+ $('<td>').addClass('long_text').text(item['controller_action']).appendTo(row);
44
44
  $('<td>').text(item['method']).appendTo(row);
45
45
  $('<td>').text(item['status']).appendTo(row);
46
46
  $('<td>').append(renderDuration(item['duration'])).appendTo(row);
@@ -49,13 +49,12 @@ $(document).ready(function() {
49
49
  // Action column
50
50
  var actionTd = $('<td>');
51
51
 
52
- if(item.stack && item.stack.length > 0) {
53
-
54
- var actionLink = $('<a>').text('Details');
55
- actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
56
- actionLink.attr('target', '_blank');
57
- actionLink.appendTo(actionTd);
58
- }
52
+
53
+ var actionTd = $('<td>');
54
+ var actionLink = $('<a>').text('Details');
55
+ actionLink.attr('href', '<%= request_details_path %>?request_id=' + item['request_id']);
56
+ actionLink.attr('target', '_blank');
57
+ actionLink.appendTo(actionTd);
59
58
  actionTd.appendTo(row);
60
59
 
61
60
  // Append the row to the table body
@@ -4,11 +4,11 @@
4
4
  <thead>
5
5
  <tr>
6
6
  <th class="sortable" data-field="start_time">Time</th>
7
- <th class="sortable" data-field="controller">Controller#Action</th>
7
+ <th class="sortable" data-field="controller_action">Controller#Action</th>
8
8
  <th class="sortable" data-field="method">Method</th>
9
9
  <th class="sortable" data-field="status">Status</th>
10
- <th class="sortable" data-field="real_duration_time">Duration (ms)</th>
11
- <th class="sortable" data-field="calc_db_runtime">DB Runtime (ms)</th>
10
+ <th class="sortable" data-field="duration">Duration (ms)</th>
11
+ <th class="sortable" data-field="db_runtime">DB Runtime (ms)</th>
12
12
  <th class="sortable" data-field="view_runtime">View Runtime (ms)</th>
13
13
  </tr>
14
14
  </thead>
@@ -4,11 +4,11 @@
4
4
  <thead>
5
5
  <tr>
6
6
  <th class="sortable" data-field="start_time">Time</th>
7
- <th class="sortable" data-field="controller">Controller#Action</th>
7
+ <th class="sortable" data-field="controller_action">Controller#Action</th>
8
8
  <th class="sortable" data-field="method">Method</th>
9
9
  <th class="sortable" data-field="status">Status</th>
10
- <th class="sortable" data-field="real_duration_time">Duration (ms)</th>
11
- <th class="sortable" data-field="calc_db_runtime">DB Runtime (ms)</th>
10
+ <th class="sortable" data-field="duration">Duration (ms)</th>
11
+ <th class="sortable" data-field="db_runtime">DB Runtime (ms)</th>
12
12
  <th class="sortable" data-field="view_runtime">View Runtime (ms)</th>
13
13
  </tr>
14
14
  </thead>
@@ -4,11 +4,11 @@
4
4
  <thead>
5
5
  <tr>
6
6
  <th class="sortable" data-field="start_time">Time</th>
7
- <th class="sortable" data-field="controller">Controller#Action</th>
7
+ <th class="sortable" data-field="controller_action">Controller#Action</th>
8
8
  <th class="sortable" data-field="method">Method</th>
9
9
  <th class="sortable" data-field="status">Status</th>
10
- <th class="sortable" data-field="real_duration_time">Duration (ms)</th>
11
- <th class="sortable" data-field="calc_db_runtime">DB Runtime (ms)</th>
10
+ <th class="sortable" data-field="duration">Duration (ms)</th>
11
+ <th class="sortable" data-field="db_runtime">DB Runtime (ms)</th>
12
12
  <th class="sortable" data-field="view_runtime">View Runtime (ms)</th>
13
13
  </tr>
14
14
  </thead>
@@ -8,9 +8,10 @@ module BuilderApm
8
8
 
9
9
  def call(env)
10
10
  request_id = env["action_dispatch.request_id"]
11
- Thread.current["request_id"] = request_id
11
+ Thread.current[:request_id] = request_id
12
12
  Thread.current[:n_plus_one_duration] = 0
13
13
  Thread.current[:has_n_plus_one] = false
14
+ Thread.current[:db_runtime] = 0
14
15
  start_time = Time.now.to_f * 1000
15
16
 
16
17
  @status, @headers, @response = @app.call(env)
@@ -20,47 +21,99 @@ module BuilderApm
20
21
  end_time = Time.now.to_f * 1000
21
22
  handle_timing(start_time, end_time, request_id)
22
23
 
23
- Thread.current['request_data'] = nil
24
- Thread.current[:has_n_plus_one] = nil
25
- Thread.current[:n_plus_one_duration] = nil
24
+ clean_up_thread_values
26
25
 
27
26
  [@status, @headers, @response]
28
27
  end
29
28
 
30
29
  private
31
30
 
31
+ def clean_up_thread_values
32
+ Thread.current['request_data'] = nil
33
+ Thread.current[:request_id] = nil
34
+ Thread.current[:has_n_plus_one] = nil
35
+ Thread.current[:n_plus_one_duration] = nil
36
+ Thread.current[:db_runtime] = nil
37
+ Thread.current[:stack] = nil
38
+ Thread.current[:sql_event_id] = nil
39
+ end
40
+
32
41
  def handle_timing(start_time, end_time, request_id)
33
42
  duration = end_time - start_time;
34
43
  data = Thread.current['request_data']
35
44
 
36
45
  if data
46
+ data[:controller] = data[:controller].gsub("::", ":") if data[:controller]
37
47
  data[:has_n_plus_one] = Thread.current[:has_n_plus_one]
38
48
  data[:n_plus_one_duration] = Thread.current[:n_plus_one_duration]
39
49
  data[:real_start_time] = start_time
40
50
  data[:real_end_time] = end_time
51
+ data[:calc_db_runtime] = Thread.current[:db_runtime]
41
52
  data[:real_duration_time] = end_time - start_time
42
53
  data[:stack][0][:start_time] = start_time
43
54
  data[:stack][0][:end_time] = end_time
44
55
  data[:stack][0][:duration] = end_time - start_time
45
56
 
46
- begin
47
- @redis_client.pipelined do |pipeline|
48
- pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:duration", data[:real_duration_time]||0)
49
- pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:db_runtime", data[:db_runtime]||0)
50
- pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:view_runtime", data[:view_runtime]||0)
51
-
52
- pipeline.zadd("builder_apm:timestamps", end_time, request_id)
53
- pipeline.zadd("builder_apm:errors", end_time, request_id) if data[:status] == 500
54
- pipeline.zadd("builder_apm:n_plus_one", end_time, request_id) if data[:has_n_plus_one]
55
- pipeline.zadd("builder_apm:slow", end_time, request_id) if data[:real_duration_time] > 1500
56
- ttl = (1.hour + 1.minute).to_i
57
- pipeline.set("builder_apm:Request:#{data[:request_id]}", data.to_json, ex: ttl)
58
- end
59
- rescue => e
60
- Rails.logger.error "Redis Missing"
57
+ save_to_redis(data)
58
+ end
59
+ end
60
+
61
+ def save_to_redis(data)
62
+ begin
63
+ @redis_client.pipelined do |pipeline|
64
+ pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:duration", data[:real_duration_time]||0)
65
+ pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:db_runtime", data[:db_runtime]||0)
66
+ pipeline.rpush("builder_apm:Analysis:#{data[:controller]}##{data[:action]}:view_runtime", data[:view_runtime]||0)
67
+ end_time = data[:real_end_time]
68
+ simplfied_data = table_data(data)
69
+ pipeline.zadd("builder_apm:dashboard", end_time, dashboard_data(data))
70
+ pipeline.zadd("builder_apm:timestamps", end_time, simplfied_data)
71
+ pipeline.zadd("builder_apm:errors", end_time, table_data_with_errors(data)) if data[:status] == 500
72
+ pipeline.zadd("builder_apm:n_plus_one", end_time, simplfied_data) if data[:has_n_plus_one]
73
+ pipeline.zadd("builder_apm:slow", end_time, simplfied_data) if data[:real_duration_time] > 1500
74
+ ttl = (2.hour + 1.minute).to_i
75
+ pipeline.set("builder_apm:Request:#{data[:request_id]}", data.to_json, ex: ttl)
61
76
  end
77
+ rescue => e
78
+ Rails.logger.error "Redis Missing?"
79
+ Rails.logger.error e.message
80
+ Rails.logger.error e.backtrace
62
81
  end
63
82
  end
83
+
84
+ def dashboard_data(data)
85
+ JSON.dump({
86
+ start_time: data[:real_start_time],
87
+ duration: data[:real_duration_time],
88
+ db_runtime: data[:calc_db_runtime],
89
+ view_runtime: data[:view_runtime]
90
+ })
91
+ end
92
+
93
+ def table_data_with_errors(data)
94
+ tbl_data = table_data(data, false)
95
+
96
+ tbl_data[:exception_class] = data[:exception_class] || ""
97
+ tbl_data[:exception_message] = data[:exception_message] || ""
98
+ tbl_data[:exception_backtrace] = data[:exception_backtrace] || []
99
+
100
+ JSON.dump(tbl_data)
101
+ end
102
+
103
+ def table_data(data, jsonify = true)
104
+ tbl_data = {
105
+ start_time: data[:real_start_time],
106
+ end_time: data[:real_end_time],
107
+ controller_action: "#{data[:controller]}##{data[:action]}",
108
+ method: data[:method],
109
+ status: data[:status],
110
+ duration: data[:real_duration_time],
111
+ db_runtime: data[:calc_db_runtime] > 0 ? data[:calc_db_runtime] : data[:db_runtime],
112
+ view_runtime: data[:view_runtime],
113
+ request_id: data[:request_id]
114
+ }
115
+ jsonify ? JSON.dump(tbl_data) : tbl_data
116
+ end
64
117
  end
65
118
  end
66
119
  end
@@ -19,7 +19,7 @@ module BuilderApm
19
19
  return if name == "SCHEMA" || Thread.current[:request_id].nil?
20
20
 
21
21
  triggering_line = determine_triggering_line(caller)
22
-
22
+ Thread.current[:db_runtime] += event.duration
23
23
  sql_query_data = build_sql_query_data(event, triggering_line)
24
24
  store_sql_query_data(sql_query_data)
25
25
  end
@@ -1,3 +1,3 @@
1
1
  module BuilderApm
2
- VERSION = "0.3.2"
2
+ VERSION = "0.4.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: builder_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Ketelle
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-26 00:00:00.000000000 Z
11
+ date: 2023-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -53,11 +53,8 @@ extra_rdoc_files: []
53
53
  files:
54
54
  - ".gitignore"
55
55
  - ".rspec"
56
- - ".travis.yml"
57
- - CODE_OF_CONDUCT.md
58
56
  - Gemfile
59
57
  - Gemfile.lock
60
- - LICENSE.txt
61
58
  - README.md
62
59
  - Rakefile
63
60
  - app/controllers/builder_apm/application_controller.rb
@@ -94,9 +91,6 @@ files:
94
91
  - app/views/builder_apm/wip/index.html.erb
95
92
  - bin/console
96
93
  - bin/setup
97
- - builder_apm-0.2.5.gem
98
- - builder_apm-0.3.0.gem
99
- - builder_apm-0.3.1.gem
100
94
  - builder_apm.gemspec
101
95
  - config/routes.rb
102
96
  - lib/builder_apm.rb
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7.2
6
- before_install: gem install bundler -v 2.1.4
data/CODE_OF_CONDUCT.md DELETED
@@ -1,74 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
11
-
12
- ## Our Standards
13
-
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
16
-
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
22
-
23
- Examples of unacceptable behavior by participants include:
24
-
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
28
- * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
31
- * Other conduct which could reasonably be considered inappropriate in a
32
- professional setting
33
-
34
- ## Our Responsibilities
35
-
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
39
-
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
45
-
46
- ## Scope
47
-
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
-
55
- ## Enforcement
56
-
57
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at TODO: Write your email address. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
-
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
67
-
68
- ## Attribution
69
-
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [https://contributor-covenant.org/version/1/4][version]
72
-
73
- [homepage]: https://contributor-covenant.org
74
- [version]: https://contributor-covenant.org/version/1/4/
data/LICENSE.txt DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2023 TODO: Write your name
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
Binary file
Binary file
Binary file