scout_apm 2.6.0 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0d5d02cc644b23e02868a9705fba1a15f0250bba5bff1c92eb97efaf04f1d87
4
- data.tar.gz: e0f5acef9895941f7d2f327596d1f9eef9f6a3ef374ddced1ee658b2882bc525
3
+ metadata.gz: 302bcce1cb8a4e8e1a000857fed8764438486e8d9a1c7df47c4cd9af475d6468
4
+ data.tar.gz: ff41334b370a15f45262c5fc6e215400c100f13beb41cd4bbafcbe2800e1ad9f
5
5
  SHA512:
6
- metadata.gz: 976b31dc166e501368825e07b3377138be135790a23a92fc52681f81d780034641d86496ac0d03dda0639127673d6b1908f5191f966a9193488094a24a7720a7
7
- data.tar.gz: 835d1e4f4db51c71a41521f5892e91d1d67c7cd96effc91f684bacbe6fa9e9303e9abdb9b697c75d79d656bab45d9b241b3200ac8203236fb75a07c101f3c5fb
6
+ metadata.gz: 50e3e4e094927f4add2b1839a9f4b5d8ded24785fc52fce7e37d5931c12e685b0be0234c3c2c5714cc366dcf4dc3dbe0c8f3d8d949e871a552011c558d65e6a5
7
+ data.tar.gz: 83f98b354e219d40b449df8e7ae121bf0f084e8ac7d7ac7c0d71c32c295791779a6c8720551e92e73f7a4592dfad9d10d709c47e619dc797c6557a8ac7523679
@@ -1,3 +1,8 @@
1
+ # 2.6.1
2
+
3
+ * Logging total autoinstrumented spans and the ratio of significant to total spans (#283).
4
+ * Added `autoinstruments_ignore` option (also #283).
5
+
1
6
  # 2.6.0
2
7
 
3
8
  * Autoinstruments (#247). Disabled by default. Set `auto_instruments: true` to enable.
@@ -103,6 +103,13 @@ module ScoutApm
103
103
  @slow_job_policy ||= ScoutApm::SlowJobPolicy.new(self)
104
104
  end
105
105
 
106
+ # Maintains a Histogram of insignificant/significant autoinstrument layers.
107
+ # significant = 1
108
+ # insignificant = 0
109
+ def auto_instruments_layer_histograms
110
+ @auto_instruments_layer_histograms ||= ScoutApm::RequestHistograms.new
111
+ end
112
+
106
113
  # Histogram of the cumulative requests since the start of the process
107
114
  def request_histograms
108
115
  @request_histograms ||= ScoutApm::RequestHistograms.new
@@ -5,15 +5,17 @@ module ScoutApm
5
5
  module AutoInstrument
6
6
  module InstructionSequence
7
7
  def load_iseq(path)
8
- if Rails.controller_path?(path)
8
+ if Rails.controller_path?(path) & !Rails.ignore?(path)
9
9
  begin
10
10
  new_code = Rails.rewrite(path)
11
11
  return self.compile(new_code, File.basename(path), path)
12
12
  rescue
13
13
  warn "Failed to apply auto-instrumentation to #{path}: #{$!}"
14
14
  end
15
+ elsif Rails.ignore?(path)
16
+ warn "AutoInstruments are ignored for path=#{path}."
15
17
  end
16
-
18
+
17
19
  return self.compile_file(path)
18
20
  end
19
21
  end
@@ -1,11 +1,12 @@
1
1
 
2
2
  module ScoutApm
3
- def self.AutoInstrument(name, backtrace = nil)
3
+ def self.AutoInstrument(name, backtrace, file_name)
4
4
  request = ScoutApm::RequestManager.lookup
5
5
 
6
6
  begin
7
7
  layer = ScoutApm::Layer.new('AutoInstrument', name)
8
8
  layer.backtrace = backtrace
9
+ layer.file_name = file_name
9
10
 
10
11
  request.start_layer(layer)
11
12
  started_layer = true
@@ -17,6 +17,19 @@ module ScoutApm
17
17
  CONTROLLER_FILE.match(path) && !GEM_FILE.match(path)
18
18
  end
19
19
 
20
+ # Autoinstruments increases overhead when applied to many code expressions that perform little work.
21
+ # You can exclude files from autoinstruments via the `auto_instruments_ignore` option.
22
+ def self.ignore?(path)
23
+ res = false
24
+ ScoutApm::Agent.instance.context.config.value('auto_instruments_ignore').each do |ignored_file_name|
25
+ if path.include?(ignored_file_name)
26
+ res = true
27
+ break
28
+ end
29
+ end
30
+ res
31
+ end
32
+
20
33
  def self.rewrite(path, code = nil)
21
34
  code ||= File.read(path)
22
35
 
@@ -60,7 +73,7 @@ module ScoutApm
60
73
  bt = ["#{file_name}:#{line}:in `#{method_name}'"]
61
74
 
62
75
  return [
63
- "::ScoutApm::AutoInstrument("+ source.dump + ",#{bt}" + "){",
76
+ "::ScoutApm::AutoInstrument("+ source.dump + ",#{bt}" + ",'#{file_name}'" + "){",
64
77
  "}"
65
78
  ]
66
79
  end
@@ -34,6 +34,7 @@ require 'scout_apm/environment'
34
34
  # start_resque_server_instrument - Used in special situations with certain Resque installs
35
35
  # timeline_traces - true/false to enable sending of of the timeline trace format.
36
36
  # auto_instruments - true/false whether to install autoinstruments. Only installed if on a supported Ruby version.
37
+ # auto_instruments_ignore - An array of file names to exclude from autoinstruments (Ex: ['application_controller']).
37
38
  #
38
39
  # Any of these config settings can be set with an environment variable prefixed
39
40
  # by SCOUT_ and uppercasing the key: SCOUT_LOG_LEVEL for instance.
@@ -77,7 +78,8 @@ module ScoutApm
77
78
  'uri_reporting',
78
79
  'instrument_http_url_length',
79
80
  'timeline_traces',
80
- 'auto_instruments'
81
+ 'auto_instruments',
82
+ 'auto_instruments_ignore'
81
83
  ]
82
84
 
83
85
  ################################################################################
@@ -171,7 +173,8 @@ module ScoutApm
171
173
  'instrument_http_url_length' => IntegerCoercion.new,
172
174
  'start_resque_server_instrument' => BooleanCoercion.new,
173
175
  'timeline_traces' => BooleanCoercion.new,
174
- 'auto_instruments' => BooleanCoercion.new
176
+ 'auto_instruments' => BooleanCoercion.new,
177
+ 'auto_instruments_ignore' => JsonCoercion.new,
175
178
  }
176
179
 
177
180
 
@@ -280,7 +283,8 @@ module ScoutApm
280
283
  'start_resque_server_instrument' => true, # still only starts if Resque is detected
281
284
  'collect_remote_ip' => true,
282
285
  'timeline_traces' => true,
283
- 'auto_instruments' => false
286
+ 'auto_instruments' => false,
287
+ 'auto_instruments_ignore' => []
284
288
  }.freeze
