newrelic_rpm 6.13.0 → 7.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +231 -0
  4. data/CONTRIBUTING.md +13 -2
  5. data/README.md +2 -2
  6. data/lib/new_relic/agent.rb +5 -8
  7. data/lib/new_relic/agent/agent.rb +4 -5
  8. data/lib/new_relic/agent/audit_logger.rb +10 -0
  9. data/lib/new_relic/agent/autostart.rb +1 -2
  10. data/lib/new_relic/agent/configuration/default_source.rb +412 -220
  11. data/lib/new_relic/agent/configuration/manager.rb +2 -2
  12. data/lib/new_relic/agent/database_adapter.rb +33 -0
  13. data/lib/new_relic/agent/datastores/redis.rb +0 -4
  14. data/lib/new_relic/agent/distributed_tracing.rb +0 -66
  15. data/lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb +2 -2
  16. data/lib/new_relic/agent/http_clients/uri_util.rb +1 -1
  17. data/lib/new_relic/agent/instrumentation/action_view_subscriber.rb +4 -5
  18. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +21 -68
  19. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +0 -16
  20. data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +23 -57
  21. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -3
  22. data/lib/new_relic/agent/instrumentation/acts_as_solr.rb +0 -15
  23. data/lib/new_relic/agent/instrumentation/bunny.rb +10 -196
  24. data/lib/new_relic/agent/instrumentation/bunny/chain.rb +45 -0
  25. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +152 -0
  26. data/lib/new_relic/agent/instrumentation/bunny/prepend.rb +35 -0
  27. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +1 -0
  28. data/lib/new_relic/agent/instrumentation/curb.rb +9 -259
  29. data/lib/new_relic/agent/instrumentation/curb/chain.rb +93 -0
  30. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +222 -0
  31. data/lib/new_relic/agent/instrumentation/curb/prepend.rb +63 -0
  32. data/lib/new_relic/agent/instrumentation/delayed_job/chain.rb +38 -0
  33. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +53 -0
  34. data/lib/new_relic/agent/instrumentation/delayed_job/prepend.rb +34 -0
  35. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +8 -84
  36. data/lib/new_relic/agent/instrumentation/excon.rb +2 -1
  37. data/lib/new_relic/agent/instrumentation/grape.rb +13 -113
  38. data/lib/new_relic/agent/instrumentation/grape/chain.rb +25 -0
  39. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +100 -0
  40. data/lib/new_relic/agent/instrumentation/grape/prepend.rb +17 -0
  41. data/lib/new_relic/agent/instrumentation/httpclient.rb +8 -30
  42. data/lib/new_relic/agent/instrumentation/httpclient/chain.rb +25 -0
  43. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +38 -0
  44. data/lib/new_relic/agent/instrumentation/httpclient/prepend.rb +17 -0
  45. data/lib/new_relic/agent/instrumentation/httprb.rb +29 -0
  46. data/lib/new_relic/agent/instrumentation/httprb/chain.rb +22 -0
  47. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +30 -0
  48. data/lib/new_relic/agent/instrumentation/httprb/prepend.rb +15 -0
  49. data/lib/new_relic/agent/instrumentation/memcache.rb +54 -69
  50. data/lib/new_relic/agent/instrumentation/memcache/chain.rb +16 -0
  51. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +38 -121
  52. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +56 -0
  53. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +88 -0
  54. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +88 -0
  55. data/lib/new_relic/agent/instrumentation/middleware_proxy.rb +4 -10
  56. data/lib/new_relic/agent/instrumentation/mongo.rb +7 -0
  57. data/lib/new_relic/agent/instrumentation/net_http.rb +44 -0
  58. data/lib/new_relic/agent/instrumentation/net_http/chain.rb +25 -0
  59. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +40 -0
  60. data/lib/new_relic/agent/instrumentation/net_http/prepend.rb +21 -0
  61. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +1 -1
  62. data/lib/new_relic/agent/instrumentation/padrino.rb +18 -75
  63. data/lib/new_relic/agent/instrumentation/padrino/chain.rb +34 -0
  64. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +27 -0
  65. data/lib/new_relic/agent/instrumentation/padrino/prepend.rb +20 -0
  66. data/lib/new_relic/agent/instrumentation/rack.rb +29 -160
  67. data/lib/new_relic/agent/instrumentation/rack/chain.rb +57 -0
  68. data/lib/new_relic/agent/instrumentation/rack/helpers.rb +32 -0
  69. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +73 -0
  70. data/lib/new_relic/agent/instrumentation/rack/prepend.rb +36 -0
  71. data/lib/new_relic/agent/instrumentation/rake.rb +13 -188
  72. data/lib/new_relic/agent/instrumentation/rake/chain.rb +25 -0
  73. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +144 -0
  74. data/lib/new_relic/agent/instrumentation/rake/prepend.rb +14 -0
  75. data/lib/new_relic/agent/instrumentation/redis.rb +12 -186
  76. data/lib/new_relic/agent/instrumentation/redis/chain.rb +34 -0
  77. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +65 -0
  78. data/lib/new_relic/agent/instrumentation/redis/prepend.rb +24 -0
  79. data/lib/new_relic/agent/instrumentation/resque.rb +21 -32
  80. data/lib/new_relic/agent/instrumentation/resque/chain.rb +22 -0
  81. data/lib/new_relic/agent/instrumentation/resque/helper.rb +19 -0
  82. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +35 -0
  83. data/lib/new_relic/agent/instrumentation/resque/prepend.rb +16 -0
  84. data/lib/new_relic/agent/instrumentation/sidekiq.rb +1 -1
  85. data/lib/new_relic/agent/instrumentation/sinatra.rb +20 -198
  86. data/lib/new_relic/agent/instrumentation/sinatra/chain.rb +55 -0
  87. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +29 -34
  88. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +124 -0
  89. data/lib/new_relic/agent/instrumentation/sinatra/prepend.rb +33 -0
  90. data/lib/new_relic/agent/instrumentation/typhoeus.rb +10 -89
  91. data/lib/new_relic/agent/instrumentation/typhoeus/chain.rb +22 -0
  92. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +82 -0
  93. data/lib/new_relic/agent/instrumentation/typhoeus/prepend.rb +14 -0
  94. data/lib/new_relic/agent/javascript_instrumentor.rb +12 -7
  95. data/lib/new_relic/agent/method_tracer.rb +6 -16
  96. data/lib/new_relic/agent/new_relic_service.rb +16 -13
  97. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  98. data/lib/new_relic/agent/span_event_primitive.rb +10 -8
  99. data/lib/new_relic/agent/sql_sampler.rb +3 -3
  100. data/lib/new_relic/agent/stats_engine/gc_profiler.rb +1 -1
  101. data/lib/new_relic/agent/transaction.rb +1 -4
  102. data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
  103. data/lib/new_relic/agent/transaction/distributed_tracer.rb +12 -6
  104. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -0
  105. data/lib/new_relic/agent/vm/mri_vm.rb +6 -4
  106. data/lib/new_relic/cli/commands/deployments.rb +0 -1
  107. data/lib/new_relic/constants.rb +4 -0
  108. data/lib/new_relic/control/frameworks/rails.rb +11 -9
  109. data/lib/new_relic/control/instance_methods.rb +1 -0
  110. data/lib/new_relic/dependency_detection.rb +119 -9
  111. data/lib/new_relic/environment_report.rb +1 -7
  112. data/lib/new_relic/noticed_error.rb +1 -5
  113. data/lib/new_relic/supportability_helper.rb +3 -2
  114. data/lib/new_relic/version.rb +2 -2
  115. data/lib/tasks/config.html.erb +14 -25
  116. data/lib/tasks/config.rake +8 -7
  117. data/newrelic_rpm.gemspec +2 -2
  118. data/test/agent_helper.rb +1 -0
  119. metadata +55 -32
  120. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -31
  121. data/.github/ISSUE_TEMPLATE/config.yml +0 -5
  122. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -24
  123. data/.github/actions/annotate/README.md +0 -79
  124. data/.github/actions/annotate/action.yml +0 -6
  125. data/.github/actions/annotate/dist/index.js +0 -433
  126. data/.github/actions/annotate/index.js +0 -25
  127. data/.github/actions/annotate/package-lock.json +0 -172
  128. data/.github/actions/annotate/package.json +0 -30
  129. data/.github/actions/annotate/pre-commit +0 -5
  130. data/.github/actions/build-ruby/README.md +0 -79
  131. data/.github/actions/build-ruby/action.yml +0 -15
  132. data/.github/actions/build-ruby/dist/index.js +0 -52683
  133. data/.github/actions/build-ruby/index.js +0 -514
  134. data/.github/actions/build-ruby/package-lock.json +0 -581
  135. data/.github/actions/build-ruby/package.json +0 -32
  136. data/.github/actions/build-ruby/pre-commit +0 -5
  137. data/.github/pull_request_template.md +0 -16
  138. data/.github/workflows/ci.yml +0 -212
  139. data/.github/workflows/pr_review_checklist.yml +0 -22
  140. data/.github/workflows/release.yml +0 -78
  141. data/.github/workflows/scripts/rubygems-authenticate.py +0 -13
  142. data/.github/workflows/scripts/rubygems-publish.rb +0 -32
  143. data/.github/workflows/snyk.yml +0 -27
  144. data/.github/workflows/stale.yml +0 -21
  145. data/cert/cacert.pem +0 -1177
  146. data/lib/new_relic/agent/instrumentation/http.rb +0 -49
  147. data/lib/new_relic/agent/instrumentation/net.rb +0 -87
