newrelic_rpm 6.15.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +96 -22
  3. data/README.md +2 -2
  4. data/lib/new_relic/agent.rb +0 -6
  5. data/lib/new_relic/agent/autostart.rb +1 -2
  6. data/lib/new_relic/agent/configuration/default_source.rb +270 -104
  7. data/lib/new_relic/agent/configuration/manager.rb +2 -2
  8. data/lib/new_relic/agent/datastores/redis.rb +0 -4
  9. data/lib/new_relic/agent/distributed_tracing.rb +0 -66
  10. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +0 -16
  11. data/lib/new_relic/agent/instrumentation/bunny.rb +10 -152
  12. data/lib/new_relic/agent/instrumentation/bunny/chain.rb +45 -0
  13. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +152 -0
  14. data/lib/new_relic/agent/instrumentation/bunny/prepend.rb +35 -0
  15. data/lib/new_relic/agent/instrumentation/curb.rb +9 -241
  16. data/lib/new_relic/agent/instrumentation/curb/chain.rb +93 -0
  17. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +222 -0
  18. data/lib/new_relic/agent/instrumentation/curb/prepend.rb +63 -0
  19. data/lib/new_relic/agent/instrumentation/delayed_job/chain.rb +38 -0
  20. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +53 -0
  21. data/lib/new_relic/agent/instrumentation/delayed_job/prepend.rb +34 -0
  22. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +8 -50
  23. data/lib/new_relic/agent/instrumentation/excon.rb +2 -1
  24. data/lib/new_relic/agent/instrumentation/grape.rb +13 -113
  25. data/lib/new_relic/agent/instrumentation/grape/chain.rb +25 -0
  26. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +100 -0
  27. data/lib/new_relic/agent/instrumentation/grape/prepend.rb +17 -0
  28. data/lib/new_relic/agent/instrumentation/httpclient.rb +8 -30
  29. data/lib/new_relic/agent/instrumentation/httpclient/chain.rb +25 -0
  30. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +38 -0
  31. data/lib/new_relic/agent/instrumentation/httpclient/prepend.rb +17 -0
  32. data/lib/new_relic/agent/instrumentation/httprb.rb +29 -0
  33. data/lib/new_relic/agent/instrumentation/httprb/chain.rb +22 -0
  34. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +30 -0
  35. data/lib/new_relic/agent/instrumentation/httprb/prepend.rb +15 -0
  36. data/lib/new_relic/agent/instrumentation/memcache.rb +54 -69
  37. data/lib/new_relic/agent/instrumentation/memcache/chain.rb +16 -0
  38. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +38 -121
  39. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +56 -0
  40. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +88 -0
  41. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +88 -0
  42. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +2 -0
  43. data/lib/new_relic/agent/instrumentation/mongo.rb +7 -0
  44. data/lib/new_relic/agent/instrumentation/net_http.rb +39 -0
  45. data/lib/new_relic/agent/instrumentation/net_http/chain.rb +25 -0
  46. data/lib/new_relic/agent/instrumentation/{net_prepend.rb → net_http/instrumentation.rb} +3 -3
  47. data/lib/new_relic/agent/instrumentation/net_http/prepend.rb +21 -0
  48. data/lib/new_relic/agent/instrumentation/padrino.rb +18 -53
  49. data/lib/new_relic/agent/instrumentation/padrino/chain.rb +34 -0
  50. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +27 -0
  51. data/lib/new_relic/agent/instrumentation/padrino/prepend.rb +20 -0
  52. data/lib/new_relic/agent/instrumentation/rack.rb +29 -139
  53. data/lib/new_relic/agent/instrumentation/rack/chain.rb +57 -0
  54. data/lib/new_relic/agent/instrumentation/rack/helpers.rb +32 -0
  55. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +73 -0
  56. data/lib/new_relic/agent/instrumentation/rack/prepend.rb +36 -0
  57. data/lib/new_relic/agent/instrumentation/rake.rb +13 -154
  58. data/lib/new_relic/agent/instrumentation/rake/chain.rb +25 -0
  59. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +144 -0
  60. data/lib/new_relic/agent/instrumentation/rake/prepend.rb +14 -0
  61. data/lib/new_relic/agent/instrumentation/redis.rb +10 -109
  62. data/lib/new_relic/agent/instrumentation/redis/chain.rb +34 -0
  63. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +65 -0
  64. data/lib/new_relic/agent/instrumentation/redis/prepend.rb +24 -0
  65. data/lib/new_relic/agent/instrumentation/resque.rb +8 -28
  66. data/lib/new_relic/agent/instrumentation/resque/chain.rb +22 -0
  67. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +33 -0
  68. data/lib/new_relic/agent/instrumentation/resque/prepend.rb +16 -0
  69. data/lib/new_relic/agent/instrumentation/sinatra.rb +20 -158
  70. data/lib/new_relic/agent/instrumentation/sinatra/chain.rb +55 -0
  71. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +29 -34
  72. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +118 -0
  73. data/lib/new_relic/agent/instrumentation/sinatra/prepend.rb +33 -0
  74. data/lib/new_relic/agent/instrumentation/typhoeus.rb +10 -89
  75. data/lib/new_relic/agent/instrumentation/typhoeus/chain.rb +22 -0
  76. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +82 -0
  77. data/lib/new_relic/agent/instrumentation/typhoeus/prepend.rb +14 -0
  78. data/lib/new_relic/agent/new_relic_service.rb +3 -12
  79. data/lib/new_relic/agent/sql_sampler.rb +1 -1
  80. data/lib/new_relic/agent/transaction.rb +1 -4
  81. data/lib/new_relic/control/frameworks/rails.rb +11 -9
  82. data/lib/new_relic/control/instance_methods.rb +1 -0
  83. data/lib/new_relic/dependency_detection.rb +116 -10
  84. data/lib/new_relic/noticed_error.rb +1 -5
  85. data/lib/new_relic/supportability_helper.rb +1 -2
  86. data/lib/new_relic/version.rb +2 -2
  87. data/newrelic_rpm.gemspec +1 -1
  88. metadata +53 -8
  89. data/cert/cacert.pem +0 -1177
  90. data/lib/new_relic/agent/instrumentation/http.rb +0 -49
  91. data/lib/new_relic/agent/instrumentation/net.rb +0 -70
