newrelic_rpm 9.5.0 → 9.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -7
  3. data/CONTRIBUTING.md +0 -7
  4. data/Rakefile +1 -1
  5. data/bin/newrelic +2 -9
  6. data/bin/newrelic_rpm +15 -0
  7. data/init.rb +2 -2
  8. data/lib/new_relic/agent/agent.rb +1 -1
  9. data/lib/new_relic/agent/agent_helpers/shutdown.rb +1 -1
  10. data/lib/new_relic/agent/agent_helpers/special_startup.rb +1 -1
  11. data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +2 -2
  12. data/lib/new_relic/agent/agent_helpers/startup.rb +2 -2
  13. data/lib/new_relic/agent/attribute_filter.rb +3 -3
  14. data/lib/new_relic/agent/configuration/default_source.rb +94 -35
  15. data/lib/new_relic/agent/configuration/manager.rb +8 -7
  16. data/lib/new_relic/agent/datastores/mongo/metric_translator.rb +1 -1
  17. data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +3 -3
  18. data/lib/new_relic/agent/event_loop.rb +1 -1
  19. data/lib/new_relic/agent/http_clients/abstract.rb +4 -0
  20. data/lib/new_relic/agent/http_clients/async_http_wrappers.rb +80 -0
  21. data/lib/new_relic/agent/http_clients/curb_wrappers.rb +1 -3
  22. data/lib/new_relic/agent/http_clients/ethon_wrappers.rb +109 -0
  23. data/lib/new_relic/agent/http_clients/excon_wrappers.rb +0 -3
  24. data/lib/new_relic/agent/http_clients/http_rb_wrappers.rb +1 -3
  25. data/lib/new_relic/agent/http_clients/httpclient_wrappers.rb +0 -3
  26. data/lib/new_relic/agent/http_clients/httpx_wrappers.rb +91 -0
  27. data/lib/new_relic/agent/http_clients/net_http_wrappers.rb +1 -4
  28. data/lib/new_relic/agent/http_clients/typhoeus_wrappers.rb +0 -3
  29. data/lib/new_relic/agent/instrumentation/active_merchant.rb +1 -1
  30. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -2
  31. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/chain.rb +69 -0
  32. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +13 -0
  33. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/prepend.rb +37 -0
  34. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +23 -0
  35. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +3 -1
  36. data/lib/new_relic/agent/instrumentation/async_http/chain.rb +23 -0
  37. data/lib/new_relic/agent/instrumentation/async_http/instrumentation.rb +37 -0
  38. data/lib/new_relic/agent/instrumentation/async_http/prepend.rb +15 -0
  39. data/lib/new_relic/agent/instrumentation/async_http.rb +26 -0
  40. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +1 -1
  41. data/lib/new_relic/agent/instrumentation/ethon/chain.rb +39 -0
  42. data/lib/new_relic/agent/instrumentation/ethon/instrumentation.rb +105 -0
  43. data/lib/new_relic/agent/instrumentation/ethon/prepend.rb +35 -0
  44. data/lib/new_relic/agent/instrumentation/ethon.rb +39 -0
  45. data/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb +1 -4
  46. data/lib/new_relic/agent/instrumentation/httpx/chain.rb +20 -0
  47. data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +51 -0
  48. data/lib/new_relic/agent/instrumentation/httpx/prepend.rb +15 -0
  49. data/lib/new_relic/agent/instrumentation/httpx.rb +27 -0
  50. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -3
  51. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +1 -1
  52. data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +1 -0
  53. data/lib/new_relic/agent/instrumentation/roda/ignorer.rb +45 -0
  54. data/lib/new_relic/agent/instrumentation/roda/instrumentation.rb +12 -0
  55. data/lib/new_relic/agent/instrumentation/roda/roda_transaction_namer.rb +1 -2
  56. data/lib/new_relic/agent/instrumentation/roda.rb +2 -0
  57. data/lib/new_relic/agent/instrumentation/sidekiq.rb +3 -1
  58. data/lib/new_relic/agent/instrumentation/sinatra/ignorer.rb +1 -1
  59. data/lib/new_relic/agent/instrumentation/sinatra/transaction_namer.rb +1 -3
  60. data/lib/new_relic/agent/instrumentation/sinatra.rb +1 -1
  61. data/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +1 -4
  62. data/lib/new_relic/agent/instrumentation/view_component/chain.rb +21 -0
  63. data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +38 -0
  64. data/lib/new_relic/agent/instrumentation/view_component/prepend.rb +13 -0
  65. data/lib/new_relic/agent/instrumentation/view_component.rb +26 -0
  66. data/lib/new_relic/agent/javascript_instrumentor.rb +0 -1
  67. data/lib/new_relic/agent/messaging.rb +2 -2
  68. data/lib/new_relic/agent/monitors/synthetics_monitor.rb +12 -1
  69. data/lib/new_relic/agent/new_relic_service/encoders.rb +2 -2
  70. data/lib/new_relic/agent/new_relic_service.rb +8 -6
  71. data/lib/new_relic/agent/obfuscator.rb +0 -2
  72. data/lib/new_relic/agent/pipe_channel_manager.rb +2 -2
  73. data/lib/new_relic/agent/rules_engine/segment_terms_rule.rb +1 -2
  74. data/lib/new_relic/agent/rules_engine.rb +1 -1
  75. data/lib/new_relic/agent/span_event_primitive.rb +16 -4
  76. data/lib/new_relic/agent/sql_sampler.rb +0 -1
  77. data/lib/new_relic/agent/system_info.rb +26 -0
  78. data/lib/new_relic/agent/tracer.rb +5 -6
  79. data/lib/new_relic/agent/transaction/abstract_segment.rb +3 -0
  80. data/lib/new_relic/agent/transaction/external_request_segment.rb +5 -2
  81. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -2
  82. data/lib/new_relic/agent/transaction/request_attributes.rb +1 -3
  83. data/lib/new_relic/agent/transaction/tracing.rb +11 -1
  84. data/lib/new_relic/agent/transaction.rb +25 -2
  85. data/lib/new_relic/agent/transaction_error_primitive.rb +16 -0
  86. data/lib/new_relic/agent/transaction_event_primitive.rb +19 -0
  87. data/lib/new_relic/agent/utilization/gcp.rb +1 -3
  88. data/lib/new_relic/agent/vm/{mri_vm.rb → c_ruby_vm.rb} +7 -15
  89. data/lib/new_relic/agent/vm.rb +2 -2
  90. data/lib/new_relic/agent/worker_loop.rb +1 -1
  91. data/lib/new_relic/agent.rb +11 -7
  92. data/lib/new_relic/base64.rb +25 -0
  93. data/lib/new_relic/cli/command.rb +6 -4
  94. data/lib/new_relic/constants.rb +5 -0
  95. data/lib/new_relic/control/frameworks/rails.rb +17 -5
  96. data/lib/new_relic/control/instrumentation.rb +1 -1
  97. data/lib/new_relic/language_support.rb +4 -0
  98. data/lib/new_relic/local_environment.rb +22 -13
  99. data/lib/new_relic/supportability_helper.rb +1 -1
  100. data/lib/new_relic/version.rb +1 -1
  101. data/lib/tasks/config.rake +1 -1
  102. data/lib/tasks/helpers/config.html.erb +6 -6
  103. data/lib/tasks/helpers/newrelicyml.rb +1 -1
  104. data/lib/tasks/instrumentation_generator/instrumentation.thor +3 -3
  105. data/lib/tasks/tests.rake +71 -0
  106. data/newrelic.yml +55 -35
  107. data/newrelic_rpm.gemspec +5 -4
  108. data/test/agent_helper.rb +14 -2
  109. metadata +32 -7
  110. data/bin/newrelic_cmd +0 -7