@@ -0,0 +1,14 @@
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 Typhoeus
7
+ module Prepend
8
+ include NewRelic::Agent::Instrumentation::Typhoeus
9
+ def run(*args)
10
+ with_tracing { super }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -83,39 +83,44 @@ module NewRelic
83
83
  value.nil? || value.empty?
84
84
  end
85
85
 
86
- def browser_timing_header #THREAD_LOCAL_ACCESS
86
+ def browser_timing_header(nonce=nil) #THREAD_LOCAL_ACCESS
87
87
  return '' unless js_enabled_and_ready? # fast exit
88
88
 
89
89
  state = NewRelic::Agent::Tracer.state
90
90
 
91
91
  return '' unless insert_js?(state) # slower exit
92
92
 
93
- bt_config = browser_timing_config(state)
93
+ bt_config = browser_timing_config(state, nonce)
94
94
  return '' if bt_config.empty?
95
95
 
96
- bt_config + browser_timing_loader
96
+ bt_config + browser_timing_loader(nonce)
97
97
  rescue => e
98
98
  ::NewRelic::Agent.logger.debug "Failure during RUM browser_timing_header construction", e
99
99
  ''
100
100
  end
101
101
 
102
- def browser_timing_loader
103
- html_safe_if_needed("\n<script>#{Agent.config[:js_agent_loader]}</script>")
102
+ def browser_timing_loader(nonce=nil)
103
+ html_safe_if_needed("\n<script type=\"text/javascript\"#{create_nonce(nonce)}>#{Agent.config[:js_agent_loader]}</script>")
104
104
  end
