builder_apm 0.2.0
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.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +196 -0
- data/LICENSE.txt +21 -0
- data/README.md +50 -0
- data/Rakefile +28 -0
- data/app/controllers/builder_apm/dashboard_controller.rb +8 -0
- data/app/controllers/builder_apm/error_requests_controller.rb +8 -0
- data/app/controllers/builder_apm/n_plus_one_controller.rb +8 -0
- data/app/controllers/builder_apm/recent_requests_controller.rb +8 -0
- data/app/controllers/builder_apm/request_analysis_controller.rb +8 -0
- data/app/controllers/builder_apm/request_data_controller.rb +41 -0
- data/app/controllers/builder_apm/request_details_controller.rb +9 -0
- data/app/controllers/builder_apm/slow_requests_controller.rb +8 -0
- data/app/controllers/builder_apm/wip_controller.rb +8 -0
- data/app/views/builder_apm/css/_dark.html.erb +119 -0
- data/app/views/builder_apm/css/_main.html.erb +268 -0
- data/app/views/builder_apm/dashboard/index.html.erb +10 -0
- data/app/views/builder_apm/error_requests/index.html.erb +23 -0
- data/app/views/builder_apm/js/_compress.html.erb +93 -0
- data/app/views/builder_apm/js/_dashboard.html.erb +199 -0
- data/app/views/builder_apm/js/_data_fetcher.html.erb +254 -0
- data/app/views/builder_apm/js/_error_requests.html.erb +65 -0
- data/app/views/builder_apm/js/_lzma.html.erb +2670 -0
- data/app/views/builder_apm/js/_n_plus_one.html.erb +79 -0
- data/app/views/builder_apm/js/_recent_requests.html.erb +82 -0
- data/app/views/builder_apm/js/_request_analysis.html.erb +77 -0
- data/app/views/builder_apm/js/_request_details.html.erb +204 -0
- data/app/views/builder_apm/js/_slow_requests.html.erb +74 -0
- data/app/views/builder_apm/n_plus_one/index.html.erb +21 -0
- data/app/views/builder_apm/recent_requests/index.html.erb +21 -0
- data/app/views/builder_apm/request_analysis/index.html.erb +24 -0
- data/app/views/builder_apm/request_details/index.html.erb +7 -0
- data/app/views/builder_apm/shared/_footer.html.erb +3 -0
- data/app/views/builder_apm/shared/_header.html.erb +55 -0
- data/app/views/builder_apm/slow_requests/index.html.erb +21 -0
- data/app/views/builder_apm/wip/index.html.erb +5 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/builder_apm.gemspec +23 -0
- data/config/routes.rb +12 -0
- data/lib/builder_apm/configuration.rb +15 -0
- data/lib/builder_apm/controllers/instrumenter.rb +88 -0
- data/lib/builder_apm/engine.rb +17 -0
- data/lib/builder_apm/methods/instrumenter.rb +79 -0
- data/lib/builder_apm/middleware/timing.rb +56 -0
- data/lib/builder_apm/models/instrumenter.rb +82 -0
- data/lib/builder_apm/railtie.rb +9 -0
- data/lib/builder_apm/redis_client.rb +11 -0
- data/lib/builder_apm/version.rb +3 -0
- data/lib/builder_apm.rb +22 -0
- data/lib/generators/builder_apm/install_generator.rb +21 -0
- data/lib/generators/builder_apm/templates/builder_apm_config.rb +6 -0
- data/lib/generators/builder_apm/templates/create_builder_apm_requests.rb +21 -0
- data/lib/generators/builder_apm/templates/create_builder_apm_sql_queries.rb +17 -0
- metadata +135 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
<%= render 'builder_apm/shared/header' %>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<thead>
|
5
|
+
<tr>
|
6
|
+
<th class="sortable" data-field="start_time">Time</th>
|
7
|
+
<th class="sortable" data-field="controller">Controller#Action</th>
|
8
|
+
<th class="sortable" data-field="status">Status</th>
|
9
|
+
<th class="sortable" data-field="real_duration_time">Duration (ms)</th>
|
10
|
+
<th class="sortable" data-field="calc_db_runtime">DB Runtime (ms)</th>
|
11
|
+
<th class="sortable" data-field="view_runtime">View Runtime (ms)</th>
|
12
|
+
</tr>
|
13
|
+
</thead>
|
14
|
+
<tbody>
|
15
|
+
<!-- Table content will be populated here by JavaScript -->
|
16
|
+
</tbody>
|
17
|
+
</table>
|
18
|
+
|
19
|
+
<%= render 'builder_apm/js/slow_requests' %>
|
20
|
+
|
21
|
+
<%= render 'builder_apm/shared/footer' %>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<%= render 'builder_apm/shared/header' %>
|
2
|
+
<div class="image-container">
|
3
|
+
<img src="https://cdn.discordapp.com/attachments/1098245214516297798/1131953429615485020/dripster82_web_page_under_construction_funncy_image_cartoonish_49e72072-6e1e-46ec-a597-2a53bda5eac0.png" />
|
4
|
+
</div>
|
5
|
+
<%= render 'builder_apm/shared/footer' %>
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "builder_apm"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/builder_apm.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'lib/builder_apm/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "builder_apm"
|
5
|
+
spec.version = BuilderApm::VERSION
|
6
|
+
spec.authors = ["Paul Ketelle"]
|
7
|
+
spec.email = ["paul.ketelle@builder.ai"]
|
8
|
+
|
9
|
+
spec.summary = %q{Write a short summary, because RubyGems requires one.}
|
10
|
+
spec.description = %q{Write a longer description or delete this line.}
|
11
|
+
# spec.homepage = "http://www.google.com"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
spec.add_dependency 'rails', '>= 4.0', '< 8'
|
15
|
+
spec.add_dependency "redis", "~> 4.5"
|
16
|
+
|
17
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
BuilderApm::Engine.routes.draw do
|
2
|
+
root to: redirect('/builder_apm/dashboard')
|
3
|
+
get 'dashboard', to: 'dashboard#index'
|
4
|
+
get 'request_data', to: 'request_data#index', defaults: { format: 'json' }
|
5
|
+
get 'recent_requests', to: 'recent_requests#index'
|
6
|
+
get 'request_details', to: 'request_details#index'
|
7
|
+
get 'errors_500', to: 'error_requests#index'
|
8
|
+
get 'slow_requests', to: 'slow_requests#index'
|
9
|
+
get 'request_analysis', to: 'request_analysis#index'
|
10
|
+
get 'n_plus_one', to: 'n_plus_one#index'
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :redis_url
|
4
|
+
attr_accessor :enable_controller_profiler
|
5
|
+
attr_accessor :enable_active_record_profiler
|
6
|
+
attr_accessor :enable_methods_profiler
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@redis_url = 'redis://localhost:6379/0'
|
10
|
+
@enable_controller_profiler = true
|
11
|
+
@enable_active_record_profiler = true
|
12
|
+
@enable_methods_profiler = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
module Controllers
|
3
|
+
class Instrumenter
|
4
|
+
|
5
|
+
def start
|
6
|
+
ActiveSupport::Notifications.subscribe 'start_processing.action_controller' do |*args|
|
7
|
+
event = event_from_args(*args)
|
8
|
+
process_start(event)
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveSupport::Notifications.subscribe 'process_action.action_controller' do |*args|
|
12
|
+
event = event_from_args(*args)
|
13
|
+
process_action(event)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_start(event)
|
18
|
+
return if event.payload[:controller].nil? || event.payload[:controller].start_with?("BuilderApm::")
|
19
|
+
|
20
|
+
uuid = event.payload[:headers].env['action_dispatch.request_id']
|
21
|
+
Thread.current[:request_id] = uuid
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_action(event)
|
25
|
+
return if event.payload[:controller].start_with?("BuilderApm::")
|
26
|
+
|
27
|
+
request_id = Thread.current[:request_id]
|
28
|
+
Thread.current['request_data'] = extract_data_from_event(event)
|
29
|
+
ensure
|
30
|
+
clean_up_thread_values
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def event_from_args(*args)
|
36
|
+
data = args.extract_options!
|
37
|
+
ActiveSupport::Notifications::Event.new(*args, data)
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_data_from_event(event)
|
41
|
+
data = {
|
42
|
+
request_id: Thread.current[:request_id],
|
43
|
+
controller: event.payload[:controller],
|
44
|
+
action: event.payload[:action],
|
45
|
+
params: event.payload[:params].except(:controller, :action),
|
46
|
+
path: event.payload[:path],
|
47
|
+
format: event.payload[:format],
|
48
|
+
method: event.payload[:method],
|
49
|
+
controller_line: file_and_line_number(event.payload[:controller], event.payload[:action]),
|
50
|
+
status: event.payload[:status],
|
51
|
+
start_time: event.time,
|
52
|
+
end_time: event.end,
|
53
|
+
duration: event.duration,
|
54
|
+
db_runtime: event.payload[:db_runtime],
|
55
|
+
view_runtime: event.payload[:view_runtime],
|
56
|
+
stack: Thread.current[:stack]
|
57
|
+
}
|
58
|
+
if event.payload[:exception]
|
59
|
+
exception_class, exception_message = event.payload[:exception]
|
60
|
+
data[:exception_class] = exception_class
|
61
|
+
data[:exception_message] = exception_message
|
62
|
+
data[:exception_backtrace] = event.payload[:exception_object].backtrace #.select { |line| line.start_with?(Rails.root.to_s) }
|
63
|
+
end
|
64
|
+
|
65
|
+
data
|
66
|
+
end
|
67
|
+
|
68
|
+
def clean_up_thread_values
|
69
|
+
Thread.current[:sql_event_id] = nil
|
70
|
+
Thread.current[:request_id] = nil
|
71
|
+
Thread.current[:stack] = nil
|
72
|
+
end
|
73
|
+
|
74
|
+
def file_and_line_number(controller, action)
|
75
|
+
begin
|
76
|
+
controller_class = controller.constantize
|
77
|
+
method_info = controller_class.instance_method(action)
|
78
|
+
absolute_path = method_info.source_location.first
|
79
|
+
line_number = method_info.source_location.last
|
80
|
+
relative_path = Pathname.new(absolute_path).relative_path_from(Rails.root)
|
81
|
+
"#{relative_path}:#{line_number}"
|
82
|
+
rescue NameError
|
83
|
+
"???"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace BuilderApm
|
4
|
+
|
5
|
+
initializer 'builder_apm.start' do |app|
|
6
|
+
BuilderApm::Controllers::Instrumenter.new.start if BuilderApm.configuration.enable_controller_profiler
|
7
|
+
BuilderApm::Models::Instrumenter.start if BuilderApm.configuration.enable_active_record_profiler
|
8
|
+
BuilderApm::Methods::Instrumenter.new.start if BuilderApm.configuration.enable_methods_profiler
|
9
|
+
end
|
10
|
+
|
11
|
+
config.after_initialize do
|
12
|
+
Rails.application.routes.append do
|
13
|
+
mount BuilderApm::Engine => '/builder_apm'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
module Methods
|
3
|
+
class Instrumenter
|
4
|
+
def initialize(root_path: Rails.root.to_s)
|
5
|
+
@root_path = root_path
|
6
|
+
@call_times = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def start
|
10
|
+
@trace = setup_trace
|
11
|
+
@trace.enable
|
12
|
+
end
|
13
|
+
|
14
|
+
def stop
|
15
|
+
@trace.disable unless @trace.nil?
|
16
|
+
end
|
17
|
+
|
18
|
+
def setup_trace
|
19
|
+
me = self
|
20
|
+
TracePoint.new(:call, :return) do |tp|
|
21
|
+
me.process_trace_point(tp) if me.valid_trace_point?(tp)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_trace_point?(tp)
|
26
|
+
!Thread.current[:request_id].nil? && tp.path.start_with?(@root_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_trace_point(tp)
|
30
|
+
if tp.event == :call
|
31
|
+
process_call_event(tp)
|
32
|
+
elsif tp.event == :return
|
33
|
+
process_return_event(tp)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def process_call_event(tp)
|
40
|
+
method_id = "#{tp.defined_class}##{tp.method_id}"
|
41
|
+
@call_times[method_id] = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
42
|
+
caller_info = caller_locations(4,1).first
|
43
|
+
calling_file_path = caller_info.absolute_path
|
44
|
+
calling_line_number = caller_info.lineno
|
45
|
+
|
46
|
+
method_call = {
|
47
|
+
method: method_id,
|
48
|
+
method_line: "#{tp.path.gsub(@root_path, '')}:#{tp.lineno}",
|
49
|
+
triggering_line: "#{calling_file_path.gsub(@root_path, '')}:#{calling_line_number}",
|
50
|
+
children: [],
|
51
|
+
start_time: Time.now.to_f * 1000,
|
52
|
+
sql_events: []
|
53
|
+
}
|
54
|
+
|
55
|
+
(Thread.current[:stack] ||= []).push(method_call)
|
56
|
+
end
|
57
|
+
|
58
|
+
def process_return_event(tp)
|
59
|
+
method_id = "#{tp.defined_class}##{tp.method_id}"
|
60
|
+
|
61
|
+
if @call_times.key?(method_id)
|
62
|
+
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - @call_times[method_id]
|
63
|
+
elapsed_time_in_ms = (elapsed_time * 1000).round(3)
|
64
|
+
@call_times.delete(method_id)
|
65
|
+
|
66
|
+
method_call = (Thread.current[:stack] ||= []).pop
|
67
|
+
method_call[:end_time] = Time.now.to_f * 1000
|
68
|
+
method_call[:duration] = elapsed_time_in_ms
|
69
|
+
|
70
|
+
if Thread.current[:stack]&.any?
|
71
|
+
Thread.current[:stack].last[:children].push(method_call)
|
72
|
+
else
|
73
|
+
Thread.current[:stack].push(method_call)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
module Middleware
|
3
|
+
class Timing
|
4
|
+
def initialize(app, redis_client: BuilderApm::RedisClient.client)
|
5
|
+
@redis_client = redis_client
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
request_id = env["action_dispatch.request_id"]
|
11
|
+
Thread.current["request_id"] = request_id
|
12
|
+
start_time = Time.now.to_f * 1000
|
13
|
+
|
14
|
+
begin
|
15
|
+
result = @app.call(env)
|
16
|
+
rescue => e
|
17
|
+
handle_exception(e, start_time, request_id)
|
18
|
+
raise e
|
19
|
+
end
|
20
|
+
|
21
|
+
end_time = Time.now.to_f * 1000
|
22
|
+
handle_timing(start_time, end_time, request_id)
|
23
|
+
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def handle_timing(start_time, end_time, request_id)
|
30
|
+
duration = end_time - start_time;
|
31
|
+
data = Thread.current['request_data']
|
32
|
+
|
33
|
+
if data
|
34
|
+
data[:real_start_time] = start_time
|
35
|
+
data[:real_end_time] = end_time
|
36
|
+
data[:real_duration_time] = end_time - start_time
|
37
|
+
Thread.current['request_data'] = nil
|
38
|
+
|
39
|
+
@redis_client.zadd("builder_apm:timestamps", end_time, request_id)
|
40
|
+
@redis_client.set("builder_apm:Request:#{data[:request_id]}", data.to_json)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def handle_exception(e, start_time, request_id)
|
45
|
+
end_time = Time.now.to_f * 1000
|
46
|
+
|
47
|
+
data = Thread.current['request_data'] || {}
|
48
|
+
data[:exception_class] = e.class.to_s
|
49
|
+
data[:exception_message] = e.message
|
50
|
+
data[:exception_backtrace] = e.backtrace
|
51
|
+
|
52
|
+
handle_timing(start_time, end_time, request_id)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
module Models
|
3
|
+
class Instrumenter
|
4
|
+
def self.start
|
5
|
+
new.subscribe_to_notifications
|
6
|
+
end
|
7
|
+
|
8
|
+
def subscribe_to_notifications
|
9
|
+
ActiveSupport::Notifications.subscribe('sql.active_record') { |*args| handle_sql_active_record(*args) }
|
10
|
+
ActiveSupport::Notifications.subscribe('instantiation.active_record') { |*args| handle_instantiation_active_record(*args) }
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def handle_sql_active_record(*args)
|
16
|
+
data = args.extract_options!
|
17
|
+
event = ActiveSupport::Notifications::Event.new(*args, data)
|
18
|
+
name = event.payload[:name]
|
19
|
+
return if name == "SCHEMA" || Thread.current[:request_id].nil?
|
20
|
+
|
21
|
+
triggering_line = determine_triggering_line(caller)
|
22
|
+
|
23
|
+
sql_query_data = build_sql_query_data(event, triggering_line)
|
24
|
+
store_sql_query_data(sql_query_data)
|
25
|
+
end
|
26
|
+
|
27
|
+
def handle_instantiation_active_record(*args)
|
28
|
+
data = args.extract_options!
|
29
|
+
event = ActiveSupport::Notifications::Event.new(*args, data)
|
30
|
+
|
31
|
+
update_last_sql_query_data_with_instantiation_info(event)
|
32
|
+
end
|
33
|
+
|
34
|
+
def determine_triggering_line(call_stack)
|
35
|
+
app_stack = call_stack.select { |line| line.include?(Rails.root.to_s) }
|
36
|
+
app_stack.first.to_s.gsub(Rails.root.to_s, '')
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_sql_query_data(event, triggering_line)
|
40
|
+
{
|
41
|
+
request_id: Thread.current[:request_id],
|
42
|
+
sql_id: SecureRandom.uuid,
|
43
|
+
sql: event.payload[:sql],
|
44
|
+
params: event.payload[:binds].map { |a| a.value },
|
45
|
+
triggering_line: triggering_line,
|
46
|
+
name: event.payload[:name],
|
47
|
+
cached: event.payload[:cached] || false,
|
48
|
+
start_time: event.time,
|
49
|
+
end_time: event.end,
|
50
|
+
duration: event.duration,
|
51
|
+
record_count: 0,
|
52
|
+
class_name: ''
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def store_sql_query_data(sql_query_data)
|
57
|
+
Thread.current[:sql_event_id] = sql_query_data[:sql_id]
|
58
|
+
|
59
|
+
if Thread.current[:stack]&.any?
|
60
|
+
Thread.current[:stack].last[:sql_events].push(sql_query_data)
|
61
|
+
else
|
62
|
+
(Thread.current[:stack] ||= []).push({sql_events: [sql_query_data], children: []})
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def update_last_sql_query_data_with_instantiation_info(event)
|
67
|
+
stack = Thread.current[:stack]
|
68
|
+
request_id = Thread.current[:request_id]
|
69
|
+
|
70
|
+
return if stack.nil? || stack.empty? || request_id.nil?
|
71
|
+
|
72
|
+
last_sql = Thread.current[:stack].last[:sql_events].pop
|
73
|
+
if last_sql[:sql_id] == Thread.current[:sql_event_id]
|
74
|
+
last_sql[:record_count] = event.payload[:record_count]
|
75
|
+
last_sql[:class_name] = event.payload[:class_name]
|
76
|
+
end
|
77
|
+
Thread.current[:stack].last[:sql_events].push(last_sql)
|
78
|
+
Thread.current[:sql_event_id] = nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/builder_apm.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "builder_apm/version"
|
2
|
+
require 'builder_apm/controllers/instrumenter'
|
3
|
+
require 'builder_apm/models/instrumenter'
|
4
|
+
require 'builder_apm/methods/instrumenter'
|
5
|
+
require 'builder_apm/middleware/timing'
|
6
|
+
require 'builder_apm/configuration'
|
7
|
+
require 'builder_apm/engine'
|
8
|
+
require 'builder_apm/redis_client'
|
9
|
+
require 'builder_apm/railtie'
|
10
|
+
|
11
|
+
module BuilderApm
|
12
|
+
def self.configure
|
13
|
+
yield(configuration)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configuration
|
17
|
+
@configuration ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
class Error < StandardError; end
|
21
|
+
# Your code goes here...
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module BuilderApm
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
include Rails::Generators::Migration
|
5
|
+
source_root File.expand_path('templates', __dir__)
|
6
|
+
|
7
|
+
def self.next_migration_number(dirname)
|
8
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
9
|
+
end
|
10
|
+
|
11
|
+
def copy_migrations
|
12
|
+
migration_template "create_builder_apm_requests.rb", "db/migrate/create_builder_apm_requests.rb"
|
13
|
+
migration_template "create_builder_apm_sql_queries.rb", "db/migrate/create_builder_apm_sql_queries.rb"
|
14
|
+
end
|
15
|
+
|
16
|
+
def copy_initializer_file
|
17
|
+
copy_file 'builder_apm_config.rb', 'config/initializers/builder_apm.rb'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class CreateBuilderApmRequests < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :builder_apm_requests do |t|
|
4
|
+
t.string :request_url
|
5
|
+
t.string :request_method
|
6
|
+
t.string :controller
|
7
|
+
t.string :action
|
8
|
+
t.text :params
|
9
|
+
t.integer :status
|
10
|
+
t.string :format
|
11
|
+
t.string :file_and_line_number
|
12
|
+
t.float :duration
|
13
|
+
t.float :view_runtime
|
14
|
+
t.float :db_runtime
|
15
|
+
t.datetime :start_time
|
16
|
+
t.datetime :end_time
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateBuilderApmSqlQueries < ActiveRecord::Migration[6.0]
|
2
|
+
def change
|
3
|
+
create_table :builder_apm_sql_queries do |t|
|
4
|
+
t.string :event_uuid
|
5
|
+
t.text :sql
|
6
|
+
t.text :params
|
7
|
+
t.float :duration
|
8
|
+
t.integer :result_count
|
9
|
+
t.string :class_name
|
10
|
+
t.boolean :rails_cached
|
11
|
+
t.datetime :start_time
|
12
|
+
t.datetime :end_time
|
13
|
+
|
14
|
+
t.timestamps
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|