@@ -0,0 +1,105 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require 'new_relic/agent/http_clients/ethon_wrappers'
6
+
7
+ module NewRelic::Agent::Instrumentation
8
+ module Ethon
9
+ module NRShared
10
+ INSTRUMENTATION_NAME = 'Ethon'
11
+ NOTICEABLE_ERROR_CLASS = 'Ethon::Errors::EthonError'
12
+
13
+ def prep_easy(easy, parent = nil)
14
+ wrapped_request = NewRelic::Agent::HTTPClients::EthonHTTPRequest.new(easy)
15
+ segment = NewRelic::Agent::Tracer.start_external_request_segment(
16
+ library: wrapped_request.type,
17
+ uri: wrapped_request.uri,
18
+ procedure: wrapped_request.method,
19
+ parent: parent
20
+ )
21
+ segment.add_request_headers(wrapped_request)
22
+
23
+ callback = proc do
24
+ wrapped_response = NewRelic::Agent::HTTPClients::EthonHTTPResponse.new(easy)
25
+ segment.process_response_headers(wrapped_response)
26
+
27
+ if easy.return_code != :ok
28
+ e = NewRelic::Agent::NoticeableError.new(NOTICEABLE_ERROR_CLASS,
29
+ "return_code: >>#{easy.return_code}<<, response_code: >>#{easy.response_code}<<")
30
+ segment.notice_error(e)
31
+ end
32
+
33
+ ::NewRelic::Agent::Transaction::Segment.finish(segment)
34
+ end
35
+
36
+ easy.on_complete { callback.call }
37
+
38
+ segment
39
+ end
40
+
41
+ def wrap_with_tracing(segment, &block)
42
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
43
+
44
+ NewRelic::Agent::Tracer.capture_segment_error(segment) do
45
+ yield
46
+ end
47
+ ensure
48
+ NewRelic::Agent::Transaction::Segment.finish(segment)
49
+ end
50
+ end
51
+
52
+ module Easy
53
+ include NRShared
54
+
55
+ ACTION_INSTANCE_VAR = :@nr_action
56
+ HEADERS_INSTANCE_VAR = :@nr_headers
57
+
58
+ # `Ethon::Easy` doesn't expose the "action name" ('GET', 'POST', etc.)
59
+ # and Ethon's fabrication of HTTP classes uses
60
+ # `Ethon::Easy::Http::Custom` for non-standard actions. To be able to
61
+ # know the action name at `#perform` time, we set a new instance variable
62
+ # on the `Ethon::Easy` instance with the base name of the fabricated
63
+ # class, respecting the 'Custom' name where appropriate.
64
+ def fabricate_with_tracing(_url, action_name, _options)
65
+ fabbed = yield
66
+ instance_variable_set(ACTION_INSTANCE_VAR, NewRelic::Agent.base_name(fabbed.class.name).upcase)
67
+ fabbed
68
+ end
69
+
70
+ # `Ethon::Easy` uses `Ethon::Easy::Header` to set request headers on
71
+ # libcurl with `#headers=`. After they are set, they aren't easy to get
72
+ # at again except via FFI so set a new instance variable on the
73
+ # `Ethon::Easy` instance to store them in Ruby hash format.
74
+ def headers_equals_with_tracing(headers)
75
+ instance_variable_set(HEADERS_INSTANCE_VAR, headers)
76
+ yield
77
+ end
78
+
79
+ def perform_with_tracing(*args)
80
+ return unless NewRelic::Agent::Tracer.state.is_execution_traced?
81
+
82
+ segment = prep_easy(self)
83
+ wrap_with_tracing(segment) { yield }
84
+ end
85
+ end
86
+
87
+ module Multi
88
+ include NRShared
89
+
90
+ MULTI_SEGMENT_NAME = 'External/Multiple/Ethon::Multi/perform'
91
+
92
+ def perform_with_tracing(*args)
93
+ return unless NewRelic::Agent::Tracer.state.is_execution_traced?
94
+
95
+ segment = NewRelic::Agent::Tracer.start_segment(name: MULTI_SEGMENT_NAME)
96
+
97
+ wrap_with_tracing(segment) do
98
+ easy_handles.each { |easy| prep_easy(easy, segment) }
99
+
100
+ yield
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,35 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Ethon
7
+ module Easy
8
+ module Prepend
9
+ include NewRelic::Agent::Instrumentation::Ethon::Easy
10
+
11
+ def fabricate(url, action_name, options)
12
+ fabricate_with_tracing(url, action_name, options) { super }
13
+ end
14
+
15
+ def headers=(headers)
16
+ headers_equals_with_tracing(headers) { super }
17
+ end
18
+
19
+ def perform(*args)
20
+ perform_with_tracing(*args) { super }
21
+ end
22
+ end
23
+ end
24
+
25
+ module Multi
26
+ module Prepend
27
+ include NewRelic::Agent::Instrumentation::Ethon::Multi
28
+
29
+ def perform(*args)
30
+ perform_with_tracing(*args) { super }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'ethon/instrumentation'
6
+ require_relative 'ethon/chain'
7
+ require_relative 'ethon/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :ethon
11
+
12
+ # If Ethon is being used as a dependency of Typhoeus, allow the Typhoeus
13
+ # instrumentation to handle everything. Otherwise each external network call
14
+ # will confusingly result in "Ethon" segments duplicating the information
15
+ # already provided by "Typhoeus" segments.
16
+ depends_on do
17
+ !defined?(Typhoeus)
18
+ end
19
+
20
+ depends_on do
21
+ defined?(Ethon) && Gem::Version.new(Ethon::VERSION) >= Gem::Version.new('0.12.0')
22
+ end
23
+
24
+ executes do
25
+ NewRelic::Agent.logger.info('Installing ethon instrumentation')
26
+ end
27
+
28
+ executes do
29
+ if use_prepend?
30
+ # NOTE: by default prepend_instrument will go with the module name that
31
+ # precedes 'Prepend' (so 'Easy' and 'Multi'), but we want to use
32
+ # 'Ethon::Easy' and 'Ethon::Multi' so 3rd argument is supplied
33
+ prepend_instrument Ethon::Easy, NewRelic::Agent::Instrumentation::Ethon::Easy::Prepend, Ethon::Easy.name
34
+ prepend_instrument Ethon::Multi, NewRelic::Agent::Instrumentation::Ethon::Multi::Prepend, Ethon::Multi.name
35
+ else
36
+ chain_instrument NewRelic::Agent::Instrumentation::Ethon::Chain
37
+ end
38
+ end
39
+ end
@@ -14,10 +14,7 @@ module NewRelic::Agent::Instrumentation
14
14
  def add_thread_tracing(&block)