@@ -0,0 +1,57 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Rack
7
+ module Chain
8
+ def self.instrument! builder_class
9
+ NewRelic::Agent::Instrumentation::RackBuilder.track_deferred_detection builder_class
10
+
11
+ builder_class.class_eval do
12
+ include ::NewRelic::Agent::Instrumentation::RackBuilder
13
+
14
+ def to_app_with_newrelic_deferred_dependency_detection
15
+ with_deferred_dependency_detection { to_app_without_newrelic }
16
+ end
17
+
18
+ alias_method :to_app_without_newrelic, :to_app
19
+ alias_method :to_app, :to_app_with_newrelic_deferred_dependency_detection
20
+
21
+ if ::NewRelic::Agent::Instrumentation::RackHelpers.middleware_instrumentation_enabled?
22
+ ::NewRelic::Agent.logger.info "Installing #{builder_class} middleware instrumentation"
23
+
24
+ def run_with_newrelic(app, *args)
25
+ run_with_tracing(app) { |wrapped_app| run_without_newrelic(wrapped_app, *args) }
26
+ end
27
+
28
+ alias_method :run_without_newrelic, :run
29
+ alias_method :run, :run_with_newrelic
30
+
31
+ def use_with_newrelic(middleware_class, *args, &block)
32
+ use_with_tracing(middleware_class) { |wrapped_class| use_without_newrelic(wrapped_class, *args, &block) }
33
+ end
34
+
35
+ alias_method :use_without_newrelic, :use
36
+ alias_method :use, :use_with_newrelic
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ module URLMap
43
+ module Chain
44
+ def self.instrument! url_map_class
45
+ url_map_class.class_eval do
46
+ alias_method :initialize_without_newrelic, :initialize
47
+
48
+ def initialize(map = {})
49
+ traced_map = ::NewRelic::Agent::Instrumentation::RackURLMap.generate_traced_map(map)
50
+ initialize_without_newrelic(traced_map)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module RackHelpers
7
+ def self.version_supported?
8
+ rack_version_supported? || puma_rack_version_supported?
9
+ end
10
+
11
+ def self.rack_version_supported?
12
+ return false unless defined? ::Rack
13
+
14
+ version = Gem::Version.new(::Rack.release)
15
+ min_version = Gem::Version.new('1.1.0')
16
+ version >= min_version
17
+ end
18
+
19
+ def self.puma_rack_version_supported?
20
+ return false unless defined? ::Puma::Const::PUMA_VERSION
21
+
22
+ version = Gem::Version.new(::Puma::Const::PUMA_VERSION)
23
+ min_version = Gem::Version.new('2.12.0')
24
+ version >= min_version
25
+ end
26
+
27
+ def self.middleware_instrumentation_enabled?
28
+ version_supported? && !::NewRelic::Agent.config[:disable_middleware_instrumentation]
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic
6
+ module Agent
7
+ module Instrumentation
8
+ module RackBuilder
9
+
10
+ def self.track_deferred_detection builder_class
11
+ class << builder_class
12
+ attr_accessor :_nr_deferred_detection_ran
13
+ end
14
+ builder_class._nr_deferred_detection_ran = false
15
+ end
16
+
17
+ def deferred_dependency_check
18
+ return if self.class._nr_deferred_detection_ran
19
+
20
+ NewRelic::Agent.logger.info "Doing deferred dependency-detection before Rack startup"
21
+ DependencyDetection.detect!
22
+ self.class._nr_deferred_detection_ran = true
23
+ end
24
+
25
+ def check_for_late_instrumentation(app)
26
+ return if defined?(@checked_for_late_instrumentation) && @checked_for_late_instrumentation
27
+ @checked_for_late_instrumentation = true
28
+ if middleware_instrumentation_enabled?
29
+ if ::NewRelic::Agent::Instrumentation::MiddlewareProxy.needs_wrapping?(app)
30
+ ::NewRelic::Agent.logger.info("We weren't able to instrument all of your Rack middlewares.",
31
+ "To correct this, ensure you 'require \"newrelic_rpm\"' before setting up your middleware stack.")
32
+ end
33
+ end
34
+ end
35
+
36
+ # We patch the #to_app method for a reason that actually has nothing to do with
37
+ # instrumenting rack itself. It happens to be a convenient and
38
+ # easy-to-hook point that happens late in the startup sequence of almost
39
+ # every application, making it a good place to do a final call to
40
+ # DependencyDetection.detect!, since all libraries are likely loaded at
41
+ # this point.
42
+ def with_deferred_dependency_detection
43
+ deferred_dependency_check
44
+ yield.tap{ |result| check_for_late_instrumentation(result) }
45
+ end
46
+
47
+ def middleware_instrumentation_enabled?
48
+ ::NewRelic::Agent::Instrumentation::RackHelpers.middleware_instrumentation_enabled?
49
+ end
50
+
51
+ def run_with_tracing app
52
+ return yield(app) unless middleware_instrumentation_enabled?
53
+ yield ::NewRelic::Agent::Instrumentation::MiddlewareProxy.wrap(app, true)
54
+ end
55
+
56
+ def use_with_tracing middleware_class
57
+ return if middleware_class.nil?
58
+ return yield(middleware_class) unless middleware_instrumentation_enabled?
59
+ yield ::NewRelic::Agent::Instrumentation::MiddlewareProxy.for_class(middleware_class)
60
+ end
61
+ end
62
+
63
+ module RackURLMap
64
+ def self.generate_traced_map(map)
65
+ map.inject({}) do |traced_map, (url, handler)|
66
+ traced_map[url] = NewRelic::Agent::Instrumentation::MiddlewareProxy.wrap(handler, true)
67
+ traced_map
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Rack
7
+
8
+ module URLMap
9
+ module Prepend
10
+ def initialize(map = {})
11
+ super ::NewRelic::Agent::Instrumentation::RackURLMap.generate_traced_map(map)
12
+ end
13
+ end
14
+ end
15
+
16
+ module Prepend
17
+ include ::NewRelic::Agent::Instrumentation::RackBuilder
18
+
19
+ def self.prepended builder_class
20
+ NewRelic::Agent::Instrumentation::RackBuilder.track_deferred_detection builder_class
21
+ end
22
+
23
+ def to_app
24
+ with_deferred_dependency_detection { super }
25
+ end
26
+
27
+ def run(app, *args)
28
+ run_with_tracing(app) { |wrapped_app| super(wrapped_app, *args) }
29
+ end
30
+
31
+ def use(middleware_class, *args, &blk)
32
+ use_with_tracing(middleware_class) { |wrapped_class| super(wrapped_class, *args, &blk) }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -2,17 +2,19 @@
2
2
  # This file is distributed under New Relic's license terms.