285
289
 
286
290
  def value(key)
@@ -94,7 +94,8 @@ module ScoutApm
94
94
 
95
95
  def preconditions_met?
96
96
  if dev_trace_disabled?
97
- logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
97
+ # The line below is very noise as it is called on every request.
98
+ # logger.debug("DevTrace: isn't activated via config. Try: SCOUT_DEV_TRACE=true rails server")
98
99
  return false
99
100
  end
100
101
 
@@ -38,6 +38,9 @@ module ScoutApm
38
38
  # backtrace of where it occurred.
39
39
  attr_accessor :backtrace
40
40
 
41
+ # The file name associated with the layer. Only used for autoinstruments overhead logging.
42
+ attr_accessor :file_name
43
+
41
44
  # As we go through a part of a request, instrumentation can store additional data
42
45
  # Known Keys:
43
46
  # :record_count - The number of rows returned by an AR query (From notification instantiation.active_record)
@@ -12,10 +12,24 @@ module ScoutApm
12
12
  ScoutApm::Debug.instance.call_periodic_hooks
13
13
  @reporting.process_metrics
14
14
  clean_old_percentiles
15
+ log_layer_histograms
15
16
  end
16
17
 
17
18
  private
18
19
 
20
+ def log_layer_histograms
21
+ # Ex key/value -
22
+ # "/Users/dlite/projects/scout/apm/app/controllers/application_controller.rb"=>[[0.0, 689], [1.0, 16]]
23
+ hists = context.auto_instruments_layer_histograms.as_json
24
+ hists_summary = hists.map { |k,v|
25
+ [
26
+ k,
27
+ {:total => total=v.map(&:last).inject(:+), :significant => (v.last.last/total.to_f).round(2)}
28
+ ]
29
+ }.to_h
30
+ context.logger.debug("AutoInstrument Significant Layer Histograms: #{hists_summary.pretty_inspect}")
31
+ end
32
+
19
33
  # XXX: Move logic into a RequestHistogramsByTime class that can keep the timeout logic in it