15
15
  return block if !NewRelic::Agent::Tracer.thread_tracing_enabled?
16
16
 
17
- NewRelic::Agent::Tracer.thread_block_with_current_transaction(
18
- segment_name: 'Ruby/Fiber',
19
- &block
20
- )
17
+ NewRelic::Agent::Tracer.thread_block_with_current_transaction(&block)
21
18
  end
22
19
  end
23
20
  end
@@ -0,0 +1,20 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module HTTPX
7
+ module Chain
8
+ def self.instrument!
9
+ ::HTTPX::Session.class_eval do
10
+ include NewRelic::Agent::Instrumentation::HTTPX
11
+
12
+ alias_method(:send_requests_without_tracing, :send_requests)
13
+ def send_requests(*requests)
14
+ send_requests_with_tracing(*requests) { send_requests_without_tracing(*requests) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,51 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require 'new_relic/agent/http_clients/httpx_wrappers'
6
+
7
+ module NewRelic::Agent::Instrumentation::HTTPX
8
+ INSTRUMENTATION_NAME = 'HTTPX'
9
+ NOTICEABLE_ERROR_CLASS = 'HTTPX::Error'
10
+
11
+ def send_requests_with_tracing(*requests)
12
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
13
+ requests.each { |r| nr_start_segment(r) }
14
+ yield
15
+ end
16
+
17
+ def nr_start_segment(request)
18
+ return unless NewRelic::Agent::Tracer.state.is_execution_traced?
19
+
20
+ wrapped_request = NewRelic::Agent::HTTPClients::HTTPXHTTPRequest.new(request)
21
+ segment = NewRelic::Agent::Tracer.start_external_request_segment(
22
+ library: wrapped_request.type,
23
+ uri: wrapped_request.uri,
24
+ procedure: wrapped_request.method
25
+ )
26
+ segment.add_request_headers(wrapped_request)
27
+
28
+ request.on(:response) { nr_finish_segment.call(request, segment) }
29
+ end
30
+
31
+ def nr_finish_segment
32
+ proc do |request, segment|
33
+ response = @responses[request]
34
+
35
+ unless response
36
+ NewRelic::Agent.logger.debug('Processed an on-response callback for HTTPX but could not find the response!')
37
+ next
38
+ end
39
+
40
+ wrapped_response = NewRelic::Agent::HTTPClients::HTTPXHTTPResponse.new(response)
41
+ segment.process_response_headers(wrapped_response)
42
+
43
+ if response.is_a?(::HTTPX::ErrorResponse)
44
+ e = NewRelic::Agent::NoticeableError.new(NOTICEABLE_ERROR_CLASS, "Couldn't connect: #{response}")
45
+ segment.notice_error(e)
46
+ end
47
+
48
+ ::NewRelic::Agent::Transaction::Segment.finish(segment)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module HTTPX
7
+ module Prepend
8
+ include NewRelic::Agent::Instrumentation::HTTPX
9
+
10
+ def send_requests(*requests)
11
+ send_requests_with_tracing(*requests) { super }
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'httpx/chain'
6
+ require_relative 'httpx/instrumentation'
7
+ require_relative 'httpx/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :httpx
11
+
12
+ depends_on do
13
+ defined?(HTTPX) && Gem::Version.new(HTTPX::VERSION) >= Gem::Version.new('1.0.0')
14
+ end
15
+
16
+ executes do
17
+ NewRelic::Agent.logger.info('Installing httpx instrumentation')
18
+ end
19
+
20
+ executes do
21
+ if use_prepend?
22
+ prepend_instrument HTTPX::Session, NewRelic::Agent::Instrumentation::HTTPX::Prepend
23
+ else
24
+ chain_instrument NewRelic::Agent::Instrumentation::HTTPX::Chain
25
+ end
26
+ end
27
+ end
@@ -131,10 +131,8 @@ module NewRelic
131
131
  UNKNOWN
132
132
  end
133
133
 
134
- SLASH = '/'.freeze
135
-
136
134
  def unix_domain_socket?(host)
137
- host.start_with?(SLASH)
135
+ host.start_with?(NewRelic::SLASH)
138
136
  end
139
137
  end
140
138
  end
@@ -35,7 +35,7 @@ module NewRelic
35
35
  segment.process_response_headers(wrapped_response)
36
36
  response
37
37
  ensure
38
- segment.finish
38
+ segment&.finish
39
39
  end
40
40
  end
41
41
  end
@@ -34,6 +34,7 @@ DependencyDetection.defer do
34
34
 
35
35
  subs = %w[send_file
36
36
  send_data
37
+ send_stream
37
38
  redirect_to
38
39
  halted_callback
39
40
  unpermitted_parameters]
@@ -0,0 +1,45 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module Roda
7
+ module Ignorer
8
+ def self.should_ignore?(app, type)
9
+ return false unless app.opts.include?(:newrelic_ignores)
10
+
11
+ app.opts[:newrelic_ignores][type].any? do |pattern|
12
+ pattern === app.request.path_info
13
+ end
14
+ end
15
+
16
+ def newrelic_ignore(*routes)
17
+ set_newrelic_ignore(:routes, *routes)
18
+ end
19
+
20
+ def newrelic_ignore_apdex(*routes)
21
+ set_newrelic_ignore(:apdex, *routes)
22
+ end
23
+
24
+ def newrelic_ignore_enduser(*routes)
25
+ set_newrelic_ignore(:enduser, *routes)
26
+ end
27
+
28
+ private
29
+
30
+ def set_newrelic_ignore(type, *routes)
31
+ # Create a newrelic_ignores hash if one doesn't exist
32
+ opts[:newrelic_ignores] = Hash.new([]) if !opts.include?(:newrelic_ignores)
33
+
34
+ if routes.empty?
35
+ opts[:newrelic_ignores][type] += [Regexp.new('.*')]
36
+ else
37
+ opts[:newrelic_ignores][type] += routes.map do |r|
38
+ # Roda adds leading slashes to routes, so we need to do the same
39
+ "#{'/' unless r.start_with?('/')}#{r}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -51,6 +51,18 @@ module NewRelic::Agent::Instrumentation
51
51
  yield
52
52
  end
53
53
  end
54
+
55
+ def do_not_trace?
56
+ NewRelic::Agent::Instrumentation::Roda::Ignorer.should_ignore?(self, :routes)
57
+ end
58
+
59
+ def ignore_apdex?
60
+ NewRelic::Agent::Instrumentation::Roda::Ignorer.should_ignore?(self, :apdex)
61
+ end
62
+
63
+ def ignore_enduser?
64
+ NewRelic::Agent::Instrumentation::Roda::Ignorer.should_ignore?(self, :enduser)
65
+ end
54
66
  end
55
67
  end
56
68
  end
@@ -9,13 +9,12 @@ module NewRelic
9
9
  module TransactionNamer
10
10
  extend self
11
11
 
12
- ROOT = '/'.freeze
13
12
  REGEX_MULTIPLE_SLASHES = %r{^[/^]*(.*?)[/$?]*$}.freeze
14
13
 
15
14
  def transaction_name(request)
16
15
  path = request.path || ::NewRelic::Agent::UNKNOWN_METRIC
17
16
  name = path.gsub(REGEX_MULTIPLE_SLASHES, '\1') # remove any rogue slashes
18
- name = ROOT if name.empty?
17
+ name = NewRelic::ROOT if name.empty?
19
18
  name = "#{request.request_method} #{name}" if request.respond_to?(:request_method)
20
19
 
21
20
  name
@@ -4,6 +4,7 @@
4
4
 
5
5
  require_relative 'roda/instrumentation'
6
6
  require_relative 'roda/roda_transaction_namer'
7
+ require_relative 'roda/ignorer'
7
8
 
8
9
  DependencyDetection.defer do
9
10
  named :roda
@@ -30,5 +31,6 @@ DependencyDetection.defer do
30
31
  chain_instrument NewRelic::Agent::Instrumentation::Roda::Build::Chain
31
32
  chain_instrument NewRelic::Agent::Instrumentation::Roda::Chain
32
33
  end
34
+ Roda.class_eval { extend NewRelic::Agent::Instrumentation::Roda::Ignorer }
33
35
  end
34
36
  end
@@ -33,7 +33,9 @@ DependencyDetection.defer do
33
33
  end
34
34
 
35
35
  if config.respond_to?(:error_handlers)
36
- config.error_handlers << proc do |error, *_|
36
+ # Sidekiq 3.0.0 - 7.1.4 expect error_handlers to have 2 arguments
37
+ # Sidekiq 7.1.5+ expect error_handlers to have 3 arguments
38
+ config.error_handlers << proc do |error, _ctx, *_|
37
39
  NewRelic::Agent.notice_error(error)
38
40
  end
39
41
  end
@@ -33,7 +33,7 @@ module NewRelic::Agent::Instrumentation
33
33
  set(:newrelic_ignores, Hash.new([])) if !respond_to?(:newrelic_ignores)
34
34
 
35
35
  # If we call an ignore without a route, it applies to the whole app
36
- routes = ['*'] if routes.empty?
36
+ routes = [::NewRelic::ASTERISK] if routes.empty?
37
37
 
38
38
  settings.newrelic_ignores[type] += routes.map do |r|
39
39
  # Ugly sending to private Base#compile, but we want to mimic
@@ -25,14 +25,12 @@ module NewRelic
25
25
  transaction_name(::NewRelic::Agent::UNKNOWN_METRIC, request)
26
26
  end
27
27
 
28
- ROOT = '/'.freeze
29
-
30
28
  def transaction_name(route_text, request)
31
29
  verb = http_verb(request)
32
30
 
33
31
  route_text = route_text.source if route_text.is_a?(Regexp)
34
32
  name = route_text.gsub(%r{^[/^\\A]*(.*?)[/\$\?\\z]*$}, '\1')
35
- name = ROOT if name.empty?
33
+ name = NewRelic::ROOT if name.empty?
36
34
  name = "#{verb} #{name}" unless verb.nil?
37
35
  name
38
36
  rescue => e
@@ -46,7 +46,7 @@ DependencyDetection.defer do
46
46
  executes do
47
47
  next unless Gem::Version.new(Sinatra::VERSION) < Gem::Version.new('2.0.0')
48
48
 
49
- deprecation_msg = 'The Ruby Agent is dropping support for Sinatra versions below 2.0.0 ' \
49
+ deprecation_msg = 'The Ruby agent is dropping support for Sinatra versions below 2.0.0 ' \
50
50
  'in version 9.0.0. Please upgrade your Sinatra version to continue receiving full compatibility. ' \
51
51
 
52
52
  NewRelic::Agent.logger.log_once(
@@ -16,10 +16,7 @@ module NewRelic
16
16
  def add_thread_tracing(*args, &block)
17
17
  return block if !NewRelic::Agent::Tracer.thread_tracing_enabled?
18
18
 
19
- NewRelic::Agent::Tracer.thread_block_with_current_transaction(
20
- segment_name: 'Ruby/Thread',
21
- &block
22
- )
19
+ NewRelic::Agent::Tracer.thread_block_with_current_transaction(&block)
23
20
  end
24
21
  end
25
22
  end
@@ -0,0 +1,21 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module ViewComponent::Chain
7
+ def self.instrument!
8
+ ::ViewComponent::Base.class_eval do
9
+ include NewRelic::Agent::Instrumentation::ViewComponent
10
+
11
+ alias_method(:render_in_without_tracing, :render_in)
12
+
13
+ def render_in(*args)
14
+ render_in_with_tracing(*args) do
15
+ render_in_without_tracing(*args)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module ViewComponent
7
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
8
+
9
+ def render_in_with_tracing(*args)
10
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
+
12
+ begin
13
+ segment = NewRelic::Agent::Tracer.start_segment(
14
+ name: metric_name(self.class.identifier, self.class.name)
15
+ )
16
+ yield
17
+ rescue => e
18
+ ::NewRelic::Agent.logger.debug('Error capturing ViewComponent segment', e)
19
+ ensure
20
+ segment&.finish
21
+ end
22
+ end
23
+
24
+ def metric_name(identifier, component)
25
+ "View/#{metric_path(identifier)}/#{component}"
26
+ end
27
+
28
+ def metric_path(identifier)
29
+ return 'component' unless identifier
30
+
31
+ if (parts = identifier.split('/')).size > 1
32
+ parts[-2..-1].join('/') # Get filepath by assuming the Rails' structure: app/components/home/example_component.rb
33
+ else
34
+ NewRelic::Agent::UNKNOWN_METRIC
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,13 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ module NewRelic::Agent::Instrumentation
6
+ module ViewComponent::Prepend
7
+ include NewRelic::Agent::Instrumentation::ViewComponent
8
+
9
+ def render_in(*args)
10
+ render_in_with_tracing(*args) { super }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # This file is distributed under New Relic's license terms.
2
+ # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
+ # frozen_string_literal: true
4
+
5
+ require_relative 'view_component/instrumentation'
6
+ require_relative 'view_component/chain'
7
+ require_relative 'view_component/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :view_component
11
+
12
+ depends_on do
13
+ defined?(ViewComponent) &&
14
+ ViewComponent::Base.method_defined?(:render_in)
15
+ end
16
+
17
+ executes do
18
+ NewRelic::Agent.logger.info('Installing ViewComponent instrumentation')
19
+
20
+ if use_prepend?
21
+ prepend_instrument ViewComponent::Base, NewRelic::Agent::Instrumentation::ViewComponent::Prepend
22
+ else
23
+ chain_instrument NewRelic::Agent::Instrumentation::ViewComponent::Chain
24
+ end
25
+ end
26
+ end
@@ -2,7 +2,6 @@
2
2
  # See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
3
3
  # frozen_string_literal: true
4
4
 
5
- require 'base64'
6
5
  require 'json'
7
6
  require 'new_relic/agent/obfuscator'
8
7
 
@@ -329,9 +329,9 @@ module NewRelic
329
329
 
330
330
  def transaction_name(library, destination_type, destination_name)
331
331
  transaction_name = Transaction::MESSAGE_PREFIX + library
332
- transaction_name << Transaction::MessageBrokerSegment::SLASH
332
+ transaction_name << NewRelic::SLASH
333
333
  transaction_name << Transaction::MessageBrokerSegment::TYPES[destination_type]
334
- transaction_name << Transaction::MessageBrokerSegment::SLASH
334
+ transaction_name << NewRelic::SLASH
335
335
 
336
336
  case destination_type
337
337
  when :queue
@@ -5,7 +5,8 @@
5
5
  module NewRelic
6
6
  module Agent
7
7
  class SyntheticsMonitor < InboundRequestMonitor
8
- SYNTHETICS_HEADER_KEY = 'HTTP_X_NEWRELIC_SYNTHETICS'.freeze
8
+ SYNTHETICS_HEADER_KEY = 'HTTP_X_NEWRELIC_SYNTHETICS'
9
+ SYNTHETICS_INFO_HEADER_KEY = 'HTTP_X_NEWRELIC_SYNTHETICS_INFO'
9
10
 
10
11
  SUPPORTED_VERSION = 1
11
12
  EXPECTED_PAYLOAD_LENGTH = 5
@@ -16,6 +17,7 @@ module NewRelic
16
17
 
17
18
  def on_before_call(request) # THREAD_LOCAL_ACCESS
18
19
  encoded_header = request[SYNTHETICS_HEADER_KEY]
20
+ info_header = request[SYNTHETICS_INFO_HEADER_KEY]
19
21
  return unless encoded_header
20
22
 
21
23
  incoming_payload = deserialize_header(encoded_header, SYNTHETICS_HEADER_KEY)
@@ -27,7 +29,16 @@ module NewRelic
27
29
 
28
30
  txn = Tracer.current_transaction
29
31
  txn.raw_synthetics_header = encoded_header
32
+ txn.raw_synthetics_info_header = info_header
30
33
  txn.synthetics_payload = incoming_payload
34
+ txn.synthetics_info_payload = load_json(info_header, SYNTHETICS_INFO_HEADER_KEY)
35
+ end
36
+
37
+ def load_json(header, key)
38
+ ::JSON.load(header)
39
+ rescue => err
40
+ NewRelic::Agent.logger.debug("Failure loading json header '#{key}' in #{self.class}, #{err.class}, #{err.message}")
41
+ nil
31
42
  end
32
43
 
33
44
  class << self