rails_performance 1.2.3 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -13
  3. data/Rakefile +14 -14
  4. data/app/assets/images/download.svg +3 -0
  5. data/app/controllers/rails_performance/base_controller.rb +21 -21
  6. data/app/controllers/rails_performance/concerns/csv_exportable.rb +29 -0
  7. data/app/controllers/rails_performance/rails_performance_controller.rb +96 -49
  8. data/app/helpers/rails_performance/rails_performance_helper.rb +152 -156
  9. data/app/views/rails_performance/javascripts/app.js +2 -2
  10. data/app/views/rails_performance/rails_performance/_export.html.erb +3 -0
  11. data/app/views/rails_performance/rails_performance/crashes.html.erb +8 -1
  12. data/app/views/rails_performance/rails_performance/index.html.erb +29 -0
  13. data/app/views/rails_performance/rails_performance/recent.html.erb +8 -6
  14. data/app/views/rails_performance/rails_performance/requests.html.erb +15 -1
  15. data/app/views/rails_performance/rails_performance/slow.html.erb +3 -0
  16. data/app/views/rails_performance/shared/_header.html.erb +0 -1
  17. data/app/views/rails_performance/stylesheets/style.css +10 -0
  18. data/config/routes.rb +16 -18
  19. data/lib/generators/rails_performance/install/install_generator.rb +1 -1
  20. data/lib/generators/rails_performance/install/templates/initializer.rb +56 -42
  21. data/lib/rails_performance/data_source.rb +120 -121
  22. data/lib/rails_performance/engine.rb +8 -8
  23. data/lib/rails_performance/extensions/trace.rb +32 -33
  24. data/lib/rails_performance/gems/custom_ext.rb +31 -34
  25. data/lib/rails_performance/gems/delayed_job_ext.rb +50 -54
  26. data/lib/rails_performance/gems/grape_ext.rb +33 -35
  27. data/lib/rails_performance/gems/rake_ext.rb +41 -44
  28. data/lib/rails_performance/gems/sidekiq_ext.rb +34 -37
  29. data/lib/rails_performance/instrument/metrics_collector.rb +50 -50
  30. data/lib/rails_performance/models/base_record.rb +33 -36
  31. data/lib/rails_performance/models/collection.rb +35 -36
  32. data/lib/rails_performance/models/custom_record.rb +47 -48
  33. data/lib/rails_performance/models/delayed_job_record.rb +61 -62
  34. data/lib/rails_performance/models/grape_record.rb +60 -61
  35. data/lib/rails_performance/models/rake_record.rb +48 -49
  36. data/lib/rails_performance/models/request_record.rb +128 -120
  37. data/lib/rails_performance/models/sidekiq_record.rb +65 -66
  38. data/lib/rails_performance/models/trace_record.rb +18 -19
  39. data/lib/rails_performance/rails/middleware.rb +75 -76
  40. data/lib/rails_performance/rails/query_builder.rb +18 -20
  41. data/lib/rails_performance/reports/base_report.rb +60 -60
  42. data/lib/rails_performance/reports/breakdown_report.rb +15 -18
  43. data/lib/rails_performance/reports/crash_report.rb +15 -17
  44. data/lib/rails_performance/reports/percentile_report.rb +14 -0
  45. data/lib/rails_performance/reports/recent_requests_report.rb +24 -24
  46. data/lib/rails_performance/reports/requests_report.rb +30 -27
  47. data/lib/rails_performance/reports/response_time_report.rb +17 -17
  48. data/lib/rails_performance/reports/slow_requests_report.rb +4 -4
  49. data/lib/rails_performance/reports/throughput_report.rb +15 -17
  50. data/lib/rails_performance/reports/trace_report.rb +16 -18
  51. data/lib/rails_performance/thread/current_request.rb +33 -34
  52. data/lib/rails_performance/utils.rb +64 -54
  53. data/lib/rails_performance/version.rb +2 -2
  54. data/lib/rails_performance.rb +35 -36
  55. metadata +21 -17