105
105
 
106
- def browser_timing_config(state)
106
+ def browser_timing_config(state, nonce=nil)
107
107
  txn = state.current_transaction
108
108
  return '' if txn.nil?
109
109
 
110
110
  txn.freeze_name_and_execute_if_not_ignored do
111
111
  data = data_for_js_agent(txn)
112
112
  json = ::JSON.dump(data)
113
- return html_safe_if_needed("\n<script>window.NREUM||(NREUM={});NREUM.info=#{json}</script>")
113
+ return html_safe_if_needed("\n<script type=\"text/javascript\"#{create_nonce(nonce)}>window.NREUM||(NREUM={});NREUM.info=#{json}</script>")
114
114
  end
115
115
 
116
116
  ''
117
117
  end
118
118
 
119
+ def create_nonce(nonce=nil)
120
+ return '' unless nonce
121
+ " nonce=\"#{nonce.to_s}\""
122
+ end
123
+
119
124
  BEACON_KEY = "beacon".freeze
120
125
  ERROR_BEACON_KEY = "errorBeacon".freeze
121
126
  LICENSE_KEY_KEY = "licenseKey".freeze
@@ -173,30 +173,20 @@ module NewRelic
173
173
  # instrumentation into effectively one method call overhead
174
174
  # when the agent is disabled
175
175
  def assemble_code_header(method_name, metric_name_code, options)
176
- header = "return #{_untraced_method_name(method_name, metric_name_code)}(*args, &block) unless NewRelic::Agent.tl_is_execution_traced?\n"
176
+ header = "return #{_untraced_method_name(method_name, metric_name_code)}(#{ARGS_FOR_RUBY_VERSION}) unless NewRelic::Agent.tl_is_execution_traced?\n"
177
177
  header += options[:code_header].to_s
178
178
  header
179
179
  end
180
180
 
181
- # Positional and Keyword arguments are separated beginning with Ruby 2.7
182
- def arguments_for_ruby_version
183
- if RUBY_VERSION < "2.7.0"
184
- "(*args, &block)"
185
- else
186
- "(*args, **kwargs, &block)"
187
- end
188
- end
189
-
190
181
  # returns an eval-able string that contains the traced
191
182
  # method code used if the agent is not creating a scope for
192
183
  # use in scoped metrics.
193
184
  def method_without_push_scope(method_name, metric_name_code, options)
194
- arguments = arguments_for_ruby_version
195
- "def #{_traced_method_name(method_name, metric_name_code)}#{arguments}
185
+ "def #{_traced_method_name(method_name, metric_name_code)}(#{ARGS_FOR_RUBY_VERSION})
196
186
  #{assemble_code_header(method_name, metric_name_code, options)}
197
187
  t0 = Time.now
198
188
  begin
199
- #{_untraced_method_name(method_name, metric_name_code)}#{arguments}\n
189
+ #{_untraced_method_name(method_name, metric_name_code)}(#{ARGS_FOR_RUBY_VERSION})\n
200
190
  ensure
