scout_apm 5.3.3 → 5.6.4
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 +4 -4
- data/.github/workflows/release.yml +30 -0
- data/.github/workflows/test.yml +18 -16
- data/CHANGELOG.markdown +51 -1
- data/README.markdown +5 -1
- data/gems/instruments.gemfile +2 -0
- data/gems/rails7.gemfile +4 -0
- data/gems/sidekiq7.gemfile +3 -0
- data/gems/sidekiq8.gemfile +4 -0
- data/gems/sqlite3-v2.gemfile +3 -0
- data/lib/scout_apm/agent.rb +1 -1
- data/lib/scout_apm/agent_context.rb +4 -0
- data/lib/scout_apm/app_server_load.rb +1 -1
- data/lib/scout_apm/auto_instrument/layer.rb +1 -0
- data/lib/scout_apm/auto_instrument/rails.rb +21 -0
- data/lib/scout_apm/background_job_integrations/good_job.rb +49 -0
- data/lib/scout_apm/background_job_integrations/resque.rb +13 -27
- data/lib/scout_apm/background_job_integrations/sidekiq.rb +22 -2
- data/lib/scout_apm/background_job_integrations/solid_queue.rb +47 -0
- data/lib/scout_apm/config.rb +69 -34
- data/lib/scout_apm/environment.rb +2 -0
- data/lib/scout_apm/error_service/sidekiq.rb +1 -1
- data/lib/scout_apm/git_revision.rb +7 -1
- data/lib/scout_apm/instruments/action_view.rb +62 -3
- data/lib/scout_apm/instruments/active_record.rb +1 -1
- data/lib/scout_apm/instruments/grape.rb +1 -1
- data/lib/scout_apm/instruments/mongoid.rb +7 -35
- data/lib/scout_apm/instruments/net_http.rb +1 -1
- data/lib/scout_apm/instruments/resque.rb +31 -2
- data/lib/scout_apm/layer_converters/external_service_converter.rb +1 -1
- data/lib/scout_apm/layer_converters/request_queue_time_converter.rb +7 -0
- data/lib/scout_apm/sampling.rb +104 -0
- data/lib/scout_apm/slow_request_policy.rb +1 -1
- data/lib/scout_apm/tracked_request.rb +7 -1
- data/lib/scout_apm/utils/installed_gems.rb +2 -1
- data/lib/scout_apm/version.rb +1 -1
- data/lib/scout_apm.rb +3 -0
- data/scout_apm.gemspec +2 -2
- data/test/test_helper.rb +61 -0
- data/test/unit/auto_instrument/hash_shorthand_controller-instrumented.rb +41 -0
- data/test/unit/auto_instrument/hash_shorthand_controller.rb +41 -0
- data/test/unit/auto_instrument_test.rb +7 -1
- data/test/unit/background_job_integrations/sidekiq_test.rb +13 -3
- data/test/unit/config_test.rb +20 -0
- data/test/unit/environment_test.rb +0 -28
- data/test/unit/git_revision_test.rb +65 -3
- data/test/unit/instruments/action_view_test.rb +102 -0
- data/test/unit/instruments/active_record_test.rb +30 -0
- data/test/unit/instruments/fixtures/test/_test_partial.html.erb +3 -0
- data/test/unit/instruments/fixtures/test/_test_partial_collection.html.erb +3 -0
- data/test/unit/instruments/fixtures/test_view.html.erb +10 -0
- data/test/unit/sampling_test.rb +215 -0
- metadata +22 -10
@@ -31,6 +31,8 @@ module ScoutApm
|
|
31
31
|
ScoutApm::BackgroundJobIntegrations::DelayedJob.new,
|
32
32
|
ScoutApm::BackgroundJobIntegrations::Que.new,
|
33
33
|
ScoutApm::BackgroundJobIntegrations::Faktory.new,
|
34
|
+
ScoutApm::BackgroundJobIntegrations::GoodJob.new,
|
35
|
+
ScoutApm::BackgroundJobIntegrations::SolidQueue.new,
|
34
36
|
]
|
35
37
|
|
36
38
|
FRAMEWORK_INTEGRATIONS = [
|
@@ -28,7 +28,7 @@ module ScoutApm
|
|
28
28
|
|
29
29
|
def install_sidekiq_with_error_handler
|
30
30
|
::Sidekiq.configure_server do |config|
|
31
|
-
config.error_handlers << proc { |exception, job_info|
|
31
|
+
config.error_handlers << proc { |exception, job_info, sidekiq_config|
|
32
32
|
context = ScoutApm::Agent.instance.context
|
33
33
|
|
34
34
|
# Bail out early, and reraise if the error is not interesting.
|
@@ -20,6 +20,7 @@ module ScoutApm
|
|
20
20
|
detect_from_config ||
|
21
21
|
detect_from_heroku ||
|
22
22
|
detect_from_capistrano ||
|
23
|
+
detect_from_kamal ||
|
23
24
|
detect_from_mina ||
|
24
25
|
detect_from_git
|
25
26
|
end
|
@@ -44,8 +45,13 @@ module ScoutApm
|
|
44
45
|
nil
|
45
46
|
end
|
46
47
|
|
48
|
+
# https://github.com/basecamp/kamal
|
49
|
+
def detect_from_kamal
|
50
|
+
ENV['KAMAL_VERSION']
|
51
|
+
end
|
52
|
+
|
47
53
|
# https://github.com/mina-deploy/mina
|
48
|
-
def detect_from_mina
|
54
|
+
def detect_from_mina
|
49
55
|
File.read(File.join(app_root, '.mina_git_revision')).strip
|
50
56
|
rescue
|
51
57
|
logger.debug "Unable to detect Git Revision from Mina: #{$!.message}"
|
@@ -72,9 +72,67 @@ module ScoutApm
|
|
72
72
|
|
73
73
|
logger.info "Instrumenting ActionView::TemplateRenderer"
|
74
74
|
::ActionView::TemplateRenderer.prepend(ActionViewTemplateRendererInstruments)
|
75
|
+
|
76
|
+
if defined?(::ActionView::CollectionRenderer)
|
77
|
+
logger.info "Instrumenting ActionView::CollectionRenderer"
|
78
|
+
::ActionView::CollectionRenderer.prepend(ActionViewCollectionRendererInstruments)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# In Rails 6.1 collection was moved to CollectionRenderer.
|
83
|
+
module ActionViewCollectionRendererInstruments
|
84
|
+
def render_collection(*args, **kwargs)
|
85
|
+
req = ScoutApm::RequestManager.lookup
|
86
|
+
|
87
|
+
maybe_template = args[3]
|
88
|
+
|
89
|
+
template_name ||= maybe_template.virtual_path rescue nil
|
90
|
+
template_name ||= "Unknown Collection"
|
91
|
+
layer_name = template_name + "/Rendering"
|
92
|
+
|
93
|
+
layer = ScoutApm::Layer.new("View", layer_name)
|
94
|
+
layer.subscopable!
|
95
|
+
|
96
|
+
begin
|
97
|
+
req.start_layer(layer)
|
98
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
99
|
+
super(*args, **kwargs)
|
100
|
+
else
|
101
|
+
super(*args)
|
102
|
+
end
|
103
|
+
ensure
|
104
|
+
req.stop_layer
|
105
|
+
end
|
106
|
+
end
|
75
107
|
end
|
76
108
|
|
77
109
|
module ActionViewPartialRendererInstruments
|
110
|
+
# In Rails 6.1, render_partial was renamed to render_partial_template
|
111
|
+
def render_partial_template(*args, **kwargs)
|
112
|
+
req = ScoutApm::RequestManager.lookup
|
113
|
+
|
114
|
+
# Template was moved to the third argument in Rails 6.1.
|
115
|
+
maybe_template = args[2]
|
116
|
+
|
117
|
+
template_name ||= maybe_template.virtual_path rescue nil
|
118
|
+
template_name ||= "Unknown Partial"
|
119
|
+
|
120
|
+
layer_name = template_name + "/Rendering"
|
121
|
+
layer = ScoutApm::Layer.new("View", layer_name)
|
122
|
+
layer.subscopable!
|
123
|
+
|
124
|
+
begin
|
125
|
+
req.start_layer(layer)
|
126
|
+
if ScoutApm::Agent.instance.context.environment.supports_kwarg_delegation?
|
127
|
+
super(*args, **kwargs)
|
128
|
+
else
|
129
|
+
super(*args)
|
130
|
+
end
|
131
|
+
ensure
|
132
|
+
req.stop_layer
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
78
136
|
# In Rails 6, the signature changed to pass the view & template args directly, as opposed to through the instance var
|
79
137
|
# New signature is: def render_partial(view, template)
|
80
138
|
def render_partial(*args, **kwargs)
|
@@ -83,7 +141,7 @@ module ScoutApm
|
|
83
141
|
maybe_template = args[1]
|
84
142
|
|
85
143
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
86
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.
|
144
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.6
|
87
145
|
template_name ||= "Unknown Partial"
|
88
146
|
|
89
147
|
layer_name = template_name + "/Rendering"
|
@@ -102,13 +160,14 @@ module ScoutApm
|
|
102
160
|
end
|
103
161
|
end
|
104
162
|
|
163
|
+
# This method was moved in Rails 6.1 to CollectionRender.
|
105
164
|
def collection_with_template(*args, **kwargs)
|
106
165
|
req = ScoutApm::RequestManager.lookup
|
107
166
|
|
108
167
|
maybe_template = args[1]
|
109
168
|
|
110
169
|
template_name = @template.virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
111
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.
|
170
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 6.0.6
|
112
171
|
template_name ||= "Unknown Collection"
|
113
172
|
layer_name = template_name + "/Rendering"
|
114
173
|
|
@@ -137,7 +196,7 @@ module ScoutApm
|
|
137
196
|
maybe_template = args[1]
|
138
197
|
|
139
198
|
template_name = args[0].virtual_path rescue nil # Works on Rails 3.2 -> end of Rails 5 series
|
140
|
-
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 ->
|
199
|
+
template_name ||= maybe_template.virtual_path rescue nil # Works on Rails 6 -> 7.1.3
|
141
200
|
template_name ||= "Unknown"
|
142
201
|
layer_name = template_name + "/Rendering"
|
143
202
|
|
@@ -53,7 +53,7 @@ module ScoutApm
|
|
53
53
|
self.options[:path].first,
|
54
54
|
].compact.map{ |n| n.to_s }.join("/")
|
55
55
|
rescue => e
|
56
|
-
logger.info("Error getting Grape Endpoint Name. Error: #{e.message}. Options: #{self.options.inspect}")
|
56
|
+
ScoutApm::Agent.instance.context.logger.info("Error getting Grape Endpoint Name. Error: #{e.message}. Options: #{self.options.inspect}")
|
57
57
|
name = "Grape/Unknown"
|
58
58
|
end
|
59
59
|
|
@@ -20,25 +20,12 @@ module ScoutApm
|
|
20
20
|
@installed = true
|
21
21
|
|
22
22
|
# Mongoid versions that use Moped should instrument Moped.
|
23
|
+
### See moped instrument for Moped driven deploys
|
23
24
|
if defined?(::Mongoid) and !defined?(::Moped)
|
24
|
-
logger.info "Instrumenting Mongoid 2.x"
|
25
25
|
@installed = true
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
::Mongoid::Collection.class_eval do
|
30
|
-
include ScoutApm::Tracer
|
31
|
-
(::Mongoid::Collections::Operations::ALL - [:<<, :[]]).each do |method|
|
32
|
-
instrument_method method, :type => "MongoDB", :name => '#{@klass}/' + method.to_s
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
### See moped instrument for Moped driven deploys
|
38
|
-
|
39
|
-
### 5.x Mongoid
|
40
|
-
if (mongoid_v5? || mongoid_v6? || mongoid_v7?) && defined?(::Mongoid::Contextual::Mongo)
|
41
|
-
logger.info "Instrumenting Mongoid 5.x/6.x/7.x"
|
27
|
+
if (mongoid_at_least_5?) && defined?(::Mongoid::Contextual::Mongo)
|
28
|
+
logger.info "Instrumenting Mongoid"
|
42
29
|
# All the public methods from Mongoid::Contextual::Mongo.
|
43
30
|
# TODO: Geo and MapReduce support (?). They are in other Contextual::* classes
|
44
31
|
methods = [
|
@@ -88,31 +75,17 @@ module ScoutApm
|
|
88
75
|
]
|
89
76
|
|
90
77
|
::Mongoid::Contextual::Mongo.class_eval(with_scout_instruments)
|
78
|
+
else
|
79
|
+
logger.warn "Expected method #{method} not defined in Mongoid::Contextual::Mongo."
|
91
80
|
end
|
92
81
|
end
|
93
82
|
end
|
94
83
|
end
|
95
84
|
end
|
96
85
|
|
97
|
-
def
|
98
|
-
if defined?(::Mongoid::VERSION)
|
99
|
-
::Mongoid::VERSION =~ /\A5/
|
100
|
-
else
|
101
|
-
false
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def mongoid_v6?
|
86
|
+
def mongoid_at_least_5?
|
106
87
|
if defined?(::Mongoid::VERSION)
|
107
|
-
::Mongoid::VERSION =~ /\
|
108
|
-
else
|
109
|
-
false
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def mongoid_v7?
|
114
|
-
if defined?(::Mongoid::VERSION)
|
115
|
-
::Mongoid::VERSION =~ /\A7/
|
88
|
+
::Mongoid::VERSION =~ /\A[56789]/
|
116
89
|
else
|
117
90
|
false
|
118
91
|
end
|
@@ -134,4 +107,3 @@ module ScoutApm
|
|
134
107
|
end
|
135
108
|
end
|
136
109
|
end
|
137
|
-
|
@@ -60,7 +60,7 @@ module ScoutApm
|
|
60
60
|
|
61
61
|
module NetHttpInstrumentationPrepend
|
62
62
|
def request(request, *args, &block)
|
63
|
-
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(
|
63
|
+
self.class.instrument("HTTP", "request", :ignore_children => true, :desc => request_scout_description(request)) do
|
64
64
|
super(request, *args, &block)
|
65
65
|
end
|
66
66
|
end
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module ScoutApm
|
2
2
|
module Instruments
|
3
3
|
module Resque
|
4
|
+
def before_perform_become_client(*args)
|
5
|
+
# Don't become remote client if explicitly disabled or if forking is disabled to force synchronous recording.
|
6
|
+
if config.value('start_resque_server_instrument') && forking?
|
7
|
+
ScoutApm::Agent.instance.context.become_remote_client!(bind, port)
|
8
|
+
else
|
9
|
+
logger.debug("Not becoming remote client due to 'start_resque_server_instrument' setting or 'fork_per_job' setting")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
4
13
|
def around_perform_with_scout_instruments(*args)
|
5
14
|
job_name = self.to_s
|
6
15
|
queue = find_queue
|
@@ -17,7 +26,6 @@ module ScoutApm
|
|
17
26
|
started_queue = true
|
18
27
|
req.start_layer(ScoutApm::Layer.new('Job', job_name))
|
19
28
|
started_job = true
|
20
|
-
|
21
29
|
yield
|
22
30
|
rescue => e
|
23
31
|
req.error!
|
@@ -33,7 +41,28 @@ module ScoutApm
|
|
33
41
|
return queue if self.respond_to?(:queue)
|
34
42
|
return "unknown"
|
35
43
|
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def bind
|
48
|
+
config.value("remote_agent_host")
|
49
|
+
end
|
50
|
+
|
51
|
+
def port
|
52
|
+
config.value("remote_agent_port")
|
53
|
+
end
|
54
|
+
|
55
|
+
def config
|
56
|
+
@config ||= ScoutApm::Agent.instance.context.config
|
57
|
+
end
|
58
|
+
|
59
|
+
def logger
|
60
|
+
@logger ||= ScoutApm::Agent.instance.context.logger
|
61
|
+
end
|
62
|
+
|
63
|
+
def forking?
|
64
|
+
@forking ||= ENV["FORK_PER_JOB"] != "false"
|
65
|
+
end
|
36
66
|
end
|
37
67
|
end
|
38
68
|
end
|
39
|
-
|
@@ -15,6 +15,9 @@ module ScoutApm
|
|
15
15
|
|
16
16
|
return unless headers
|
17
17
|
|
18
|
+
# When an application uses Turbo Streams, we capture very innaccurate queue times.
|
19
|
+
return if request_over_websocket?
|
20
|
+
|
18
21
|
raw_start = locate_timestamp
|
19
22
|
return unless raw_start
|
20
23
|
|
@@ -38,6 +41,10 @@ module ScoutApm
|
|
38
41
|
|
39
42
|
private
|
40
43
|
|
44
|
+
def request_over_websocket?
|
45
|
+
headers["Upgrade"] == "websocket"
|
46
|
+
end
|
47
|
+
|
41
48
|
# Looks through the possible headers with this data, and extracts the raw
|
42
49
|
# value of the header
|
43
50
|
# Returns nil if not found
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module ScoutApm
|
2
|
+
class Sampling
|
3
|
+
attr_reader :global_sample_rate, :sample_endpoints, :sample_uri_regex, :sample_jobs, :ignore_uri_regex, :ignore_jobs
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@global_sample_rate = config.value('sample_rate')
|
7
|
+
# web endpoints matched prefix by regex
|
8
|
+
# jobs matched explicitly by name
|
9
|
+
|
10
|
+
# for now still support old config key ('ignore') for backwards compatibility
|
11
|
+
@ignore_endpoints = config.value('ignore').present? ? config.value('ignore') : config.value('ignore_endpoints')
|
12
|
+
@sample_endpoints = individual_sample_to_hash(config.value('sample_endpoints'))
|
13
|
+
@endpoint_sample_rate = config.value('endpoint_sample_rate')
|
14
|
+
|
15
|
+
@ignore_jobs = config.value('ignore_jobs')
|
16
|
+
@sample_jobs = individual_sample_to_hash(config.value('sample_jobs'))
|
17
|
+
@job_sample_rate = config.value('job_sample_rate')
|
18
|
+
|
19
|
+
log_string = "Sampling initialized with config: "
|
20
|
+
log_string += "global_sample_rate: #{@global_sample_rate.inspect}, "
|
21
|
+
log_string += "endpoint_sample_rate: #{@endpoint_sample_rate.inspect}, "
|
22
|
+
log_string += "sample_endpoints: #{@sample_endpoints.inspect}, "
|
23
|
+
log_string += "ignore_endpoints: #{@ignore_endpoints.inspect}, "
|
24
|
+
log_string += "job_sample_rate: #{@job_sample_rate.inspect}, "
|
25
|
+
log_string += "sample_jobs: #{@sample_jobs.inspect}, "
|
26
|
+
log_string += "ignore_jobs: #{@ignore_jobs.inspect}"
|
27
|
+
logger.info(log_string)
|
28
|
+
end
|
29
|
+
|
30
|
+
def drop_request?(transaction)
|
31
|
+
# Individual endpoint/job sampling takes precedence over ignoring.
|
32
|
+
# Individual endpoint/job sample rate always takes precedence over general endpoint/job rate.
|
33
|
+
# General endpoint/job rate always takes precedence over global sample rate
|
34
|
+
if transaction.job?
|
35
|
+
job_name = transaction.layer_finder.job.name
|
36
|
+
rate = job_sample_rate(job_name)
|
37
|
+
return sample?(rate) unless rate.nil?
|
38
|
+
return true if ignore_job?(job_name)
|
39
|
+
return sample?(@job_sample_rate) unless @job_sample_rate.nil?
|
40
|
+
elsif transaction.web?
|
41
|
+
uri = transaction.annotations[:uri]
|
42
|
+
rate = web_sample_rate(uri)
|
43
|
+
return sample?(rate) unless rate.nil?
|
44
|
+
return true if ignore_uri?(uri)
|
45
|
+
return sample?(@endpoint_sample_rate) unless @endpoint_sample_rate.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
# global sample check
|
49
|
+
if @global_sample_rate
|
50
|
+
return sample?(@global_sample_rate)
|
51
|
+
end
|
52
|
+
|
53
|
+
false # don't drop the request
|
54
|
+
end
|
55
|
+
|
56
|
+
def individual_sample_to_hash(sampling_config)
|
57
|
+
return nil if sampling_config.blank?
|
58
|
+
# config looks like ['/foo:50','/bar:100']. parse it into hash of string: integer
|
59
|
+
sample_hash = {}
|
60
|
+
sampling_config.each do |sample|
|
61
|
+
path, rate = sample.split(':')
|
62
|
+
sample_hash[path] = rate.to_i
|
63
|
+
end
|
64
|
+
sample_hash
|
65
|
+
end
|
66
|
+
|
67
|
+
def ignore_uri?(uri)
|
68
|
+
return false if @ignore_endpoints.blank?
|
69
|
+
@ignore_endpoints.each do |prefix|
|
70
|
+
return true if uri.start_with?(prefix)
|
71
|
+
end
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
def web_sample_rate(uri)
|
76
|
+
return nil if @sample_endpoints.blank?
|
77
|
+
@sample_endpoints.each do |prefix, rate|
|
78
|
+
return rate if uri.start_with?(prefix)
|
79
|
+
end
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def ignore_job?(job_name)
|
84
|
+
return false if @ignore_jobs.blank?
|
85
|
+
@ignore_jobs.include?(job_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
def job_sample_rate(job_name)
|
89
|
+
return nil if @sample_jobs.blank?
|
90
|
+
@sample_jobs.fetch(job_name, nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
def sample?(rate)
|
94
|
+
rand * 100 > rate
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def logger
|
100
|
+
ScoutApm::Agent.instance.logger
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -14,7 +14,7 @@ module ScoutApm
|
|
14
14
|
|
15
15
|
def add_default_policies
|
16
16
|
add(SlowPolicy::SpeedPolicy.new(context))
|
17
|
-
add(SlowPolicy::
|
17
|
+
add(SlowPolicy::PercentPolicy.new(context))
|
18
18
|
add(SlowPolicy::AgePolicy.new(context))
|
19
19
|
add(SlowPolicy::PercentilePolicy.new(context))
|
20
20
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# A TrackedRequest is a stack of layers, where completed layers (go into, then
|
2
4
|
# come out of a layer) are forgotten as they finish. Layers are attached to
|
3
5
|
# their children as the process goes, building a tree structure within the
|
@@ -303,7 +305,11 @@ module ScoutApm
|
|
303
305
|
restore_from_dump! if @agent_context.nil?
|
304
306
|
|
305
307
|
# Bail out early if the user asked us to ignore this uri
|
306
|
-
return if @agent_context.ignored_uris.ignore?(annotations[:uri])
|
308
|
+
# return if @agent_context.ignored_uris.ignore?(annotations[:uri])
|
309
|
+
if @agent_context.sampling.drop_request?(self)
|
310
|
+
logger.debug("Dropping request due to sampling")
|
311
|
+
return
|
312
|
+
end
|
307
313
|
|
308
314
|
apply_name_override
|
309
315
|
|
@@ -12,7 +12,8 @@ module ScoutApm
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def run
|
15
|
-
Bundler.rubygems.
|
15
|
+
specs = Bundler.rubygems.public_send(Bundler.rubygems.respond_to?(:installed_specs) ? :installed_specs : :all_specs)
|
16
|
+
specs.map { |spec| [spec.name, spec.version.to_s] }
|
16
17
|
rescue => e
|
17
18
|
logger.warn("Couldn't fetch Gem information: #{e.message}")
|
18
19
|
[]
|
data/lib/scout_apm/version.rb
CHANGED
data/lib/scout_apm.rb
CHANGED
@@ -67,6 +67,8 @@ require 'scout_apm/background_job_integrations/shoryuken'
|
|
67
67
|
require 'scout_apm/background_job_integrations/sneakers'
|
68
68
|
require 'scout_apm/background_job_integrations/que'
|
69
69
|
require 'scout_apm/background_job_integrations/legacy_sneakers'
|
70
|
+
require 'scout_apm/background_job_integrations/good_job'
|
71
|
+
require 'scout_apm/background_job_integrations/solid_queue'
|
70
72
|
|
71
73
|
require 'scout_apm/framework_integrations/rails_2'
|
72
74
|
require 'scout_apm/framework_integrations/rails_3_or_4'
|
@@ -109,6 +111,7 @@ require 'scout_apm/instruments/samplers'
|
|
109
111
|
require 'scout_apm/app_server_load'
|
110
112
|
|
111
113
|
require 'scout_apm/ignored_uris.rb'
|
114
|
+
require 'scout_apm/sampling.rb'
|
112
115
|
require 'scout_apm/utils/active_record_metric_name'
|
113
116
|
require 'scout_apm/utils/backtrace_parser'
|
114
117
|
require 'scout_apm/utils/installed_gems'
|
data/scout_apm.gemspec
CHANGED
@@ -31,10 +31,10 @@ Gem::Specification.new do |s|
|
|
31
31
|
s.add_runtime_dependency "parser"
|
32
32
|
|
33
33
|
# These are general development dependencies which are used in instrumentation
|
34
|
-
# tests. Specific versions are pulled in using specific gemfiles, e.g.
|
34
|
+
# tests. Specific versions are pulled in using specific gemfiles, e.g.
|
35
35
|
# `gems/rails3.gemfile`.
|
36
36
|
s.add_development_dependency "activerecord"
|
37
|
-
s.add_development_dependency "sqlite3"
|
37
|
+
s.add_development_dependency "sqlite3", "~> 1.4"
|
38
38
|
|
39
39
|
s.add_development_dependency "rubocop"
|
40
40
|
s.add_development_dependency "guard"
|
data/test/test_helper.rb
CHANGED
@@ -38,6 +38,10 @@ class FakeConfigOverlay
|
|
38
38
|
@values[key]
|
39
39
|
end
|
40
40
|
|
41
|
+
def values
|
42
|
+
@values
|
43
|
+
end
|
44
|
+
|
41
45
|
def has_key?(key)
|
42
46
|
@values.has_key?(key)
|
43
47
|
end
|
@@ -65,6 +69,38 @@ class FakeEnvironment
|
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
72
|
+
def remove_rails_namespace
|
73
|
+
Object.send(:remove_const, "Rails") if defined?(Rails)
|
74
|
+
end
|
75
|
+
|
76
|
+
def fake_rails(version)
|
77
|
+
remove_rails_namespace if (ENV["SCOUT_TEST_FEATURES"] || "").include?("instruments")
|
78
|
+
|
79
|
+
Kernel.const_set("Rails", Module.new)
|
80
|
+
Kernel.const_set("ActionController", Module.new)
|
81
|
+
r = Kernel.const_get("Rails")
|
82
|
+
r.const_set("VERSION", Module.new)
|
83
|
+
v = r.const_get("VERSION")
|
84
|
+
v.const_set("MAJOR", version)
|
85
|
+
|
86
|
+
assert_equal version, Rails::VERSION::MAJOR
|
87
|
+
end
|
88
|
+
|
89
|
+
def clean_fake_rails
|
90
|
+
Kernel.send(:remove_const, "Rails") if defined?(Kernel::Rails)
|
91
|
+
Kernel.send(:remove_const, "ActionController") if defined?(Kernel::ActionController)
|
92
|
+
end
|
93
|
+
|
94
|
+
def fake_sinatra
|
95
|
+
Kernel.const_set("Sinatra", Module.new)
|
96
|
+
s = Kernel.const_get("Sinatra")
|
97
|
+
s.const_set("Base", Module.new)
|
98
|
+
end
|
99
|
+
|
100
|
+
def clean_fake_sinatra
|
101
|
+
Kernel.const_unset("Sinatra") if defined?(Kernel::Sinatra)
|
102
|
+
end
|
103
|
+
|
68
104
|
# Helpers available to all tests
|
69
105
|
class Minitest::Test
|
70
106
|
def setup
|
@@ -137,3 +173,28 @@ end
|
|
137
173
|
class Minitest::Test
|
138
174
|
include CustomAsserts
|
139
175
|
end
|
176
|
+
|
177
|
+
class FakeTrackedRequest
|
178
|
+
def self.new_web_request(uri)
|
179
|
+
context = ScoutApm::Agent.instance.context
|
180
|
+
fake_store = ScoutApm::FakeStore.new
|
181
|
+
req = ScoutApm::TrackedRequest.new(context, fake_store)
|
182
|
+
|
183
|
+
first_layer = ScoutApm::Layer.new("Controller", "index")
|
184
|
+
req.start_layer(first_layer)
|
185
|
+
req.annotate_request(:uri => uri)
|
186
|
+
|
187
|
+
req
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.new_job_request(job_name)
|
191
|
+
context = ScoutApm::Agent.instance.context
|
192
|
+
fake_store = ScoutApm::FakeStore.new
|
193
|
+
req = ScoutApm::TrackedRequest.new(context, fake_store)
|
194
|
+
|
195
|
+
first_layer = ScoutApm::Layer.new("Job", job_name)
|
196
|
+
req.start_layer(first_layer)
|
197
|
+
|
198
|
+
req
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
class HashShorthandController < ApplicationController
|
3
|
+
def hash
|
4
|
+
json = {
|
5
|
+
static: "static",
|
6
|
+
shorthand: ::ScoutApm::AutoInstrument("shorthand:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:6:in `hash'"]){shorthand},
|
7
|
+
longhand: ::ScoutApm::AutoInstrument("longhand: longhand",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:7:in `hash'"]){longhand},
|
8
|
+
longhand_different_key: ::ScoutApm::AutoInstrument("longhand",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:8:in `hash'"]){longhand},
|
9
|
+
hash_rocket: ::ScoutApm::AutoInstrument(":hash_rocket => hash_rocket",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:9:in `hash'"]){hash_rocket},
|
10
|
+
:hash_rocket_different_key => ::ScoutApm::AutoInstrument("hash_rocket",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:10:in `hash'"]){hash_rocket},
|
11
|
+
non_nil_receiver: ::ScoutApm::AutoInstrument("non_nil_receiver.value",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:11:in `hash'"]){non_nil_receiver.value},
|
12
|
+
nested: {
|
13
|
+
shorthand: ::ScoutApm::AutoInstrument("shorthand:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:13:in `hash'"]){shorthand},
|
14
|
+
},
|
15
|
+
nested_call: ::ScoutApm::AutoInstrument("nested_call(params[\"timestamp\"])",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:15:in `hash'"]){nested_call(params["timestamp"])}
|
16
|
+
}
|
17
|
+
::ScoutApm::AutoInstrument("render json:",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:17:in `hash'"]){render json:}
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def nested_call(noop)
|
23
|
+
noop
|
24
|
+
end
|
25
|
+
|
26
|
+
def shorthand
|
27
|
+
"shorthand"
|
28
|
+
end
|
29
|
+
|
30
|
+
def longhand
|
31
|
+
"longhand"
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash_rocket
|
35
|
+
"hash_rocket"
|
36
|
+
end
|
37
|
+
|
38
|
+
def non_nil_receiver
|
39
|
+
::ScoutApm::AutoInstrument("OpenStruct.new(value: \"value\")",["ROOT/test/unit/auto_instrument/hash_shorthand_controller.rb:39:in `non_nil_receiver'"]){OpenStruct.new(value: "value")}
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
|
2
|
+
class HashShorthandController < ApplicationController
|
3
|
+
def hash
|
4
|
+
json = {
|
5
|
+
static: "static",
|
6
|
+
shorthand:,
|
7
|
+
longhand: longhand,
|
8
|
+
longhand_different_key: longhand,
|
9
|
+
:hash_rocket => hash_rocket,
|
10
|
+
:hash_rocket_different_key => hash_rocket,
|
11
|
+
non_nil_receiver: non_nil_receiver.value,
|
12
|
+
nested: {
|
13
|
+
shorthand:,
|
14
|
+
},
|
15
|
+
nested_call: nested_call(params["timestamp"])
|
16
|
+
}
|
17
|
+
render json:
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def nested_call(noop)
|
23
|
+
noop
|
24
|
+
end
|
25
|
+
|
26
|
+
def shorthand
|
27
|
+
"shorthand"
|
28
|
+
end
|
29
|
+
|
30
|
+
def longhand
|
31
|
+
"longhand"
|
32
|
+
end
|
33
|
+
|
34
|
+
def hash_rocket
|
35
|
+
"hash_rocket"
|
36
|
+
end
|
37
|
+
|
38
|
+
def non_nil_receiver
|
39
|
+
OpenStruct.new(value: "value")
|
40
|
+
end
|
41
|
+
end
|