@@ -1,62 +1,61 @@
1
- module RailsPerformance
2
- module Models
3
- class DelayedJobRecord < BaseRecord
4
- attr_accessor :jid, :duration, :datetime, :datetimei, :source_type, :class_name, :method_name, :status, :json
5
-
6
- # delayed_job
7
- #|jid|22
8
- #|datetime|20210415T0616
9
- #|datetimei|1618492591
10
- #|source_type|instance_method
11
- #|class_name|User
12
- #|method_name|say_hello_without_delay
13
- #|status|success|END|1.0.0
14
- def DelayedJobRecord.from_db(key, value)
15
- items = key.split("|")
16
-
17
- DelayedJobRecord.new(
18
- jid: items[2],
19
- datetime: items[4],
20
- datetimei: items[6],
21
- source_type: items[8],
22
- class_name: items[10],
23
- method_name: items[12],
24
- status: items[14],
25
- json: value
26
- )
27
- end
28
-
29
- def initialize(jid:, duration: nil, datetime:, datetimei:, source_type:, class_name:, method_name:, status:, json: '{}')
30
- @jid = jid
31
- @duration = duration
32
- @datetime = datetime
33
- @datetimei = datetimei.to_i
34
- @source_type = source_type
35
- @class_name = class_name
36
- @method_name = method_name
37
- @status = status
38
- @json = json
39
- end
40
-
41
- def record_hash
42
- {
43
- jid: jid,
44
- datetime: Time.at(datetimei),
45
- datetimei: datetimei,
46
- duration: value['duration'],
47
- status: status,
48
- source_type: source_type,
49
- class_name: class_name,
50
- method_name: method_name,
51
- }
52
- end
53
-
54
- def save
55
- key = "delayed_job|jid|#{jid}|datetime|#{datetime}|datetimei|#{datetimei}|source_type|#{source_type}|class_name|#{class_name}|method_name|#{method_name}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
56
- value = { duration: duration }
57
- Utils.save_to_redis(key, value)
58
- end
59
-
60
- end
61
- end
62
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class DelayedJobRecord < BaseRecord
4
+ attr_accessor :jid, :duration, :datetime, :datetimei, :source_type, :class_name, :method_name, :status, :json
5
+
6
+ # delayed_job
7
+ # |jid|22
8
+ # |datetime|20210415T0616
9
+ # |datetimei|1618492591
10
+ # |source_type|instance_method
11
+ # |class_name|User
12
+ # |method_name|say_hello_without_delay
13
+ # |status|success|END|1.0.0
14
+ def self.from_db(key, value)
15
+ items = key.split("|")
16
+
17
+ DelayedJobRecord.new(
18
+ jid: items[2],
19
+ datetime: items[4],
20
+ datetimei: items[6],
21
+ source_type: items[8],
22
+ class_name: items[10],
23
+ method_name: items[12],
24
+ status: items[14],
25
+ json: value
26
+ )
27
+ end
28
+
29
+ def initialize(jid:, datetime:, datetimei:, source_type:, class_name:, method_name:, status:, duration: nil, json: "{}")
30
+ @jid = jid
31
+ @duration = duration
32
+ @datetime = datetime
33
+ @datetimei = datetimei.to_i
34
+ @source_type = source_type
35
+ @class_name = class_name
36
+ @method_name = method_name
37
+ @status = status
38
+ @json = json
39
+ end
40
+
41
+ def record_hash
42
+ {
43
+ jid: jid,
44
+ datetime: Time.at(datetimei),
45
+ datetimei: datetimei,
46
+ duration: value["duration"],
47
+ status: status,
48
+ source_type: source_type,
49
+ class_name: class_name,
50
+ method_name: method_name
51
+ }
52
+ end
53
+
54
+ def save
55
+ key = "delayed_job|jid|#{jid}|datetime|#{datetime}|datetimei|#{datetimei}|source_type|#{source_type}|class_name|#{class_name}|method_name|#{method_name}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
56
+ value = {duration: duration}
57
+ Utils.save_to_redis(key, value)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -1,61 +1,60 @@
1
- module RailsPerformance
2
- module Models
3
- class GrapeRecord < BaseRecord
4
- attr_accessor :datetime, :datetimei, :format, :status, :path, :method, :request_id, :json
5
- attr_accessor :endpoint_render_grape, :endpoint_run_grape, :format_response_grape
6
-
7
- # key = grape|datetime|20210409T1115|datetimei|1617992134|format|json|path|/api/users|status|200|method|GET|request_id|1122|END|1.0.0
8
- # value = {"endpoint_render.grape"=>0.000643989, "endpoint_run.grape"=>0.002000907, "format_response.grape"=>0.0348967}
9
- def GrapeRecord.from_db(key, value)
10
- items = key.split("|")
11
-
12
- GrapeRecord.new(
13
- datetime: items[2],
14
- datetimei: items[4],
15
- format: items[6],
16
- path: items[8],
17
- status: items[10],
18
- method: items[12],
19
- request_id: items[14],
20
- json: value
21
- )
22
- end
23
-
24
- def initialize(datetime: nil, datetimei: nil, format: nil, path: nil, status: nil, method: nil, request_id:, endpoint_render_grape: nil, endpoint_run_grape: nil, format_response_grape: nil, json: '{}')
25
- @datetime = datetime
26
- @datetimei = datetimei.to_i unless datetimei.nil?
27
- @format = format
28
- @path = path
29
- @status = status
30
- @method = method
31
- @request_id = request_id
32
-
33
- @endpoint_render_grape = endpoint_render_grape
34
- @endpoint_run_grape = endpoint_run_grape
35
- @format_response_grape = format_response_grape
36
-
37
- @json = json
38
- end
39
-
40
- def record_hash
41
- {
42
- format: self.format,
43
- status: self.status,
44
- method: self.method,
45
- path: self.path,
46
- datetime: Time.at(self.datetimei.to_i),
47
- datetimei: datetimei.to_i,
48
- request_id: self.request_id,
49
- }.merge(self.value)
50
- end
51
-
52
- def save
53
- key = "grape|datetime|#{datetime}|datetimei|#{datetimei}|format|#{format}|path|#{path}|status|#{status}|method|#{method}|request_id|#{request_id}|END|#{RailsPerformance::SCHEMA}"
54
- value = { "endpoint_render.grape" => endpoint_render_grape, "endpoint_run.grape" => endpoint_run_grape, "format_response.grape" => format_response_grape }
55
-
56
- Utils.save_to_redis(key, value)
57
- end
58
-
59
- end
60
- end
61
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class GrapeRecord < BaseRecord
4
+ attr_accessor :datetime, :datetimei, :format, :status, :path, :method, :request_id, :json
5
+ attr_accessor :endpoint_render_grape, :endpoint_run_grape, :format_response_grape
6
+
7
+ # key = grape|datetime|20210409T1115|datetimei|1617992134|format|json|path|/api/users|status|200|method|GET|request_id|1122|END|1.0.0
8
+ # value = {"endpoint_render.grape"=>0.000643989, "endpoint_run.grape"=>0.002000907, "format_response.grape"=>0.0348967}
9
+ def self.from_db(key, value)
10
+ items = key.split("|")
11
+
12
+ GrapeRecord.new(
13
+ datetime: items[2],
14
+ datetimei: items[4],
15
+ format: items[6],
16
+ path: items[8],
17
+ status: items[10],
18
+ method: items[12],
19
+ request_id: items[14],
20
+ json: value
21
+ )
22
+ end
23
+
24
+ def initialize(request_id:, datetime: nil, datetimei: nil, format: nil, path: nil, status: nil, method: nil, endpoint_render_grape: nil, endpoint_run_grape: nil, format_response_grape: nil, json: "{}")
25
+ @datetime = datetime
26
+ @datetimei = datetimei.to_i unless datetimei.nil?
27
+ @format = format
28
+ @path = path
29
+ @status = status
30
+ @method = method
31
+ @request_id = request_id
32
+
33
+ @endpoint_render_grape = endpoint_render_grape
34
+ @endpoint_run_grape = endpoint_run_grape
35
+ @format_response_grape = format_response_grape
36
+
37
+ @json = json
38
+ end
39
+
40
+ def record_hash
41
+ {
42
+ format: self.format,
43
+ status: status,
44
+ method: method,
45
+ path: path,
46
+ datetime: Time.at(datetimei.to_i),
47
+ datetimei: datetimei.to_i,
48
+ request_id: request_id
49
+ }.merge(value)
50
+ end
51
+
52
+ def save
53
+ key = "grape|datetime|#{datetime}|datetimei|#{datetimei}|format|#{format}|path|#{path}|status|#{status}|method|#{method}|request_id|#{request_id}|END|#{RailsPerformance::SCHEMA}"
54
+ value = {"endpoint_render.grape" => endpoint_render_grape, "endpoint_run.grape" => endpoint_run_grape, "format_response.grape" => format_response_grape}
55
+
56
+ Utils.save_to_redis(key, value)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,49 +1,48 @@
1
- module RailsPerformance
2
- module Models
3
- class RakeRecord < BaseRecord
4
- attr_accessor :task, :duration, :datetime, :datetimei, :status
5
-
6
- # rake|task|["task3"]|datetime|20210416T1254|datetimei|1618602843|status|error|END|1.0.0
7
- # {"duration":0.00012442}
8
- def RakeRecord.from_db(key, value)
9
- items = key.split("|")
10
-
11
- RakeRecord.new(
12
- task: JSON.parse(items[2]),
13
- datetime: items[4],
14
- datetimei: items[6],
15
- status: items[8],
16
- json: value
17
- )
18
- end
19
-
20
- def initialize(task:, duration: nil, datetime:, datetimei:, status:, json: '{}')
21
- @task = Array.wrap(task)
22
- @datetime = datetime
23
- @datetimei = datetimei.to_i
24
- @status = status
25
- @duration = duration
26
- @json = json
27
-
28
- @duration ||= value['duration']
29
- end
30
-
31
- def record_hash
32
- {
33
- task: task,
34
- datetime: Time.at(datetimei),
35
- datetimei: datetimei,
36
- duration: duration,
37
- status: status,
38
- }
39
- end
40
-
41
- def save
42
- key = "rake|task|#{task.to_json}|datetime|#{datetime}|datetimei|#{datetimei}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
43
- value = { duration: duration }
44
- Utils.save_to_redis(key, value)
45
- end
46
-
47
- end
48
- end
49
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class RakeRecord < BaseRecord
4
+ attr_accessor :task, :duration, :datetime, :datetimei, :status
5
+
6
+ # rake|task|["task3"]|datetime|20210416T1254|datetimei|1618602843|status|error|END|1.0.0
7
+ # {"duration":0.00012442}
8
+ def self.from_db(key, value)
9
+ items = key.split("|")
10
+
11
+ RakeRecord.new(
12
+ task: JSON.parse(items[2]),
13
+ datetime: items[4],
14
+ datetimei: items[6],
15
+ status: items[8],
16
+ json: value
17
+ )
18
+ end
19
+
20
+ def initialize(task:, datetime:, datetimei:, status:, duration: nil, json: "{}")
21
+ @task = Array.wrap(task)
22
+ @datetime = datetime
23
+ @datetimei = datetimei.to_i
24
+ @status = status
25
+ @duration = duration
26
+ @json = json
27
+
28
+ @duration ||= value["duration"]
29
+ end
30
+
31
+ def record_hash
32
+ {
33
+ task: task,
34
+ datetime: Time.at(datetimei),
35
+ datetimei: datetimei,
36
+ duration: duration,
37
+ status: status
38
+ }
39
+ end
40
+
41
+ def save
42
+ key = "rake|task|#{task.to_json}|datetime|#{datetime}|datetimei|#{datetimei}|status|#{status}|END|#{RailsPerformance::SCHEMA}"
43
+ value = {duration: duration}
44
+ Utils.save_to_redis(key, value)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,120 +1,128 @@
1
- module RailsPerformance
2
- module Models
3
- class RequestRecord < BaseRecord
4
- attr_accessor :controller, :action, :format, :status, :datetime, :datetimei, :method, :path, :request_id, :json
5
- attr_accessor :view_runtime, :db_runtime, :duration, :http_referer, :custom_data
6
- attr_accessor :exception, :exception_object
7
-
8
- def RequestRecord.find_by(request_id:)
9
- keys, values = RailsPerformance::Utils.fetch_from_redis("performance|*|request_id|#{request_id}|*")
10
-
11
- return nil if keys.blank?
12
- return nil if values.blank?
13
-
14
- RailsPerformance::Models::RequestRecord.from_db(keys[0], values[0])
15
- end
16
-
17
- # key = performance|
18
- # controller|HomeController|
19
- # action|index|
20
- # format|html|
21
- # status|200|
22
- # datetime|20200124T0523|
23
- # datetimei|1579861423|
24
- # method|GET|
25
- # path|/|
26
- # request_id|454545454545454545|
27
- # END|1.0.0
28
- # = {"view_runtime":null,"db_runtime":0,"duration":27.329741000000002,"http_referer":null,"custom_data":null,"exception":"ZeroDivisionError divided by 0","backtrace":["/root/projects/rails_performance/test/dummy/app/controllers/account/site_controller.rb:17:in `/'","/root/projects/rails_performance/test/dummy/app/controllers/account/site_controller.rb:17:in `crash'","/usr/local/rvm/gems/ruby-2.6.3/gems/actionpack-6.1.3.1/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'"]}
29
- # value = JSON
30
- def RequestRecord.from_db(key, value)
31
- items = key.split("|")
32
-
33
- RequestRecord.new(
34
- controller: items[2],
35
- action: items[4],
36
- format: items[6],
37
- status: items[8],
38
- datetime: items[10],
39
- datetimei: items[12],
40
- method: items[14],
41
- path: items[16],
42
- request_id: items[18],
43
- json: value
44
- )
45
- end
46
-
47
- def initialize(controller:, action:, format:, status:, datetime:, datetimei:, method:, path:, request_id:, view_runtime: nil, db_runtime: nil, duration: nil, http_referer: nil, custom_data: nil, exception: nil, exception_object: nil, json: '{}')
48
- @controller = controller
49
- @action = action
50
- @format = format
51
- @status = status
52
- @datetime = datetime
53
- @datetimei = datetimei.to_i
54
- @method = method
55
- @path = path
56
- @request_id = request_id
57
-
58
- @view_runtime = view_runtime
59
- @db_runtime = db_runtime
60
- @duration = duration
61
- @http_referer = http_referer
62
- @custom_data = custom_data
63
-
64
- @exception = Array.wrap(exception).compact.join(" ")
65
- @exception_object = exception_object
66
-
67
- @json = json
68
- end
69
-
70
- def controller_action
71
- "#{controller}##{action}"
72
- end
73
-
74
- def controller_action_format
75
- "#{controller}##{action}|#{format}"
76
- end
77
-
78
- # show on UI in the right panel
79
- def record_hash
80
- {
81
- controller: self.controller,
82
- action: self.action,
83
- format: self.format,
84
- status: self.status,
85
- method: self.method,
86
- path: self.path,
87
- request_id: self.request_id,
88
- datetime: Time.at(self.datetimei.to_i),
89
- datetimei: datetimei,
90
- duration: self.value['duration'],
91
- db_runtime: self.value['db_runtime'],
92
- view_runtime: self.value['view_runtime'],
93
- exception: self.value['exception'],
94
- backtrace: self.value['backtrace'],
95
- http_referer: self.value['http_referer']
96
- }.tap do |h|
97
- custom_data = JSON.parse(self.value['custom_data']) rescue nil
98
- if custom_data.is_a?(Hash)
99
- h.merge!(custom_data)
100
- end
101
- end
102
- end
103
-
104
- def save
105
- key = "performance|controller|#{controller}|action|#{action}|format|#{format}|status|#{status}|datetime|#{datetime}|datetimei|#{datetimei}|method|#{method}|path|#{path}|request_id|#{request_id}|END|#{RailsPerformance::SCHEMA}"
106
- value = {
107
- view_runtime: view_runtime,
108
- db_runtime: db_runtime,
109
- duration: duration,
110
- http_referer: http_referer,
111
- custom_data: custom_data.to_json
112
- }
113
- value[:exception] = exception if exception.present?
114
- value[:backtrace] = exception_object.backtrace.take(3) if exception_object
115
- Utils.save_to_redis(key, value)
116
- end
117
-
118
- end
119
- end
120
- end
1
+ module RailsPerformance
2
+ module Models
3
+ class RequestRecord < BaseRecord
4
+ attr_accessor :controller, :action, :format, :status, :datetime, :datetimei, :method, :path, :request_id, :json
5
+ attr_accessor :view_runtime, :db_runtime, :duration, :http_referer, :custom_data
6
+ attr_accessor :exception, :exception_object
7
+
8
+ def self.find_by(request_id:)
9
+ keys, values = RailsPerformance::Utils.fetch_from_redis("performance|*|request_id|#{request_id}|*")
10
+
11
+ return nil if keys.blank?
12
+ return nil if values.blank?
13
+
14
+ RailsPerformance::Models::RequestRecord.from_db(keys[0], values[0])
15
+ end
16
+
17
+ # key = performance|
18
+ # controller|HomeController|
19
+ # action|index|
20
+ # format|html|
21
+ # status|200|
22
+ # datetime|20200124T0523|
23
+ # datetimei|1579861423|
24
+ # method|GET|
25
+ # path|/|
26
+ # request_id|454545454545454545|
27
+ # END|1.0.0
28
+ # = {"view_runtime":null,"db_runtime":0,"duration":27.329741000000002,"http_referer":null,"custom_data":null,"exception":"ZeroDivisionError divided by 0","backtrace":["/root/projects/rails_performance/test/dummy/app/controllers/account/site_controller.rb:17:in `/'","/root/projects/rails_performance/test/dummy/app/controllers/account/site_controller.rb:17:in `crash'","/usr/local/rvm/gems/ruby-2.6.3/gems/actionpack-6.1.3.1/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'"]}
29
+ # value = JSON
30
+ def self.from_db(key, value)
31
+ items = key.split("|")
32
+
33
+ parsed_value = JSON.parse(value) rescue {}
34
+
35
+ RequestRecord.new(
36
+ controller: items[2],
37
+ action: items[4],
38
+ format: items[6],
39
+ status: items[8],
40
+ datetime: items[10],
41
+ datetimei: items[12],
42
+ method: items[14],
43
+ path: items[16],
44
+ request_id: items[18],
45
+ json: value,
46
+ duration: parsed_value["duration"],
47
+ view_runtime: parsed_value["view_runtime"],
48
+ db_runtime: parsed_value["db_runtime"],
49
+ )
50
+ end
51
+
52
+ def initialize(controller:, action:, format:, status:, datetime:, datetimei:, method:, path:, request_id:, view_runtime: nil, db_runtime: nil, duration: nil, http_referer: nil, custom_data: nil, exception: nil, exception_object: nil, json: "{}")
53
+ @controller = controller
54
+ @action = action
55
+ @format = format
56
+ @status = status
57
+ @datetime = datetime
58
+ @datetimei = datetimei.to_i
59
+ @method = method
60
+ @path = path
61
+ @request_id = request_id
62
+
63
+ @view_runtime = view_runtime
64
+ @db_runtime = db_runtime
65
+ @duration = duration
66
+ @http_referer = http_referer
67
+ @custom_data = custom_data
68
+
69
+ @exception = Array.wrap(exception).compact.join(" ")
70
+ @exception_object = exception_object
71
+
72
+ @json = json
73
+ end
74
+
75
+ def controller_action
76
+ "#{controller}##{action}"
77
+ end
78
+
79
+ def controller_action_format
80
+ "#{controller}##{action}|#{format}"
81
+ end
82
+
83
+ # show on UI in the right panel
84
+ def record_hash
85
+ {
86
+ controller: controller,
87
+ action: action,
88
+ format: self.format,
89
+ status: status,
90
+ method: method,
91
+ path: path,
92
+ request_id: request_id,
93
+ datetime: Time.at(datetimei.to_i),
94
+ datetimei: datetimei,
95
+ duration: value["duration"],
96
+ db_runtime: value["db_runtime"],
97
+ view_runtime: value["view_runtime"],
98
+ exception: value["exception"],
99
+ backtrace: value["backtrace"],
100
+ http_referer: value["http_referer"]
101
+ }.tap do |h|
102
+ custom_data = begin
103
+ JSON.parse(value["custom_data"])
104
+ rescue
105
+ nil
106
+ end
107
+ if custom_data.is_a?(Hash)
108
+ h.merge!(custom_data)
109
+ end
110
+ end
111
+ end
112
+
113
+ def save
114
+ key = "performance|controller|#{controller}|action|#{action}|format|#{format}|status|#{status}|datetime|#{datetime}|datetimei|#{datetimei}|method|#{method}|path|#{path}|request_id|#{request_id}|END|#{RailsPerformance::SCHEMA}"
115
+ value = {
116
+ view_runtime: view_runtime,
117
+ db_runtime: db_runtime,
118
+ duration: duration,
119
+ http_referer: http_referer,
120
+ custom_data: custom_data.to_json
121
+ }
122
+ value[:exception] = exception if exception.present?
123
+ value[:backtrace] = exception_object.backtrace.take(3) if exception_object
124
+ Utils.save_to_redis(key, value)
125
+ end
126
+ end
127
+ end
128
+ end