3
3
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
4
 
5
+ require_relative 'rake/instrumentation'
6
+ require_relative 'rake/chain'
7
+ require_relative 'rake/prepend'
8
+
5
9
  DependencyDetection.defer do
6
10
  # Why not :rake? newrelic-rake used that name, so avoid conflicting
7
11
  named :rake_instrumentation
12
+ configure_with :rake
8
13
 
9
- depends_on do
10
- defined?(::Rake) &&
11
- defined?(::Rake::VERSION) &&
12
- ::NewRelic::Agent.config[:'disable_rake'] == false &&
13
- ::NewRelic::Agent.config[:'rake.tasks'].any? &&
14
- ::NewRelic::Agent::Instrumentation::RakeInstrumentation.should_install?
15
- end
14
+ depends_on { defined?(::Rake) && defined?(::Rake::VERSION) }
15
+ depends_on { Gem::Version.new(::Rake::VERSION) >= Gem::Version.new("10.0.0") }
16
+ depends_on { ::NewRelic::Agent.config[:'rake.tasks'].any? }
17
+ depends_on { ::NewRelic::Agent::Instrumentation::Rake.safe_from_third_party_gem? }
16
18
 
17
19
  executes do
18
20
  ::NewRelic::Agent.logger.info "Installing Rake instrumentation"
