scout_apm 2.6.0 → 2.6.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: 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