newrelic_rpm 3.7.3.204 → 3.8.0.218
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGELOG +73 -0
- data/README.md +1 -1
- data/Rakefile +1 -5
- data/lib/new_relic/agent.rb +1 -0
- data/lib/new_relic/agent/agent.rb +47 -18
- data/lib/new_relic/agent/agent_logger.rb +11 -1
- data/lib/new_relic/agent/configuration/default_source.rb +85 -1
- data/lib/new_relic/agent/configuration/manager.rb +5 -1
- data/lib/new_relic/agent/datastores/mongo.rb +8 -3
- data/lib/new_relic/agent/harvester.rb +5 -1
- data/lib/new_relic/agent/instrumentation/action_controller_subscriber.rb +1 -0
- data/lib/new_relic/agent/instrumentation/active_merchant.rb +7 -3
- data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +13 -3
- data/lib/new_relic/agent/instrumentation/rails/action_controller.rb +7 -1
- data/lib/new_relic/agent/instrumentation/sidekiq.rb +3 -1
- data/lib/new_relic/agent/instrumentation/sinatra.rb +3 -1
- data/lib/new_relic/agent/new_relic_service.rb +8 -0
- data/lib/new_relic/agent/request_sampler.rb +1 -1
- data/lib/new_relic/agent/sampler.rb +22 -2
- data/lib/new_relic/agent/sampler_collection.rb +13 -1
- data/lib/new_relic/agent/samplers/cpu_sampler.rb +3 -1
- data/lib/new_relic/agent/samplers/delayed_job_sampler.rb +2 -1
- data/lib/new_relic/agent/samplers/memory_sampler.rb +2 -1
- data/lib/new_relic/agent/samplers/object_sampler.rb +1 -3
- data/lib/new_relic/agent/samplers/vm_sampler.rb +126 -0
- data/lib/new_relic/agent/stats.rb +0 -15
- data/lib/new_relic/agent/stats_engine/gc_profiler.rb +66 -75
- data/lib/new_relic/agent/stats_engine/stats_hash.rb +1 -1
- data/lib/new_relic/agent/supported_versions.rb +2 -2
- data/lib/new_relic/agent/transaction.rb +6 -3
- data/lib/new_relic/agent/vm/monotonic_gc_profiler.rb +17 -5
- data/lib/new_relic/agent/vm/mri_vm.rb +2 -1
- data/lib/new_relic/agent/vm/snapshot.rb +5 -1
- data/lib/new_relic/control/instance_methods.rb +8 -5
- data/lib/new_relic/control/instrumentation.rb +0 -9
- data/lib/new_relic/environment_report.rb +1 -1
- data/lib/new_relic/language_support.rb +4 -0
- data/lib/new_relic/local_environment.rb +39 -14
- data/lib/new_relic/noticed_error.rb +7 -4
- data/lib/new_relic/rack/browser_monitoring.rb +16 -3
- data/lib/new_relic/version.rb +2 -2
- data/newrelic_rpm.gemspec +1 -1
- data/test/agent_helper.rb +5 -3
- data/test/environments/lib/environments/runner.rb +8 -7
- data/test/environments/norails/Gemfile +1 -1
- data/test/environments/rails21/Gemfile +1 -0
- data/test/environments/rails22/Gemfile +1 -0
- data/test/environments/rails23/Gemfile +1 -0
- data/test/environments/rails30/Gemfile +4 -1
- data/test/environments/rails31/Gemfile +4 -1
- data/test/environments/rails32/Gemfile +3 -4
- data/test/environments/rails40/Gemfile +1 -1
- data/test/environments/rails41/Gemfile +1 -1
- data/test/flaky_proxy/lib/flaky_proxy/proxy.rb +1 -0
- data/test/multiverse/lib/multiverse/output_collector.rb +3 -1
- data/test/multiverse/lib/multiverse/runner.rb +2 -10
- data/test/multiverse/lib/multiverse/suite.rb +100 -30
- data/test/multiverse/suites/activemerchant/Envfile +16 -0
- data/test/multiverse/suites/activemerchant/activemerchant_test.rb +65 -0
- data/test/multiverse/suites/agent_only/custom_queue_time_test.rb +57 -0
- data/test/multiverse/suites/config_file_loading/config_file_loading_test.rb +1 -1
- data/test/multiverse/suites/mongo/Envfile +9 -1
- data/test/multiverse/suites/rails/Envfile +2 -2
- data/test/multiverse/suites/rails/app.rb +3 -0
- data/test/multiverse/suites/rails/bad_instrumentation_test.rb +0 -2
- data/test/multiverse/suites/rails/error_tracing_test.rb +1 -2
- data/test/multiverse/suites/rails/gc_instrumentation_test.rb +17 -8
- data/test/multiverse/suites/rails/ignore_test.rb +0 -2
- data/test/multiverse/suites/rails/mongrel_queue_depth_test.rb +0 -2
- data/test/multiverse/suites/rails/queue_time_test.rb +40 -11
- data/test/multiverse/suites/rails/request_statistics_test.rb +0 -3
- data/test/multiverse/suites/rails/view_instrumentation_test.rb +0 -2
- data/test/multiverse/suites/sidekiq/Envfile +7 -2
- data/test/multiverse/suites/sinatra/Envfile +1 -1
- data/test/multiverse/suites/sinatra/nested_middleware_test.rb +41 -0
- data/test/multiverse/suites/sinatra/sinatra_metric_explosion_test.rb +1 -1
- data/test/new_relic/agent/agent/connect_test.rb +32 -4
- data/test/new_relic/agent/agent/start_test.rb +9 -1
- data/test/new_relic/agent/agent_logger_test.rb +23 -2
- data/test/new_relic/agent/agent_test.rb +49 -7
- data/test/new_relic/agent/configuration/manager_test.rb +8 -0
- data/test/new_relic/agent/configuration/orphan_configuration_test.rb +7 -0
- data/test/new_relic/agent/cross_app_monitor_test.rb +5 -6
- data/test/new_relic/agent/harvester_test.rb +13 -8
- data/test/new_relic/agent/instrumentation/action_controller_subscriber_test.rb +28 -7
- data/test/new_relic/agent/instrumentation/controller_instrumentation_test.rb +32 -21
- data/test/new_relic/agent/new_relic_service_test.rb +14 -0
- data/test/new_relic/agent/request_sampler_test.rb +5 -3
- data/test/new_relic/agent/rpm_agent_test.rb +2 -3
- data/test/new_relic/agent/sampler_collection_test.rb +15 -5
- data/test/new_relic/agent/sampler_test.rb +43 -0
- data/test/new_relic/agent/{cpu_sampler_test.rb → samplers/cpu_sampler_test.rb} +1 -1
- data/test/new_relic/agent/samplers/vm_sampler_test.rb +349 -0
- data/test/new_relic/agent/stats_engine/gc_profiler_test.rb +165 -44
- data/test/new_relic/agent/stats_hash_test.rb +1 -1
- data/test/new_relic/agent/transaction_test.rb +14 -0
- data/test/new_relic/agent/vm/monotonic_gc_profiler_test.rb +5 -5
- data/test/new_relic/agent/vm/mri_vm_test.rb +7 -0
- data/test/new_relic/agent/vm/snapshot_test.rb +5 -0
- data/test/new_relic/agent_test.rb +2 -2
- data/test/new_relic/control/instance_methods_test.rb +30 -0
- data/test/new_relic/control_test.rb +43 -21
- data/test/new_relic/dispatcher_test.rb +5 -0
- data/test/new_relic/local_environment_test.rb +3 -26
- data/test/new_relic/multiverse_helpers.rb +5 -0
- data/test/new_relic/noticed_error_test.rb +7 -0
- data/test/new_relic/rack/browser_monitoring_test.rb +13 -14
- data/test/test_helper.rb +2 -1
- metadata +56 -68
- metadata.gz.sig +1 -1
- data/lib/new_relic/agent/instrumentation/puma.rb +0 -25
- data/lib/new_relic/agent/instrumentation/unicorn_instrumentation.rb +0 -26
- data/test/multiverse/script/run_one +0 -5
- data/test/rum/basic.result.html +0 -10
- data/test/rum/basic.source.html +0 -10
- data/test/rum/comments1.result.html +0 -24
- data/test/rum/comments1.source.html +0 -24
- data/test/rum/comments2.result.html +0 -24
- data/test/rum/comments2.source.html +0 -24
- data/test/rum/gt_in_quotes1.result.html +0 -27
- data/test/rum/gt_in_quotes1.source.html +0 -27
- data/test/rum/gt_in_quotes2.result.html +0 -24
- data/test/rum/gt_in_quotes2.source.html +0 -24
- data/test/rum/gt_in_quotes_mismatch.result.html +0 -24
- data/test/rum/gt_in_quotes_mismatch.source.html +0 -24
- data/test/rum/gt_in_single_quotes1.result.html +0 -25
- data/test/rum/gt_in_single_quotes1.source.html +0 -25
- data/test/rum/gt_in_single_quotes_mismatch.result.html +0 -25
- data/test/rum/gt_in_single_quotes_mismatch.source.html +0 -25
- data/test/rum/incomplete_non_meta_tags.result.html +0 -10
- data/test/rum/incomplete_non_meta_tags.source.html +0 -10
- data/test/rum/no_body.result.html +0 -21
- data/test/rum/no_body.source.html +0 -21
- data/test/rum/no_header.result.html +0 -7
- data/test/rum/no_header.source.html +0 -7
- data/test/rum/no_html_and_no_header.result.html +0 -3
- data/test/rum/no_html_and_no_header.source.html +0 -3
- data/test/rum/no_start_header.result.html +0 -9
- data/test/rum/no_start_header.source.html +0 -9
- data/test/rum/script1.result.html +0 -19
- data/test/rum/script1.source.html +0 -19
- data/test/rum/script2.result.html +0 -17
- data/test/rum/script2.source.html +0 -17
- data/test/rum/x_ua_meta_tag.result.html +0 -10
- data/test/rum/x_ua_meta_tag.source.html +0 -10
- data/test/rum/x_ua_meta_tag_multiline.result.html +0 -11
- data/test/rum/x_ua_meta_tag_multiline.source.html +0 -11
- data/test/rum/x_ua_meta_tag_spaces_around_equals.result.html +0 -10
- data/test/rum/x_ua_meta_tag_spaces_around_equals.source.html +0 -10
- data/test/rum/x_ua_meta_tag_with_others.result.html +0 -11
- data/test/rum/x_ua_meta_tag_with_others.source.html +0 -11
- data/test/rum/x_ua_meta_tag_with_spaces.result.html +0 -10
- data/test/rum/x_ua_meta_tag_with_spaces.source.html +0 -10
@@ -16,12 +16,16 @@ DependencyDetection.defer do
|
|
16
16
|
end
|
17
17
|
|
18
18
|
executes do
|
19
|
+
class ActiveMerchant::Billing::Gateway
|
20
|
+
include NewRelic::Agent::MethodTracer
|
21
|
+
end
|
22
|
+
|
19
23
|
ActiveMerchant::Billing::Gateway.implementations.each do |gateway|
|
20
24
|
gateway.class_eval do
|
21
|
-
implemented_methods = public_instance_methods(false)
|
25
|
+
implemented_methods = public_instance_methods(false).map(&:to_sym)
|
22
26
|
gateway_name = self.name.split('::').last
|
23
|
-
[:authorize, :purchase, :credit, :void, :capture, :recurring].each do |operation|
|
24
|
-
if implemented_methods.include?(operation
|
27
|
+
[:authorize, :purchase, :credit, :void, :capture, :recurring, :store, :unstore, :update].each do |operation|
|
28
|
+
if implemented_methods.include?(operation)
|
25
29
|
add_method_tracer operation, "ActiveMerchant/gateway/#{gateway_name}/#{operation}", :scoped_metric_only => true
|
26
30
|
add_method_tracer operation, "ActiveMerchant/gateway/#{gateway_name}", :push_scope => false
|
27
31
|
add_method_tracer operation, "ActiveMerchant/operation/#{operation}", :push_scope => false
|
@@ -45,6 +45,7 @@ module NewRelic
|
|
45
45
|
def perform_action_with_newrelic_trace(*args); yield; end
|
46
46
|
end
|
47
47
|
|
48
|
+
# @api public
|
48
49
|
module ClassMethods
|
49
50
|
# Have NewRelic ignore actions in this controller. Specify the actions as hash options
|
50
51
|
# using :except and :only. If no actions are specified, all actions are ignored.
|
@@ -385,7 +386,14 @@ module NewRelic
|
|
385
386
|
def newrelic_response_code; end
|
386
387
|
|
387
388
|
def newrelic_request_headers
|
388
|
-
|
389
|
+
request = NewRelic::Agent::TransactionState.get.request
|
390
|
+
if request
|
391
|
+
if request.respond_to?(:headers)
|
392
|
+
request.headers
|
393
|
+
elsif request.respond_to?(:env)
|
394
|
+
request.env
|
395
|
+
end
|
396
|
+
end
|
389
397
|
end
|
390
398
|
|
391
399
|
# overrideable method to determine whether to trace an action
|
@@ -431,7 +439,7 @@ module NewRelic
|
|
431
439
|
txn = Transaction.start(category, options)
|
432
440
|
txn.name = TransactionNamer.new(self).name(options)
|
433
441
|
|
434
|
-
txn.apdex_start = _detect_upstream_wait(txn
|
442
|
+
txn.apdex_start = _detect_upstream_wait(txn)
|
435
443
|
_record_queue_length
|
436
444
|
|
437
445
|
return txn
|
@@ -468,7 +476,9 @@ module NewRelic
|
|
468
476
|
# Return a Time instance representing the upstream start time.
|
469
477
|
# now is a Time instance to fall back on if no other candidate
|
470
478
|
# for the start time is found.
|
471
|
-
def _detect_upstream_wait(
|
479
|
+
def _detect_upstream_wait(txn)
|
480
|
+
now = txn.start_time
|
481
|
+
return now unless txn.root?
|
472
482
|
if newrelic_request_headers
|
473
483
|
queue_start = QueueTime.parse_frontend_timestamp(newrelic_request_headers, now)
|
474
484
|
QueueTime.record_frontend_metrics(queue_start, now) if queue_start
|
@@ -17,10 +17,13 @@ DependencyDetection.defer do
|
|
17
17
|
|
18
18
|
executes do
|
19
19
|
ActionView::PartialTemplate.class_eval do
|
20
|
+
include NewRelic::Agent::MethodTracer
|
20
21
|
add_method_tracer :render, 'View/#{path_without_extension[%r{^(/.*/)?(.*)$},2]}.#{@view.template_format}.#{extension}/Partial'
|
21
22
|
end
|
23
|
+
|
22
24
|
# this is for template rendering, as opposed to partial rendering.
|
23
25
|
ActionView::Template.class_eval do
|
26
|
+
include NewRelic::Agent::MethodTracer
|
24
27
|
add_method_tracer :render, 'View/#{(path_without_extension || @view.controller.newrelic_metric_path)[%r{^(/.*/)?(.*)$},2]}.#{@view.template_format}.#{extension}/Rendering'
|
25
28
|
end
|
26
29
|
end
|
@@ -41,9 +44,9 @@ DependencyDetection.defer do
|
|
41
44
|
|
42
45
|
executes do
|
43
46
|
ActionController::Base.class_eval do
|
47
|
+
include NewRelic::Agent::MethodTracer
|
44
48
|
add_method_tracer :render, 'View/#{newrelic_metric_path}/Rendering'
|
45
49
|
end
|
46
|
-
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -62,9 +65,12 @@ DependencyDetection.defer do
|
|
62
65
|
|
63
66
|
executes do
|
64
67
|
ActionView::RenderablePartial.module_eval do
|
68
|
+
include NewRelic::Agent::MethodTracer
|
65
69
|
add_method_tracer :render_partial, 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Partial'
|
66
70
|
end
|
71
|
+
|
67
72
|
ActionView::Template.class_eval do
|
73
|
+
include NewRelic::Agent::MethodTracer
|
68
74
|
add_method_tracer :render, 'View/#{path[%r{^(/.*/)?(.*)$},2]}/Rendering'
|
69
75
|
end
|
70
76
|
end
|
@@ -17,7 +17,9 @@ DependencyDetection.defer do
|
|
17
17
|
class NewRelic::SidekiqInstrumentation
|
18
18
|
include NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
19
19
|
|
20
|
-
|
20
|
+
# Client middleware has additional parameters, and our tests use the
|
21
|
+
# middleware client-side to work inline.
|
22
|
+
def call(worker, msg, queue, *_)
|
21
23
|
perform_action_with_newrelic_trace(
|
22
24
|
:name => 'perform',
|
23
25
|
:class_name => msg['class'],
|
@@ -125,7 +125,7 @@ module NewRelic
|
|
125
125
|
def route_eval_with_newrelic(*args, &block)
|
126
126
|
begin
|
127
127
|
txn_name = TransactionNamer.transaction_name_for_route(env, request)
|
128
|
-
::NewRelic::Agent.set_transaction_name("#{self.class.name}/#{txn_name}") unless txn_name.nil?
|
128
|
+
::NewRelic::Agent.set_transaction_name("#{self.class.name}/#{txn_name}", :category => :sinatra) unless txn_name.nil?
|
129
129
|
rescue => e
|
130
130
|
::NewRelic::Agent.logger.debug("Failed during route_eval to set transaction name", e)
|
131
131
|
end
|
@@ -137,6 +137,8 @@ module NewRelic
|
|
137
137
|
if ignore_request?
|
138
138
|
env['newrelic.ignored'] = true
|
139
139
|
return dispatch_without_newrelic
|
140
|
+
elsif NewRelic::Agent::Transaction.current
|
141
|
+
return dispatch_and_notice_errors_with_newrelic
|
140
142
|
end
|
141
143
|
|
142
144
|
name = TransactionNamer.initial_transaction_name(request)
|
@@ -255,6 +255,14 @@ module NewRelic
|
|
255
255
|
File.expand_path(File.join(control.newrelic_root, 'cert', 'cacert.pem'))
|
256
256
|
end
|
257
257
|
|
258
|
+
def valid_to_marshal?(data)
|
259
|
+
@marshaller.dump(data)
|
260
|
+
true
|
261
|
+
rescue StandardError, SystemStackError => e
|
262
|
+
NewRelic::Agent.logger.warn("Unable to marshal environment report on connect.", e)
|
263
|
+
false
|
264
|
+
end
|
265
|
+
|
258
266
|
private
|
259
267
|
|
260
268
|
# A shorthand for NewRelic::Control.instance
|
@@ -140,10 +140,10 @@ class NewRelic::Agent::RequestSampler
|
|
140
140
|
# scoped to just one transaction, so Datastore/all has what we want.
|
141
141
|
map_metric('Datastore/all', :total_call_time => "databaseDuration")
|
142
142
|
map_metric('Datastore/all', :call_count => "databaseCallCount")
|
143
|
+
map_metric('GC/Transaction/all', :total_call_time => "gcCumulative")
|
143
144
|
|
144
145
|
# Web Metrics
|
145
146
|
map_metric('WebFrontend/QueueTime', :total_call_time => "queueDuration")
|
146
|
-
map_metric("GC/cumulative", :total_call_time => "gcCumulative")
|
147
147
|
map_metric('Memcache/allWeb', :total_call_time => "memcacheDuration")
|
148
148
|
|
149
149
|
map_metric('External/allWeb', :total_call_time => "externalDuration")
|
@@ -19,6 +19,14 @@ module NewRelic
|
|
19
19
|
attr_reader :id
|
20
20
|
@sampler_classes = []
|
21
21
|
|
22
|
+
def self.named(new_name)
|
23
|
+
@name = new_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.name
|
27
|
+
@name
|
28
|
+
end
|
29
|
+
|
22
30
|
def self.inherited(subclass)
|
23
31
|
@sampler_classes << subclass
|
24
32
|
end
|
@@ -28,12 +36,24 @@ module NewRelic
|
|
28
36
|
true
|
29
37
|
end
|
30
38
|
|
39
|
+
def self.enabled?
|
40
|
+
if @name
|
41
|
+
config_key = "disable_#{@name}_sampler"
|
42
|
+
!(Agent.config[config_key])
|
43
|
+
else
|
44
|
+
true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
31
48
|
def self.sampler_classes
|
32
49
|
@sampler_classes
|
33
50
|
end
|
34
51
|
|
35
|
-
|
36
|
-
|
52
|
+
# The ID passed in here is unused by our code, but is preserved in case
|
53
|
+
# we have clients who are defining their own subclasses of this class, and
|
54
|
+
# expecting to be able to call super with an ID.
|
55
|
+
def initialize(id=nil)
|
56
|
+
@id = id || self.class.name
|
37
57
|
end
|
38
58
|
|
39
59
|
def poll
|
@@ -25,6 +25,16 @@ module NewRelic
|
|
25
25
|
self.any? { |s| s.class == sampler_class }
|
26
26
|
end
|
27
27
|
|
28
|
+
# adds samplers to the sampler collection so that they run every
|
29
|
+
# minute. This is dynamically recognized by any class that
|
30
|
+
# subclasses NewRelic::Agent::Sampler
|
31
|
+
def load_samplers
|
32
|
+
Sampler.sampler_classes.each do |subclass|
|
33
|
+
add_sampler(subclass)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
28
38
|
def poll_samplers
|
29
39
|
@samplers.delete_if do |sampler|
|
30
40
|
begin
|
@@ -38,7 +48,9 @@ module NewRelic
|
|
38
48
|
end
|
39
49
|
|
40
50
|
def add_sampler(sampler_class)
|
41
|
-
|
51
|
+
supported = sampler_class.supported_on_this_platform?
|
52
|
+
enabled = sampler_class.enabled?
|
53
|
+
if supported && enabled
|
42
54
|
if !sampler_class_registered?(sampler_class)
|
43
55
|
sampler = sampler_class.new
|
44
56
|
sampler.setup_events(@event_listener) if sampler.respond_to?(:setup_events)
|
@@ -9,8 +9,10 @@ module NewRelic
|
|
9
9
|
module Samplers
|
10
10
|
class CpuSampler < NewRelic::Agent::Sampler
|
11
11
|
attr_reader :last_time
|
12
|
+
|
13
|
+
named :cpu
|
14
|
+
|
12
15
|
def initialize
|
13
|
-
super :cpu
|
14
16
|
@processor_count = NewRelic::Agent::SystemInfo.processor_count
|
15
17
|
if @processor_count.nil?
|
16
18
|
NewRelic::Agent.logger.warn("Failed to determine processor count, assuming 1")
|
@@ -16,8 +16,9 @@ module NewRelic
|
|
16
16
|
# versions of DJ where distinct queues are supported, it breaks it out by queue name.
|
17
17
|
#
|
18
18
|
class DelayedJobSampler < NewRelic::Agent::Sampler
|
19
|
+
named :delayed_job
|
20
|
+
|
19
21
|
def initialize
|
20
|
-
super :delayed_job_queue
|
21
22
|
raise Unsupported, "DJ instrumentation disabled" if Agent.config[:disable_dj]
|
22
23
|
raise Unsupported, "No DJ worker present" unless NewRelic::DelayedJobInjection.worker_name
|
23
24
|
end
|
@@ -9,10 +9,11 @@ module NewRelic
|
|
9
9
|
module Samplers
|
10
10
|
|
11
11
|
class MemorySampler < NewRelic::Agent::Sampler
|
12
|
+
named :memory
|
13
|
+
|
12
14
|
attr_accessor :sampler
|
13
15
|
|
14
16
|
def initialize
|
15
|
-
super :memory
|
16
17
|
# macos, linux, solaris
|
17
18
|
if defined? JRuby
|
18
19
|
@sampler = JavaHeapSampler.new
|
@@ -8,9 +8,7 @@ module NewRelic
|
|
8
8
|
module Agent
|
9
9
|
module Samplers
|
10
10
|
class ObjectSampler < NewRelic::Agent::Sampler
|
11
|
-
|
12
|
-
super :objects
|
13
|
-
end
|
11
|
+
named :object
|
14
12
|
|
15
13
|
def self.supported_on_this_platform?
|
16
14
|
NewRelic::LanguageSupport.object_space_usable? && ObjectSpace.respond_to?(:live_objects)
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# This file is distributed under New Relic's license terms.
|
3
|
+
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.
|
4
|
+
|
5
|
+
require 'new_relic/agent/sampler'
|
6
|
+
require 'new_relic/agent/vm'
|
7
|
+
|
8
|
+
module NewRelic
|
9
|
+
module Agent
|
10
|
+
module Samplers
|
11
|
+
class VMSampler < Sampler
|
12
|
+
GC_RUNS_METRIC = 'RubyVM/GC/runs'.freeze
|
13
|
+
HEAP_LIVE_METRIC = 'RubyVM/GC/heap_live'.freeze
|
14
|
+
HEAP_FREE_METRIC = 'RubyVM/GC/heap_free'.freeze
|
15
|
+
THREAD_COUNT_METRIC = 'RubyVM/Threads/all'.freeze
|
16
|
+
OBJECT_ALLOCATIONS_METRIC = 'RubyVM/GC/total_allocated_object'.freeze
|
17
|
+
MAJOR_GC_METRIC = 'RubyVM/GC/major_gc_count'.freeze
|
18
|
+
MINOR_GC_METRIC = 'RubyVM/GC/minor_gc_count'.freeze
|
19
|
+
METHOD_INVALIDATIONS_METRIC = 'RubyVM/CacheInvalidations/method'.freeze
|
20
|
+
CONSTANT_INVALIDATIONS_METRIC = 'RubyVM/CacheInvalidations/constant'.freeze
|
21
|
+
|
22
|
+
attr_reader :transaction_count
|
23
|
+
|
24
|
+
named :vm
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
@lock = Mutex.new
|
28
|
+
@transaction_count = 0
|
29
|
+
@last_snapshot = take_snapshot
|
30
|
+
end
|
31
|
+
|
32
|
+
def take_snapshot
|
33
|
+
NewRelic::Agent::VM.snapshot
|
34
|
+
end
|
35
|
+
|
36
|
+
def setup_events(event_listener)
|
37
|
+
event_listener.subscribe(:transaction_finished, &method(:on_transaction_finished))
|
38
|
+
end
|
39
|
+
|
40
|
+
def on_transaction_finished(*_)
|
41
|
+
@lock.synchronize { @transaction_count += 1 }
|
42
|
+
end
|
43
|
+
|
44
|
+
def reset_transaction_count
|
45
|
+
@lock.synchronize do
|
46
|
+
old_count = @transaction_count
|
47
|
+
@transaction_count = 0
|
48
|
+
old_count
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def record_gc_runs_metric(snapshot, txn_count)
|
53
|
+
if snapshot.gc_total_time || snapshot.gc_runs
|
54
|
+
if snapshot.gc_total_time
|
55
|
+
gc_time = snapshot.gc_total_time - @last_snapshot.gc_total_time.to_f
|
56
|
+
end
|
57
|
+
if snapshot.gc_runs
|
58
|
+
gc_runs = snapshot.gc_runs - @last_snapshot.gc_runs
|
59
|
+
end
|
60
|
+
wall_clock_time = snapshot.taken_at - @last_snapshot.taken_at
|
61
|
+
NewRelic::Agent.agent.stats_engine.record_metrics(GC_RUNS_METRIC) do |stats|
|
62
|
+
stats.call_count += txn_count
|
63
|
+
stats.total_call_time += gc_runs if gc_runs
|
64
|
+
stats.total_exclusive_time += gc_time if gc_time
|
65
|
+
stats.max_call_time = (gc_time.nil? ? 0 : 1)
|
66
|
+
stats.sum_of_squares += wall_clock_time
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def record_delta(snapshot, key, metric, txn_count)
|
72
|
+
value = snapshot.send(key)
|
73
|
+
if value
|
74
|
+
delta = value - @last_snapshot.send(key)
|
75
|
+
NewRelic::Agent.agent.stats_engine.record_metrics(metric) do |stats|
|
76
|
+
stats.call_count += txn_count
|
77
|
+
stats.total_call_time += delta
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def record_gauge_metric(metric_name, value)
|
83
|
+
NewRelic::Agent.agent.stats_engine.record_metrics(metric_name) do |stats|
|
84
|
+
stats.call_count = value
|
85
|
+
stats.sum_of_squares = 1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def record_heap_live_metric(snapshot)
|
90
|
+
if snapshot.heap_live
|
91
|
+
record_gauge_metric(HEAP_LIVE_METRIC, snapshot.heap_live)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def record_heap_free_metric(snapshot)
|
96
|
+
if snapshot.heap_free
|
97
|
+
record_gauge_metric(HEAP_FREE_METRIC, snapshot.heap_free)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def record_thread_count_metric(snapshot)
|
102
|
+
if snapshot.thread_count
|
103
|
+
record_gauge_metric(THREAD_COUNT_METRIC, snapshot.thread_count)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def poll
|
108
|
+
snap = take_snapshot
|
109
|
+
tcount = reset_transaction_count
|
110
|
+
|
111
|
+
record_gc_runs_metric(snap, tcount)
|
112
|
+
record_delta(snap, :total_allocated_object, OBJECT_ALLOCATIONS_METRIC, tcount)
|
113
|
+
record_delta(snap, :major_gc_count, MAJOR_GC_METRIC, tcount)
|
114
|
+
record_delta(snap, :minor_gc_count, MINOR_GC_METRIC, tcount)
|
115
|
+
record_delta(snap, :method_cache_invalidations, METHOD_INVALIDATIONS_METRIC, tcount)
|
116
|
+
record_delta(snap, :constant_cache_invalidations, CONSTANT_INVALIDATIONS_METRIC, tcount)
|
117
|
+
record_heap_live_metric(snap)
|
118
|
+
record_heap_free_metric(snap)
|
119
|
+
record_thread_count_metric(snap)
|
120
|
+
|
121
|
+
@last_snapshot = snap
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -76,21 +76,6 @@ module NewRelic
|
|
76
76
|
|
77
77
|
alias trace_call record_data_point
|
78
78
|
|
79
|
-
# Records multiple data points as one method call - this handles
|
80
|
-
# all the aggregation that would be done with multiple
|
81
|
-
# record_data_point calls
|
82
|
-
def record_multiple_data_points(total_value, count=1)
|
83
|
-
return record_data_point(total_value) if count == 1
|
84
|
-
@call_count += count
|
85
|
-
@total_call_time += total_value
|
86
|
-
avg_val = total_value / count
|
87
|
-
@min_call_time = avg_val if avg_val < @min_call_time || @call_count == count
|
88
|
-
@max_call_time = avg_val if avg_val > @max_call_time
|
89
|
-
@total_exclusive_time += total_value
|
90
|
-
@sum_of_squares += (avg_val * avg_val) * count
|
91
|
-
self
|
92
|
-
end
|
93
|
-
|
94
79
|
# increments the call_count by one
|
95
80
|
def increment_count(value = 1)
|
96
81
|
@call_count += value
|
@@ -7,68 +7,83 @@ module NewRelic
|
|
7
7
|
module Agent
|
8
8
|
class StatsEngine
|
9
9
|
module GCProfiler
|
10
|
+
GCSnapshot = Struct.new(:gc_time_s, :gc_call_count)
|
11
|
+
|
10
12
|
def self.init
|
11
|
-
@profiler
|
12
|
-
@profiler =
|
13
|
-
|
13
|
+
return @profiler if @initialized
|
14
|
+
@profiler = if RailsBenchProfiler.enabled?
|
15
|
+
RailsBenchProfiler.new
|
16
|
+
elsif CoreGCProfiler.enabled?
|
17
|
+
CoreGCProfiler.new
|
18
|
+
end
|
19
|
+
@initialized = true
|
14
20
|
@profiler
|
15
21
|
end
|
16
22
|
|
17
|
-
def self.
|
18
|
-
@profiler
|
23
|
+
def self.reset
|
24
|
+
@profiler = nil
|
25
|
+
@initialized = nil
|
19
26
|
end
|
20
27
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
def self.take_snapshot
|
29
|
+
init
|
30
|
+
if @profiler
|
31
|
+
GCSnapshot.new(@profiler.call_time_s, @profiler.call_count)
|
32
|
+
else
|
33
|
+
nil
|
27
34
|
end
|
35
|
+
end
|
28
36
|
|
29
|
-
|
30
|
-
|
31
|
-
|
37
|
+
def self.record_delta(start_snapshot, end_snapshot)
|
38
|
+
if @profiler && start_snapshot && end_snapshot
|
39
|
+
elapsed_gc_time_s = end_snapshot.gc_time_s - start_snapshot.gc_time_s
|
40
|
+
num_calls = end_snapshot.gc_call_count - start_snapshot.gc_call_count
|
41
|
+
record_gc_metric(num_calls, elapsed_gc_time_s)
|
32
42
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
@last_count = call_count
|
38
|
-
reset
|
43
|
+
@profiler.reset
|
44
|
+
elapsed_gc_time_s
|
45
|
+
end
|
46
|
+
end
|
39
47
|
|
40
|
-
|
41
|
-
|
48
|
+
def self.record_gc_metric(call_count, elapsed)
|
49
|
+
NewRelic::Agent.agent.stats_engine.record_metrics(gc_metric_specs) do |stats|
|
50
|
+
stats.call_count += call_count
|
51
|
+
stats.total_call_time += elapsed
|
52
|
+
stats.total_exclusive_time += elapsed
|
42
53
|
end
|
54
|
+
end
|
43
55
|
|
44
|
-
|
56
|
+
GC_ROLLUP = 'GC/Transaction/all'.freeze
|
57
|
+
GC_OTHER = 'GC/Transaction/allOther'.freeze
|
58
|
+
GC_WEB = 'GC/Transaction/allWeb'.freeze
|
45
59
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
.record_metrics('GC/cumulative', nil, :scoped => true) do |stat|
|
54
|
-
stat.record_multiple_data_points(elapsed, num_calls)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
60
|
+
SCOPE_PLACEHOLDER = NewRelic::Agent::StatsEngine::MetricStats::SCOPE_PLACEHOLDER
|
61
|
+
|
62
|
+
GC_ROLLUP_SPEC = NewRelic::MetricSpec.new(GC_ROLLUP)
|
63
|
+
GC_OTHER_SPEC = NewRelic::MetricSpec.new(GC_OTHER)
|
64
|
+
GC_OTHER_SCOPED_SPEC = NewRelic::MetricSpec.new(GC_OTHER, SCOPE_PLACEHOLDER)
|
65
|
+
GC_WEB_SPEC = NewRelic::MetricSpec.new(GC_WEB)
|
66
|
+
GC_WEB_SCOPED_SPEC = NewRelic::MetricSpec.new(GC_WEB, SCOPE_PLACEHOLDER)
|
58
67
|
|
59
|
-
|
60
|
-
|
68
|
+
def self.gc_metric_specs
|
69
|
+
# The .dup call on the scoped MetricSpec here is necessary because
|
70
|
+
# metric specs with non-empty scopes will have their scopes mutated
|
71
|
+
# when the metrics are merged into the global stats hash, and we don't
|
72
|
+
# want to mutate the original MetricSpec.
|
73
|
+
if NewRelic::Agent::Transaction.recording_web_transaction?
|
74
|
+
[GC_ROLLUP_SPEC, GC_WEB_SPEC, GC_WEB_SCOPED_SPEC.dup]
|
75
|
+
else
|
76
|
+
[GC_ROLLUP_SPEC, GC_OTHER_SPEC, GC_OTHER_SCOPED_SPEC.dup]
|
61
77
|
end
|
62
78
|
end
|
63
79
|
|
64
|
-
class RailsBenchProfiler
|
80
|
+
class RailsBenchProfiler
|
65
81
|
def self.enabled?
|
66
82
|
::GC.respond_to?(:time) && ::GC.respond_to?(:collections)
|
67
83
|
end
|
68
84
|
|
69
|
-
|
70
|
-
|
71
|
-
::GC.time # this should already be microseconds
|
85
|
+
def call_time_s
|
86
|
+
::GC.time.to_f / 1_000_000 # this value is reported in us, so convert to s
|
72
87
|
end
|
73
88
|
|
74
89
|
def call_count
|
@@ -80,49 +95,25 @@ module NewRelic
|
|
80
95
|
end
|
81
96
|
end
|
82
97
|
|
83
|
-
class CoreGCProfiler
|
98
|
+
class CoreGCProfiler
|
84
99
|
def self.enabled?
|
85
|
-
|
86
|
-
defined?(::GC::Profiler) && ::GC::Profiler.enabled?
|
100
|
+
NewRelic::LanguageSupport.gc_profiler_enabled?
|
87
101
|
end
|
88
102
|
|
89
|
-
|
90
|
-
|
91
|
-
def call_time
|
92
|
-
::GC::Profiler.total_time * 1_000_000.0 # convert seconds to microseconds
|
103
|
+
def call_time_s
|
104
|
+
NewRelic::Agent.instance.monotonic_gc_profiler.total_time_s
|
93
105
|
end
|
94
106
|
|
95
107
|
def call_count
|
96
108
|
::GC.count
|
97
109
|
end
|
98
110
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
# Only present for legacy support of Rubinius < 2.0.0
|
106
|
-
class LegacyRubiniusProfiler < Profiler
|
107
|
-
def self.enabled?
|
108
|
-
self.has_rubinius_profiler? && !has_core_profiler?
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.has_rubinius_profiler?
|
112
|
-
defined?(::Rubinius) && defined?(::Rubinius::GC) && ::Rubinius::GC.respond_to?(:count)
|
113
|
-
end
|
114
|
-
|
115
|
-
def self.has_core_profiler?
|
116
|
-
defined?(::GC::Profiler)
|
117
|
-
end
|
118
|
-
|
119
|
-
def call_time
|
120
|
-
::Rubinius::GC.time * 1000
|
121
|
-
end
|
122
|
-
|
123
|
-
def call_count
|
124
|
-
::Rubinius::GC.count
|
125
|
-
end
|
111
|
+
# When using GC::Profiler, it's important to periodically call
|
112
|
+
# GC::Profiler.clear in order to avoid unbounded growth in the number
|
113
|
+
# of GC recordds that are stored. However, we actually do this
|
114
|
+
# internally within MonotonicGCProfiler on calls to #total_time_s,
|
115
|
+
# so the reset here is a no-op.
|
116
|
+
def reset; end
|
126
117
|
end
|
127
118
|
end
|
128
119
|
end
|