20
34
  def clean_old_percentiles
21
35
  context.
@@ -22,6 +22,10 @@ module ScoutApm
22
22
  @histograms.keys.each { |n| yield n }
23
23
  end
24
24
 
25
+ def as_json
26
+ @histograms.as_json
27
+ end
28
+
25
29
  def add(item, value)
26
30
  @histograms[item].add(value)
27
31
  end
@@ -180,14 +180,18 @@ module ScoutApm
180
180
  false
181
181
  end
182
182
 
183
+ # Returns +true+ if the total call time of AutoInstrument layers exceeds +AUTO_INSTRUMENT_TIMING_THRESHOLD+ and
184
+ # records a Histogram of insignificant / significant layers by file name.
183
185
  def layer_insignificant?(layer)
186
+ result = false # default is significant
184
187
  if layer.type == 'AutoInstrument'
185
188
  if layer.total_call_time < AUTO_INSTRUMENT_TIMING_THRESHOLD
186
- context.logger.debug("IGNORE LAYER name=#{layer.name} total_call_time=#{layer.total_call_time}")
187
- return true
189
+ result = true # not significant
188
190
  end
191
+ # 0 = not significant, 1 = significant
192
+ @agent_context.auto_instruments_layer_histograms.add(layer.file_name, (result ? 0 : 1))
189
193
  end
190
- return false
194
+ result
191
195
  end
192
196
 
193
197
  # Maintains a lookup Hash of call counts by layer name. Used to determine if we should capture a backtrace.
@@ -329,7 +333,7 @@ module ScoutApm
329
333
  memo
330
334
  end
331
335
  walker.walk
332
- converter_results = converter_instances.inject({}) do |memo, (slug,i)|
336
+ converter_results = converter_instances.inject({}) do |memo, (slug,i)|
333
337
  memo[slug] = i.record!
334
338
  memo
335
339
  end
@@ -1,3 +1,3 @@
1
1
  module ScoutApm
2
- VERSION = "2.6.0"
2
+ VERSION = "2.6.1"
3
3
  end
@@ -1,26 +1,26 @@
1
1
 
2
2
  class Assignments
3
3
  def nested_assignment
4
- @email ||= if (email = ::ScoutApm::AutoInstrument("session[\"email\"]",["BACKTRACE"]){session["email"]}).present?
5
- ::ScoutApm::AutoInstrument("User.where(email: email).first",["BACKTRACE"]){User.where(email: email).first}
4
+ @email ||= if (email = ::ScoutApm::AutoInstrument("session[\"email\"]",["BACKTRACE"],'FILE_NAME'){session["email"]}).present?
5
+ ::ScoutApm::AutoInstrument("User.where(email: email).first",["BACKTRACE"],'FILE_NAME'){User.where(email: email).first}
6
6
  else
7
7
  nil
8
8
  end
9
9
  end
10
10
 
11
11
  def paginate_collection(coll)