201
191
  duration = (Time.now - t0).to_f
202
192
  NewRelic::Agent.record_metric(\"#{metric_name_code}\", duration)
@@ -208,12 +198,11 @@ module NewRelic
208
198
  # returns an eval-able string that contains the tracing code
209
199
  # for a fully traced metric including scoping
210
200
  def method_with_push_scope(method_name, metric_name_code, options)
211
- arguments = arguments_for_ruby_version
212
- "def #{_traced_method_name(method_name, metric_name_code)}#{arguments}
201
+ "def #{_traced_method_name(method_name, metric_name_code)}(#{ARGS_FOR_RUBY_VERSION})
213
202
  #{options[:code_header]}
214
203
  result = ::NewRelic::Agent::MethodTracerHelpers.trace_execution_scoped(\"#{metric_name_code}\",
215
204
  :metric => #{options[:metric]}) do
216
- #{_untraced_method_name(method_name, metric_name_code)}#{arguments}
205
+ #{_untraced_method_name(method_name, metric_name_code)}(#{ARGS_FOR_RUBY_VERSION})
217
206
  end
218
207
  #{options[:code_footer]}
219
208
  result
@@ -337,6 +326,7 @@ module NewRelic
337
326
  class_eval traced_method, __FILE__, __LINE__
338
327
  alias_method _untraced_method_name(method_name, metric_name_code), method_name
339
328
  alias_method method_name, _traced_method_name(method_name, metric_name_code)
329
+ ruby2_keywords(_traced_method_name(method_name, metric_name_code)) if respond_to?(:ruby2_keywords, true)
340
330
  send visibility, method_name
341
331
  send visibility, _traced_method_name(method_name, metric_name_code)
342
332
  ::NewRelic::Agent.logger.debug("Traced method: class = #{derived_class_name},"+
@@ -41,7 +41,6 @@ module NewRelic
41
41
  @configured_collector = collector
42
42
  @request_timeout = Agent.config[:timeout]
43
43
  @ssl_cert_store = nil
44
- @use_bundled_certs = false
45
44
  @in_session = nil
46
45
  @agent_id = nil
47
46
  @shared_tcp_connection = nil
@@ -299,7 +298,7 @@ module NewRelic
299
298
  end
300
299
 
301
300
  def set_cert_store(conn)
302
- if @use_bundled_certs || NewRelic::Agent.config[:ca_bundle_path]
301
+ if NewRelic::Agent.config[:ca_bundle_path]
303
302
  conn.cert_store = ssl_cert_store
304
303
  else
305
304
  ::NewRelic::Agent.logger.debug("Using default security certificates")
@@ -348,13 +347,8 @@ module NewRelic
348
347
  start_connection(conn)
349
348
  conn
350
349
  rescue Timeout::Error
351
- if @use_bundled_certs == false
352
- ::NewRelic::Agent.logger.info("Unable to connect. Falling back to bundled security certificates")
353
- @use_bundled_certs = true
354
- retry
355
- else
356
- raise
357
- end
350
+ ::NewRelic::Agent.logger.info ("Timeout while attempting to connect. You may need to install system-level CA Certificates, as the ruby agent no longer includes these.")
351
+ raise
358
352
  end
359
353
 
360
354
  # The path to the certificate file used to verify the SSL
@@ -363,9 +357,6 @@ module NewRelic
363
357
  if path_override = NewRelic::Agent.config[:ca_bundle_path]
364
358
  NewRelic::Agent.logger.warn("Couldn't find CA bundle from configured ca_bundle_path: #{path_override}") unless File.exist? path_override
365
359
  path_override
366
- else
367
- ::NewRelic::Agent.increment_metric("Supportability/Ruby/Certificate/BundleRequired")
368
- File.expand_path(File.join(control.newrelic_root, 'cert', 'cacert.pem'))
369
360
  end
370
361
  end
371
362
 
@@ -435,7 +426,18 @@ module NewRelic
435
426
  size = data.size
436
427
 
437
428
  # Preconnect needs to always use the configured collector host, not the redirect host
438
- endpoint_specific_collector = (method == :preconnect) ? @configured_collector : @collector
429
+ # endpoint_specific_collector = (method == :preconnect) ? @configured_collector : @collector
430
+
431
+ # This is a temporary workaround due to errors occurring on the staging collector. The prod collector does not have the same issue.
432
+ # The staging collector does not respond correctly when using the configured collector host for preconnect, so must use the redirect host
433
+ # Once this issue is resolved on the staging collector, use the original line that is commented out above.
434
+ endpoint_specific_collector = if method == :preconnect && (@configured_collector && @configured_collector.name != 'staging-collector.newrelic.com')
435
+ ::NewRelic::Agent.logger.debug "Using configured collector for preconnect: #{@configured_collector}"
436
+ @configured_collector
437
+ else
438
+ ::NewRelic::Agent.logger.debug "Using redirect host for collector: #{@collector}"
439
+ @collector
440
+ end
439
441
 
440
442
  uri = remote_method_uri(method)
441
443
  full_uri = "#{endpoint_specific_collector}#{uri}"
@@ -526,6 +528,7 @@ module NewRelic
526
528
  else
527
529
  request = Net::HTTP::Post.new(opts[:uri], headers)
528
530
  end
531
+ @audit_logger.log_request_headers(opts[:uri], headers)
529
532
  request['user-agent'] = user_agent
530
533
  request.content_type = "application/octet-stream"
531
534
  request.body = opts[:data]
@@ -29,7 +29,7 @@ module NewRelic
29
29
  end
30
30
  elsif platform =~ /darwin9/ # 10.5
31
31
  @sampler = ShellPS.new("ps -o rsz")
32
- elsif platform =~ /darwin1\d+/ # >= 10.6
32
+ elsif platform =~ /darwin(1|2)\d+/ # >= 10.6
33
33
  @sampler = ShellPS.new("ps -o rss")
34
34
  elsif platform =~ /freebsd/
35
35
  @sampler = ShellPS.new("ps -o rss")
@@ -165,16 +165,18 @@ module NewRelic
165
165
  end
166
166
  end
167
167
 
168
+ def merge_and_freeze_attributes agent_attributes, error_attributes
169
+ return agent_attributes.freeze unless error_attributes
170
+ return error_attributes.freeze if agent_attributes.equal?(NewRelic::EMPTY_HASH)
171
+ agent_attributes.merge!(error_attributes).freeze
172
+ end
173
+
168
174
  def agent_attributes segment
169
- attributes = segment.attributes
170
- agent_attributes = attributes.agent_attributes_for(NewRelic::Agent::AttributeFilter::DST_SPAN_EVENTS)
175
+ agent_attributes = segment.attributes
176
+ .agent_attributes_for(NewRelic::Agent::AttributeFilter::DST_SPAN_EVENTS)
171
177
  error_attributes = error_attributes(segment)
172
- if agent_attributes || error_attributes
173
- agent_attributes.merge!(error_attributes) if error_attributes
174
- agent_attributes.freeze
175
- else
176
- NewRelic::EMPTY_HASH
177
- end
178
+ return NewRelic::EMPTY_HASH unless agent_attributes || error_attributes
179
+ merge_and_freeze_attributes(agent_attributes, error_attributes)
178
180
  end
179
181
 
180
182
  def parent_guid segment
@@ -65,7 +65,7 @@ module NewRelic
65
65
  end
66
66
 
67
67
  # This is called when we are done with the transaction.
68
- def on_finishing_transaction(state, name, time=Time.now)
68
+ def on_finishing_transaction(state, name)
69
69
  return unless enabled?
70
70
 
71
71
  data = state.sql_sampler_transaction_data
@@ -359,10 +359,10 @@ module NewRelic
359
359
  # need to hash the same way in every process, to be able to aggregate slow SQL traces
360
360
  def consistent_hash(string)
361
361
  if NewRelic::Agent.config[:'slow_sql.use_longer_sql_id']
362
- Digest::MD5.hexdigest(string).hex.modulo(2**63-1)
362
+ Digest::SHA1.hexdigest(string).hex.modulo(2**63-1)
363
363
  else
364
364
  # from when sql_id needed to fit in an INT(11)
365
- Digest::MD5.hexdigest(string).hex.modulo(2**31-1)
365
+ Digest::SHA1.hexdigest(string).hex.modulo(2**31-1)
366
366
  end
367
367
  end
368
368
  end
@@ -99,7 +99,7 @@ module NewRelic
99
99
 
100
100
  # When using GC::Profiler, it's important to periodically call
101
101
  # GC::Profiler.clear in order to avoid unbounded growth in the number
102
- # of GC recordds that are stored. However, we actually do this
102
+ # of GC records that are stored. However, we actually do this
103
103
  # internally within MonotonicGCProfiler on calls to #total_time_s,
104
104
  # so the reset here is a no-op.
105
105
  def reset; end
@@ -464,7 +464,7 @@ module NewRelic
464
464
  @ignore_apdex = options[:ignore_apdex] if options.key? :ignore_apdex
465
465
  @ignore_enduser = options[:ignore_enduser] if options.key? :ignore_enduser
466
466
 
467
- nest_initial_segment if nesting_max_depth == 1
467
+ nest_initial_segment if segments.length == 1
468
468
  nested_name = self.class.nested_transaction_name options[:transaction_name]
469
469
  segment = create_segment nested_name
470
470
  set_default_transaction_name(options[:transaction_name], category)
@@ -571,9 +571,6 @@ module NewRelic
571
571
  AttributeFilter::DST_ERROR_COLLECTOR
572
572
 
573
573
  if http_response_code
574
- add_agent_attribute(:httpResponseCode, http_response_code.to_s, default_destinations)
575
- # Sending status code as an int with http.statusCode key is correct
576
- # The above attribute is deprecated and should be removed in agent version 7.0.0
577
574
  add_agent_attribute(:'http.statusCode', http_response_code, default_destinations)
578
575
  end
579
576
 
@@ -167,7 +167,7 @@ module NewRelic
167
167
 
168
168
  if finished?
169
169
  transaction.async = true
170
- parent.descendant_complete self, segment
170
+ parent.descendant_complete(self, segment) if parent
171
171
  end
172
172
  end
173
173
 
@@ -68,13 +68,19 @@ module NewRelic
68
68
  payload
69
69
  end
70
70
 
71
- def insert_headers request
72
- insert_trace_context_header request
73
- insert_distributed_trace_header request
74
- insert_cross_app_header request
71
+ def log_request_headers headers, direction = "OUTGOING"
72
+ NewRelic::Agent.logger.debug "#{direction} REQUEST HEADERS: #{headers}"
73
+ end
74
+
75
+ def insert_headers headers
76
+ insert_trace_context_header headers
77
+ insert_distributed_trace_header headers
78
+ insert_cross_app_header headers
79
+ log_request_headers headers
75
80
  end
76
81
 
77
82
  def consume_message_headers headers, tracer_state, transport_type
83
+ log_request_headers headers, "INCOMING"
78
84
  consume_message_distributed_tracing_headers headers, transport_type
79
85
  consume_message_cross_app_tracing_headers headers, tracer_state
80
86
  consume_message_synthetics_headers headers
@@ -90,11 +96,11 @@ module NewRelic
90
96
  end
91
97
  end
92
98
 
93
- def insert_distributed_trace_header request
99
+ def insert_distributed_trace_header headers
94
100
  return unless Agent.config[:'distributed_tracing.enabled']
95
101
  return if Agent.config[:'exclude_newrelic_header']
96
102
  payload = create_distributed_trace_payload
97
- request[NewRelic::NEWRELIC_KEY] = payload.http_safe if payload
103
+ headers[NewRelic::NEWRELIC_KEY] = payload.http_safe if payload
98
104
  end
99
105
 
100
106
  def insert_cat_headers headers
@@ -93,6 +93,7 @@ module NewRelic
93
93
  if headers && transaction && action == :produce && record_metrics?
94
94
  transaction.distributed_tracer.insert_distributed_trace_header headers
95
95
  transaction.distributed_tracer.insert_cat_headers headers
96
+ transaction.distributed_tracer.log_request_headers headers
96
97
  end
97
98
  rescue => e
98
99
  NewRelic::Agent.logger.error "Error during message header processing", e
@@ -45,9 +45,11 @@ module NewRelic
45
45
 
46
46
  def gather_ruby_vm_stats(snap)
47
47
  if supports?(:method_cache_invalidations)
48
- vm_stats = RubyVM.stat
49
- snap.method_cache_invalidations = vm_stats[:global_method_state]
50
- snap.constant_cache_invalidations = vm_stats[:global_constant_state]
48
+ snap.method_cache_invalidations = RubyVM.stat[:global_method_state]
49
+ end
50
+
51
+ if supports?(:constant_cache_invalidations)
52
+ snap.constant_cache_invalidations = RubyVM.stat[:global_constant_state]
51
53
  end
52
54
  end
53
55
 
@@ -66,7 +68,7 @@ module NewRelic
66
68
  when :minor_gc_count
67
69
  RUBY_VERSION >= '2.1.0'
68
70
  when :method_cache_invalidations
69
- RUBY_VERSION >= '2.1.0'
71
+ RUBY_VERSION >= '2.1.0' && RUBY_VERSION < '3.0.0'
70
72
  when :constant_cache_invalidations
71
73
  RUBY_VERSION >= '2.1.0'
72
74
  else
@@ -7,7 +7,6 @@
7
7
 
8
8
  require 'yaml'
9
9
  require 'net/http'
10
- require 'rexml/document'
11
10
  require 'new_relic/agent/hostname'
12
11
 
13
12
  # We need to use the Control object but we don't want to load
@@ -28,6 +28,10 @@ module NewRelic
28
28
  TRACEPARENT_KEY = "traceparent"
29
29
  TRACESTATE_KEY = "tracestate"
30
30
 
31
+ # Right now, old and new Rubies are the same. Intention is to use "(...)" for
32
+ # Ruby 2.7+ for argument delegation, but doing so is a breaking API change.
33
+ ARGS_FOR_RUBY_VERSION = RUBY_VERSION < "2.7.0" ? "*args, &block" : "*args, &block"
34
+
31
35
  HTTP_TRACEPARENT_KEY = "HTTP_#{TRACEPARENT_KEY.upcase}"
32
36
  HTTP_TRACESTATE_KEY = "HTTP_#{TRACESTATE_KEY.upcase}"
33
37
  HTTP_NEWRELIC_KEY = "HTTP_#{NEWRELIC_KEY.upcase}"
@@ -82,15 +82,17 @@ module NewRelic
82
82
  end
83
83
 
84
84
  def install_browser_monitoring(config)
85
- return if defined?(@browser_monitoring_installed) && @browser_monitoring_installed
86
- @browser_monitoring_installed = true
87
- return if config.nil? || !config.respond_to?(:middleware) || !Agent.config[:'browser_monitoring.auto_instrument']
88
- begin
89
- require 'new_relic/rack/browser_monitoring'
90
- config.middleware.use NewRelic::Rack::BrowserMonitoring
91
- ::NewRelic::Agent.logger.debug("Installed New Relic Browser Monitoring middleware")
92
- rescue => e
93
- ::NewRelic::Agent.logger.warn("Error installing New Relic Browser Monitoring middleware", e)
85
+ @install_lock.synchronize do
86
+ return if defined?(@browser_monitoring_installed) && @browser_monitoring_installed
87
+ @browser_monitoring_installed = true
88
+ return if config.nil? || !config.respond_to?(:middleware) || !Agent.config[:'browser_monitoring.auto_instrument']
89
+ begin
90
+ require 'new_relic/rack/browser_monitoring'
91
+ config.middleware.use NewRelic::Rack::BrowserMonitoring
92
+ ::NewRelic::Agent.logger.debug("Installed New Relic Browser Monitoring middleware")
93
+ rescue => e
94
+ ::NewRelic::Agent.logger.warn("Error installing New Relic Browser Monitoring middleware", e)
95
+ end
94
96
  end
95
97
  end
96
98
 
@@ -165,6 +165,7 @@ module NewRelic
165
165
  protected
166
166
 
167
167
  def initialize(local_env, config_file_override=nil)
168
+ @install_lock = Mutex.new
168
169
  @local_env = local_env
169
170
  @started_in_env = nil
170
171
 
@@ -21,6 +21,7 @@ module DependencyDetection
21
21
  end
22
22
 
23
23
  @items << item
24
+ return item
24
25
  end
25
26
 
26
27
  def detect!
@@ -51,28 +52,74 @@ module DependencyDetection
51
52
  class Dependent
52
53
  attr_reader :executed
53
54
  attr_accessor :name
55
+ attr_writer :config_name
56
+ attr_reader :dependencies
57
+ attr_reader :prepend_conflicts
58
+
59
+
54
60
  def executed!
55
61
  @executed = true
56
62
  end
57
63
 
58
- attr_reader :dependencies
64
+ def config_name
65
+ @config_name || @name
66
+ end
59
67
 
60
68
  def initialize
61
69
  @dependencies = []
62
70
  @executes = []
71
+ @prepend_conflicts = []
63
72
  @name = nil
73
+ @config_name = nil
64
74
  end
65
75
 
66
76
  def dependencies_satisfied?
67
77
  !executed and check_dependencies
68
78
  end
69
79
 
80
+ def source_location_for klass, method_name
81
+ Object.instance_method(:method).bind(klass.allocate).call(method_name).source_location.to_s
82
+ end
83
+
84
+ # Extracts the instrumented library name from the instrumenting module's name
85
+ # Given "NewRelic::Agent::Instrumentation::NetHTTP::Prepend"
86
+ # Will extract "NetHTTP" which is in the 2nd to last spot
87
+ def extract_supportability_name instrumenting_module
88
+ instrumenting_module.to_s.split("::")[-2]
89
+ end
90
+
91
+ def log_and_instrument method, instrumenting_module, supportability_name
92
+ supportability_name ||= extract_supportability_name(instrumenting_module)
93
+ NewRelic::Agent.logger.info "Installing New Relic supported #{supportability_name} instrumentation using #{method}"
94
+ NewRelic::Agent.record_metric("Supportability/Instrumentation/#{supportability_name}/#{method}", 0.0)
95
+ yield
96
+ end
97
+
98
+ def prepend_instrument target_class, instrumenting_module, supportability_name=nil
99
+ log_and_instrument("Prepend", instrumenting_module, supportability_name) do
100
+ target_class.send :prepend, instrumenting_module
101
+ end
102
+ end
103
+
104
+ def chain_instrument instrumenting_module, supportability_name=nil
105
+ log_and_instrument("MethodChaining", instrumenting_module, supportability_name) do
106
+ instrumenting_module.instrument!
107
+ end
108
+ end
109
+
110
+ def chain_instrument_target target, instrumenting_module, supportability_name=nil
111
+ NewRelic::Agent.logger.info "Installing deferred #{target} instrumentation"
112
+ log_and_instrument("MethodChaining", instrumenting_module, supportability_name) do
113
+ instrumenting_module.instrument! target
114
+ end
115
+ end
116
+
70
117
  def execute
71
118
  @executes.each do |x|
72
119
  begin
73
120
  x.call
74
121
  rescue => err
75
- NewRelic::Agent.logger.error( "Error while installing #{self.name} instrumentation:", err )
122
+ NewRelic::Agent.logger.error "Error while installing #{self.name} instrumentation:", err
76
123
  break
77
124
  end
78
125
  end
@@ -98,24 +145,87 @@ module DependencyDetection
98
145
  end
99
146
 
100
147
  def allowed_by_config?
101
- # If we don't have a name, can't check config so allow it
102
- return true if self.name.nil?
148
+ !(disabled_configured? || deprecated_disabled_configured?)
149
+ end
150
+
151
+ # TODO: Remove in 8.0
152
+ # will only return true if a disabled key is found and is truthy
153
+ def deprecated_disabled_configured?
154
+ return false if self.name.nil?
103
155
 
104
156
  key = "disable_#{self.name}".to_sym
105
- if (::NewRelic::Agent.config[key] == true)
106
- ::NewRelic::Agent.logger.debug("Not installing #{self.name} instrumentation because of configuration #{key}")
107
- false
108
- else
109
- true
157
+ return false unless ::NewRelic::Agent.config[key] == true
158
+
159
+ ::NewRelic::Agent.logger.debug("Not installing #{self.name} instrumentation because of configuration #{key}")
160
+ ::NewRelic::Agent.logger.debug \
161
+ "[DEPRECATED] configuration #{key} for #{self.name} will be removed in the next major release." \
162
+ " Use `#{config_key}` with one of `#{VALID_CONFIG_VALUES.map(&:to_s).inspect}`"
163
+
164
+ return true
165
+ end
166
+
167
+ def config_key
168
+ return nil if self.config_name.nil?
169
+ @config_key ||= "instrumentation.#{self.config_name}".to_sym
170
+ end
171
+
172
+ VALID_CONFIG_VALUES = [:auto, :disabled, :prepend, :chain]
173
+ AUTO_CONFIG_VALUE = VALID_CONFIG_VALUES[0]
174
+
175
+ VALID_CONFIG_VALUES.each do |value|
176
+ define_method "#{value}_configured?" do
177
+ value == config_value
110
178
  end
111
179
  end
112
180
 
181
+ # returns only a valid value for instrumentation configuration
182
+ # If user uses "enabled" it's converted to "auto"
183
+ def valid_config_value retrieved_value
184
+ VALID_CONFIG_VALUES.include?(retrieved_value) ? retrieved_value : AUTO_CONFIG_VALUE
185
+ end
186
+
187
+ # fetches and transform potentially invalid value given to one of the valid config values
188
+ # logs the resolved value during debug mode.
189
+ def fetch_config_value(key)
190
+ valid_value = valid_config_value(::NewRelic::Agent.config[key].to_s.to_sym)
191
+ ::NewRelic::Agent.logger.debug("Using #{valid_value} configuration value for #{self.name} to configure instrumentation")
192
+ return valid_value
193
+ end
194
+
195
+ def config_value
196
+ return AUTO_CONFIG_VALUE unless config_key
197
+ fetch_config_value(config_key)
198
+ end
199
+
113
200
  def named(new_name)
114
201
  self.name = new_name
115
202
  end
116
203
 
204
+ def configure_with(new_config_name)
205
+ self.config_name = new_config_name
206
+ end
207
+
117
208
  def executes &block
118
209
  @executes << block if block_given?
119
210
  end
211
+
212
+ def conflicts_with_prepend &block
213
+ @prepend_conflicts << block if block_given?
214
+ end
215
+
216
+ def use_prepend?
217
+ prepend_configured? || (auto_configured? && !prepend_conflicts?)
218
+ end
219
+
220
+ def prepend_conflicts?
221
+ @prepend_conflicts.any? do |conflict|
222
+ begin
223
+ conflict.call
224
+ rescue => err
225
+ NewRelic::Agent.logger.error( "Error while checking prepend conflicts #{self.name}:", err )
226
+ false # assumes no conflicts exist since `prepend` is preferred method of instrumenting
227
+ end
228
+ end
229
+ end
120
230
  end
121
231
  end