@@ -20,153 +22,10 @@ DependencyDetection.defer do
20
22
  end
21
23
 
22
24
  executes do
23
- module Rake
24
- class Task
25
- alias_method :invoke_without_newrelic, :invoke
26
-
27
- def invoke(*args)
28
- unless NewRelic::Agent::Instrumentation::RakeInstrumentation.should_trace? name
29
- return invoke_without_newrelic(*args)
30
- end
31
-
32
- begin
33
- timeout = NewRelic::Agent.config[:'rake.connect_timeout']
34
- NewRelic::Agent.instance.wait_on_connect(timeout)
35
- rescue => e
36
- NewRelic::Agent.logger.error("Exception in wait_on_connect", e)
37
- return invoke_without_newrelic(*args)
38
- end
39
-
40
- NewRelic::Agent::Instrumentation::RakeInstrumentation.before_invoke_transaction(self)
41
-
42
- NewRelic::Agent::Tracer.in_transaction(name: "OtherTransaction/Rake/invoke/#{name}", category: :rake) do
43
- NewRelic::Agent::Instrumentation::RakeInstrumentation.record_attributes(args, self)
44
- invoke_without_newrelic(*args)
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
51
-
52
- module NewRelic
53
- module Agent
54
- module Instrumentation
55
- module RakeInstrumentation
56
- def self.should_install?
57
- is_supported_version? && safe_from_third_party_gem?
58
- end
59
-
60
- def self.is_supported_version?
61
- Gem::Version.new(::Rake::VERSION) >= Gem::Version.new("10.0.0")
62
- end
63
-
64
- def self.safe_from_third_party_gem?
65
- if NewRelic::LanguageSupport.bundled_gem?("newrelic-rake")
66
- ::NewRelic::Agent.logger.info("Not installing New Relic supported Rake instrumentation because the third party newrelic-rake gem is present")
67
- false
68
- else
69
- true
70
- end
71
- end
72
-
73
- def self.should_trace?(name)
74
- NewRelic::Agent.config[:'rake.tasks'].any? do |regex|
75
- regex.match(name)
76
- end
77
- end
78
-
79
- def self.instrument_execute_on_prereqs(task)
80
- task.prerequisite_tasks.each do |child_task|
81
- instrument_execute(child_task)
82
- end
83
- end
84
-
85
- def self.instrument_execute(task)
86
- return if task.instance_variable_get(:@__newrelic_instrumented_execute)
87
-
88
- task.instance_variable_set(:@__newrelic_instrumented_execute, true)
89
- task.instance_eval do
90
- def execute(*args, &block)
91
- NewRelic::Agent::MethodTracer.trace_execution_scoped("Rake/execute/#{self.name}") do
92
- super
93
- end
94
- end
95
- end
96
-
97
- instrument_execute_on_prereqs(task)
98
- end
99
-
100
- def self.instrument_invoke_prerequisites_concurrently(task)
101
- task.instance_eval do
102
- def invoke_prerequisites_concurrently(*_)
103
- NewRelic::Agent::MethodTracer.trace_execution_scoped("Rake/execute/multitask") do
104
- prereqs = self.prerequisite_tasks.map(&:name).join(", ")
105
- if txn = ::NewRelic::Agent::Tracer.current_transaction
106
- txn.current_segment.params[:statement] = NewRelic::Agent::Database.truncate_query("Couldn't trace concurrent prereq tasks: #{prereqs}")
107
- end
108
- super
109
- end
110
- end
111
- end
112
- end
113
-
114
- def self.before_invoke_transaction(task)
115
- ensure_at_exit
116
-
117
- # We can't represent overlapping operations yet, so if multitask just
118
- # make one node and annotate with prereq task names
119
- if task.application.options.always_multitask
120
- instrument_invoke_prerequisites_concurrently(task)
121
- else
122
- instrument_execute_on_prereqs(task)
123
- end
124
- rescue => e
125
- NewRelic::Agent.logger.error("Error during Rake task invoke", e)
126
- end
127
-
128
- def self.record_attributes(args, task)
129
- command_line = task.application.top_level_tasks.join(" ")
130
- NewRelic::Agent::Transaction.merge_untrusted_agent_attributes({ :command => command_line },
131
- :'job.rake',
132
- NewRelic::Agent::AttributeFilter::DST_NONE)
133
- named_args = name_the_args(args, task.arg_names)
134
- unless named_args.empty?
135
- NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(named_args,
136
- :'job.rake.args',
137
- NewRelic::Agent::AttributeFilter::DST_NONE)
138
- end
139
- rescue => e
140
- NewRelic::Agent.logger.error("Error during Rake task attribute recording.", e)
141
- end
142
-
143
- # Expects literal args passed to the task and array of task names
144
- # If names are present without matching args, still sets them with nils
145
- def self.name_the_args(args, names)
146
- unfulfilled_names_length = names.length - args.length
147
- if unfulfilled_names_length > 0
148
- args.concat(Array.new(unfulfilled_names_length))
149
- end
150
-
151
- result = {}
152
- args.zip(names).each_with_index do |(value, key), index|
153
- result[key || index.to_s] = value
154
- end
155
- result
156
- end
157
-
158
- def self.ensure_at_exit
159
- return if @installed_at_exit
160
-
161
- at_exit do
162
- # The agent's default at_exit might not default to installing, but
163
- # if we are running an instrumented rake task, we always want it.
164
- NewRelic::Agent.shutdown
165
- end
166
-
167
- @installed_at_exit = true
168
- end
169
- end
25
+ if use_prepend?
26
+ prepend_instrument ::Rake::Task, NewRelic::Agent::Instrumentation::Rake::Prepend
27
+ else
28
+ chain_instrument NewRelic::Agent::Instrumentation::Rake::Chain
170
29
  end
171
30
  end
172
31
  end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Rake
7
+ module Chain
8
+ def self.instrument!
9
+ ::Rake::Task.class_eval do
10
+ include NewRelic::Agent::Instrumentation::Rake::Tracer
11
+ alias_method :invoke_without_newrelic, :invoke
12
+
13
+ def invoke(*args)
14
+ invoke_with_newrelic_tracing(*args) { invoke_without_newrelic(*args) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
23
+
24
+
25
+
@@ -0,0 +1,144 @@
1
+ # encoding: utf-8
2
+ # This file is distributed under New Relic's license terms.
3
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
4
+ # frozen_string_literal: true
5
+
6
+ module NewRelic
7
+ module Agent
8
+ module Instrumentation
9
+ module Rake
10
+ module Tracer
11
+ def invoke_with_newrelic_tracing(*args)
12
+ unless NewRelic::Agent::Instrumentation::Rake.should_trace? name
13
+ return yield
14
+ end
15
+
16
+ begin
17
+ timeout = NewRelic::Agent.config[:'rake.connect_timeout']
18
+ NewRelic::Agent.instance.wait_on_connect(timeout)
19
+ rescue => e
20
+ NewRelic::Agent.logger.error("Exception in wait_on_connect", e)
21
+ return yield
22
+ end
23
+
24
+ NewRelic::Agent::Instrumentation::Rake.before_invoke_transaction(self)
25
+
26
+ NewRelic::Agent::Tracer.in_transaction(name: "OtherTransaction/Rake/invoke/#{name}", category: :rake) do
27
+ NewRelic::Agent::Instrumentation::Rake.record_attributes(args, self)
28
+ yield
29
+ end
30
+ end
31
+ end
32
+
33
+ module_function
34
+
35
+ def should_install?
36
+ safe_from_third_party_gem?
37
+ end
38
+
39
+ def safe_from_third_party_gem?
40
+ return true unless NewRelic::LanguageSupport.bundled_gem?("newrelic-rake")
41
+ ::NewRelic::Agent.logger.info("Not installing New Relic supported Rake instrumentation because the third party newrelic-rake gem is present")
42
+ false
43
+ end
44
+
45
+ def should_trace?(name)
46
+ NewRelic::Agent.config[:'rake.tasks'].any? do |regex|
47
+ regex.match(name)
48
+ end
49
+ end
50
+
51
+ def instrument_execute_on_prereqs(task)
52
+ task.prerequisite_tasks.each do |child_task|
53
+ instrument_execute(child_task)
54
+ end
55
+ end
56
+
57
+ def instrument_execute(task)
58
+ return if task.instance_variable_get(:@__newrelic_instrumented_execute)
59
+
60
+ task.instance_variable_set(:@__newrelic_instrumented_execute, true)
61
+ task.instance_eval do
62
+ def execute(*args, &block)
63
+ NewRelic::Agent::MethodTracer.trace_execution_scoped("Rake/execute/#{self.name}") do
64
+ super
65
+ end
66
+ end
67
+ end
68
+
69
+ instrument_execute_on_prereqs(task)
70
+ end
71
+
72
+ def instrument_invoke_prerequisites_concurrently(task)
73
+ task.instance_eval do
74
+ def invoke_prerequisites_concurrently(*_)
75
+ NewRelic::Agent::MethodTracer.trace_execution_scoped("Rake/execute/multitask") do
76
+ prereqs = self.prerequisite_tasks.map(&:name).join(", ")
77
+ if txn = ::NewRelic::Agent::Tracer.current_transaction
78
+ txn.current_segment.params[:statement] = NewRelic::Agent::Database.truncate_query("Couldn't trace concurrent prereq tasks: #{prereqs}")
79
+ end
80
+ super
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def before_invoke_transaction(task)
87
+ ensure_at_exit
88
+
89
+ # We can't represent overlapping operations yet, so if multitask just
90
+ # make one node and annotate with prereq task names
91
+ if task.application.options.always_multitask
92
+ instrument_invoke_prerequisites_concurrently(task)
93
+ else
94
+ instrument_execute_on_prereqs(task)
95
+ end
96
+ rescue => e
97
+ NewRelic::Agent.logger.error("Error during Rake task invoke", e)
98
+ end
99
+
100
+ def record_attributes(args, task)
101
+ command_line = task.application.top_level_tasks.join(" ")
102
+ NewRelic::Agent::Transaction.merge_untrusted_agent_attributes({ :command => command_line },
103
+ :'job.rake',
104
+ NewRelic::Agent::AttributeFilter::DST_NONE)
105
+ named_args = name_the_args(args, task.arg_names)
106
+ unless named_args.empty?
107
+ NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(named_args,
108
+ :'job.rake.args',
109
+ NewRelic::Agent::AttributeFilter::DST_NONE)
110
+ end
111
+ rescue => e
112
+ NewRelic::Agent.logger.error("Error during Rake task attribute recording.", e)
113
+ end
114
+
115
+ # Expects literal args passed to the task and array of task names
116
+ # If names are present without matching args, still sets them with nils
117
+ def name_the_args(args, names)
118
+ unfulfilled_names_length = names.length - args.length
119
+ if unfulfilled_names_length > 0
120
+ args.concat(Array.new(unfulfilled_names_length))
121
+ end
122
+
123
+ result = {}
124
+ args.zip(names).each_with_index do |(value, key), index|
125
+ result[key || index.to_s] = value
126
+ end
127
+ result
128
+ end
129
+
130
+ def ensure_at_exit
131
+ return if @installed_at_exit
132
+
133
+ at_exit do
134
+ # The agent's default at_exit might not default to installing, but
135
+ # if we are running an instrumented rake task, we always want it.
136
+ NewRelic::Agent.shutdown
137
+ end
138
+
139
+ @installed_at_exit = true
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end