12
- page = (::ScoutApm::AutoInstrument("params[:page].present?",["BACKTRACE"]){params[:page].to_i} : 1)
13
- per_page = (::ScoutApm::AutoInstrument("params[:per_page].present?",["BACKTRACE"]){params[:per_page].to_i} : 20)
14
- pagination, self.collection = ::ScoutApm::AutoInstrument("pagy(...",["BACKTRACE"]){pagy(
12
+ page = (::ScoutApm::AutoInstrument("params[:page].present?",["BACKTRACE"],'FILE_NAME'){params[:page].to_i} : 1)
13
+ per_page = (::ScoutApm::AutoInstrument("params[:per_page].present?",["BACKTRACE"],'FILE_NAME'){params[:per_page].to_i} : 20)
14
+ pagination, self.collection = ::ScoutApm::AutoInstrument("pagy(...",["BACKTRACE"],'FILE_NAME'){pagy(
15
15
  coll,
16
16
  items: per_page,
17
17
  page: page
18
18
  )}
19
- ::ScoutApm::AutoInstrument("headers[PAGINATION_TOTAL_HEADER] = pagination.count.to_s",["BACKTRACE"]){headers[PAGINATION_TOTAL_HEADER] = pagination.count.to_s}
20
- ::ScoutApm::AutoInstrument("headers[PAGINATION_TOTAL_PAGES_HEADER] = pagination.pages.to_s",["BACKTRACE"]){headers[PAGINATION_TOTAL_PAGES_HEADER] = pagination.pages.to_s}
21
- ::ScoutApm::AutoInstrument("headers[PAGINATION_PER_PAGE_HEADER] = per_page.to_s",["BACKTRACE"]){headers[PAGINATION_PER_PAGE_HEADER] = per_page.to_s}
22
- ::ScoutApm::AutoInstrument("headers[PAGINATION_PAGE_HEADER] = pagination.page.to_s",["BACKTRACE"]){headers[PAGINATION_PAGE_HEADER] = pagination.page.to_s}
23
- ::ScoutApm::AutoInstrument("headers[PAGINATION_NEXT_PAGE_HEADER] = pagination.next.to_s",["BACKTRACE"]){headers[PAGINATION_NEXT_PAGE_HEADER] = pagination.next.to_s}
24
- ::ScoutApm::AutoInstrument("collection",["BACKTRACE"]){collection}
19
+ ::ScoutApm::AutoInstrument("headers[PAGINATION_TOTAL_HEADER] = pagination.count.to_s",["BACKTRACE"],'FILE_NAME'){headers[PAGINATION_TOTAL_HEADER] = pagination.count.to_s}
20
+ ::ScoutApm::AutoInstrument("headers[PAGINATION_TOTAL_PAGES_HEADER] = pagination.pages.to_s",["BACKTRACE"],'FILE_NAME'){headers[PAGINATION_TOTAL_PAGES_HEADER] = pagination.pages.to_s}
21
+ ::ScoutApm::AutoInstrument("headers[PAGINATION_PER_PAGE_HEADER] = per_page.to_s",["BACKTRACE"],'FILE_NAME'){headers[PAGINATION_PER_PAGE_HEADER] = per_page.to_s}
22
+ ::ScoutApm::AutoInstrument("headers[PAGINATION_PAGE_HEADER] = pagination.page.to_s",["BACKTRACE"],'FILE_NAME'){headers[PAGINATION_PAGE_HEADER] = pagination.page.to_s}
23
+ ::ScoutApm::AutoInstrument("headers[PAGINATION_NEXT_PAGE_HEADER] = pagination.next.to_s",["BACKTRACE"],'FILE_NAME'){headers[PAGINATION_NEXT_PAGE_HEADER] = pagination.next.to_s}
24
+ ::ScoutApm::AutoInstrument("collection",["BACKTRACE"],'FILE_NAME'){collection}
25
25
  end
26
26
  end
@@ -3,47 +3,47 @@ class ClientsController < ApplicationController
3
3
  before_action :check_authorization
4
4
 
5
5
  def index
6
- if ::ScoutApm::AutoInstrument("params[:status] == \"activated\"",["BACKTRACE"]){params[:status] == "activated"}
7
- @clients = ::ScoutApm::AutoInstrument("Client.activated",["BACKTRACE"]){Client.activated}
6
+ if ::ScoutApm::AutoInstrument("params[:status] == \"activated\"",["BACKTRACE"],'FILE_NAME'){params[:status] == "activated"}
7
+ @clients = ::ScoutApm::AutoInstrument("Client.activated",["BACKTRACE"],'FILE_NAME'){Client.activated}
8
8
  else
9
- @clients = ::ScoutApm::AutoInstrument("Client.inactivated",["BACKTRACE"]){Client.inactivated}
9
+ @clients = ::ScoutApm::AutoInstrument("Client.inactivated",["BACKTRACE"],'FILE_NAME'){Client.inactivated}
10
10
  end
11
11
  end
12
12
 
13
13
  def create
14
- @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["BACKTRACE"]){Client.new(params[:client])}
15
- if ::ScoutApm::AutoInstrument("@client.save",["BACKTRACE"]){@client.save}
16
- ::ScoutApm::AutoInstrument("redirect_to @client",["BACKTRACE"]){redirect_to @client}
14
+ @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["BACKTRACE"],'FILE_NAME'){Client.new(params[:client])}
15
+ if ::ScoutApm::AutoInstrument("@client.save",["BACKTRACE"],'FILE_NAME'){@client.save}
16
+ ::ScoutApm::AutoInstrument("redirect_to @client",["BACKTRACE"],'FILE_NAME'){redirect_to @client}
17
17
  else
18
18
  # This line overrides the default rendering behavior, which
19
19
  # would have been to render the "create" view.
20
- ::ScoutApm::AutoInstrument("render \"new\"",["BACKTRACE"]){render "new"}
20
+ ::ScoutApm::AutoInstrument("render \"new\"",["BACKTRACE"],'FILE_NAME'){render "new"}
21
21
  end
22
22
  end
23
23
 
