newrelic_rpm 6.15.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
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