24
24
  def edit
25
- @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["BACKTRACE"]){Client.new(params[:client])}
25
+ @client = ::ScoutApm::AutoInstrument("Client.new(params[:client])",["BACKTRACE"],'FILE_NAME'){Client.new(params[:client])}
26
26
 
27
- if ::ScoutApm::AutoInstrument("request.post?",["BACKTRACE"]){request.post?}
28
- ::ScoutApm::AutoInstrument("@client.transaction do...",["BACKTRACE"]){@client.transaction do
27
+ if ::ScoutApm::AutoInstrument("request.post?",["BACKTRACE"],'FILE_NAME'){request.post?}
28
+ ::ScoutApm::AutoInstrument("@client.transaction do...",["BACKTRACE"],'FILE_NAME'){@client.transaction do
29
29
  @client.update_attributes(params[:client])
30
30
  end}
31
31
  end
32
32
  end
33
33
 
34
34
  def data
35
- @clients = ::ScoutApm::AutoInstrument("Client.all",["BACKTRACE"]){Client.all}
35
+ @clients = ::ScoutApm::AutoInstrument("Client.all",["BACKTRACE"],'FILE_NAME'){Client.all}
36
36
 
37
- formatter = ::ScoutApm::AutoInstrument("proc do |row|...",["BACKTRACE"]){proc do |row|
37
+ formatter = ::ScoutApm::AutoInstrument("proc do |row|...",["BACKTRACE"],'FILE_NAME'){proc do |row|
38
38
  row.to_json
39
39
  end}
40
40
 
41
- ::ScoutApm::AutoInstrument("respond_with @clients.each(&formatter).join(\"\\n\"), :content_type => 'application/json; boundary=NL'",["BACKTRACE"]){respond_with @clients.each(&formatter).join("\n"), :content_type => 'application/json; boundary=NL'}
41
+ ::ScoutApm::AutoInstrument("respond_with @clients.each(&formatter).join(\"\\n\"), :content_type => 'FILE_NAME'}
42
42
  end
43
43
 
44
44
  def things
45
45
  x = {}
46
46
  x[:this] ||= 'foo'
47
- x[:that] &&= ::ScoutApm::AutoInstrument("'foo'.size",["BACKTRACE"]){'foo'.size}
47
+ x[:that] &&= ::ScoutApm::AutoInstrument("'FILE_NAME'.size}
48
48
  end
49
49
  end
@@ -19,35 +19,33 @@ class AutoInstrumentTest < Minitest::Test
19
19
  # test controller.rb file, which will be different on different environments.
20
20
  # This normalizes backtraces across environments.
21
21
  def normalize_backtrace(string)
22
- string.gsub(/\[".+auto_instrument\/.+?:.+?"\]/,'["BACKTRACE"]')
22
+ string
23
+ .gsub(/\[".+auto_instrument\/.+?:.+?"\]/,'["BACKTRACE"]')
24
+ .gsub(/'.+auto_instrument\/.+'/,"'FILE_NAME'")
23
25
  end
24
26
 
25
27
  # Use this to automatically update the test fixtures.
26
28
  def update_instrumented_source(name)
27
- File.write(
28
- instrumented_path(name),
29
- normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path(name)))
30
- )
29
+ source = ::ScoutApm::AutoInstrument::Rails.rewrite(source_path(name))
30
+ source = normalize_backtrace(source)
31
+ File.write(instrumented_path(name),source)
31
32
  end
32
33
 
33
34
  def test_controller_rewrite
35
+ # update_instrumented_source("controller")
34
36
  assert_equal instrumented_source("controller"),
35
37
  normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("controller")))
36
-
37
- # update_instrumented_source("controller")
38
38
  end
39
39
 
40
40
  def test_rescue_from_rewrite
41
+ # update_instrumented_source("rescue_from")
41
42
  assert_equal instrumented_source("rescue_from"),
42
43
  normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("rescue_from")))
43
-
44
- # update_instrumented_source("rescue_from")
45
44
  end
46
45
 
47
46
  def test_assignments_rewrite
47
+ # update_instrumented_source("assignments")
48
48
  assert_equal instrumented_source("assignments"),
49
49
  normalize_backtrace(::ScoutApm::AutoInstrument::Rails.rewrite(source_path("assignments")))
50
-
51
- # update_instrumented_source("assignments")
52
50
  end
53
51
  end if defined? ScoutApm::AutoInstrument
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout_apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Derek Haynes
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-09-09 00:00:00.000000000 Z
12
+ date: 2019-09-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest