newrelic_rpm 9.4.2 → 9.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +138 -10
  3. data/Rakefile +1 -1
  4. data/lib/new_relic/agent/attribute_pre_filtering.rb +109 -0
  5. data/lib/new_relic/agent/configuration/default_source.rb +153 -33
  6. data/lib/new_relic/agent/http_clients/async_http_wrappers.rb +83 -0
  7. data/lib/new_relic/agent/http_clients/ethon_wrappers.rb +111 -0
  8. data/lib/new_relic/agent/http_clients/httpx_wrappers.rb +93 -0
  9. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +1 -2
  10. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/chain.rb +69 -0
  11. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +13 -0
  12. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/prepend.rb +37 -0
  13. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +23 -0
  14. data/lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb +4 -0
  15. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +3 -1
  16. data/lib/new_relic/agent/instrumentation/async_http/chain.rb +23 -0
  17. data/lib/new_relic/agent/instrumentation/async_http/instrumentation.rb +37 -0
  18. data/lib/new_relic/agent/instrumentation/async_http/prepend.rb +15 -0
  19. data/lib/new_relic/agent/instrumentation/async_http.rb +26 -0
  20. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +9 -0
  21. data/lib/new_relic/agent/instrumentation/concurrent_ruby/instrumentation.rb +2 -2
  22. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +4 -0
  23. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +3 -0
  24. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +4 -1
  25. data/lib/new_relic/agent/instrumentation/ethon/chain.rb +39 -0
  26. data/lib/new_relic/agent/instrumentation/ethon/instrumentation.rb +105 -0
  27. data/lib/new_relic/agent/instrumentation/ethon/prepend.rb +35 -0
  28. data/lib/new_relic/agent/instrumentation/ethon.rb +39 -0
  29. data/lib/new_relic/agent/instrumentation/excon/middleware.rb +3 -0
  30. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +4 -0
  31. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +4 -0
  32. data/lib/new_relic/agent/instrumentation/grpc/server/instrumentation.rb +4 -0
  33. data/lib/new_relic/agent/instrumentation/grpc_client.rb +1 -1
  34. data/lib/new_relic/agent/instrumentation/grpc_server.rb +1 -1
  35. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +4 -0
  36. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +4 -0
  37. data/lib/new_relic/agent/instrumentation/httpx/chain.rb +20 -0
  38. data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +51 -0
  39. data/lib/new_relic/agent/instrumentation/httpx/prepend.rb +15 -0
  40. data/lib/new_relic/agent/instrumentation/httpx.rb +27 -0
  41. data/lib/new_relic/agent/instrumentation/logger/instrumentation.rb +3 -0
  42. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +9 -0
  43. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -3
  44. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +5 -1
  45. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +4 -0
  46. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +4 -0
  47. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +6 -0
  48. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +4 -0
  49. data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +1 -0
  50. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +4 -0
  51. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +4 -0
  52. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +4 -0
  53. data/lib/new_relic/agent/instrumentation/roda/ignorer.rb +45 -0
  54. data/lib/new_relic/agent/instrumentation/roda/instrumentation.rb +16 -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/client.rb +4 -0
  58. data/lib/new_relic/agent/instrumentation/sidekiq/server.rb +26 -3
  59. data/lib/new_relic/agent/instrumentation/sidekiq.rb +5 -3
  60. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +4 -0
  61. data/lib/new_relic/agent/instrumentation/sinatra/transaction_namer.rb +1 -3
  62. data/lib/new_relic/agent/instrumentation/stripe.rb +28 -0
  63. data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +77 -0
  64. data/lib/new_relic/agent/instrumentation/tilt/instrumentation.rb +4 -0
  65. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +5 -1
  66. data/lib/new_relic/agent/messaging.rb +2 -2
  67. data/lib/new_relic/agent/monitors/synthetics_monitor.rb +12 -1
  68. data/lib/new_relic/agent/rules_engine.rb +1 -1
  69. data/lib/new_relic/agent/span_event_primitive.rb +16 -4
  70. data/lib/new_relic/agent/system_info.rb +26 -0
  71. data/lib/new_relic/agent/tracer.rb +1 -3
  72. data/lib/new_relic/agent/transaction/abstract_segment.rb +55 -0
  73. data/lib/new_relic/agent/transaction/external_request_segment.rb +5 -2
  74. data/lib/new_relic/agent/transaction/message_broker_segment.rb +1 -2
  75. data/lib/new_relic/agent/transaction/request_attributes.rb +1 -3
  76. data/lib/new_relic/agent/transaction.rb +25 -2
  77. data/lib/new_relic/agent/transaction_error_primitive.rb +16 -0
  78. data/lib/new_relic/agent/transaction_event_primitive.rb +19 -0
  79. data/lib/new_relic/agent/utilization/gcp.rb +1 -3
  80. data/lib/new_relic/agent.rb +18 -2
  81. data/lib/new_relic/constants.rb +3 -0
  82. data/lib/new_relic/control/frameworks/rails.rb +14 -2
  83. data/lib/new_relic/language_support.rb +4 -0
  84. data/lib/new_relic/rack/agent_hooks.rb +1 -1
  85. data/lib/new_relic/rack/agent_middleware.rb +0 -16
  86. data/lib/new_relic/supportability_helper.rb +1 -0
  87. data/lib/new_relic/version.rb +2 -2
  88. data/lib/tasks/instrumentation_generator/instrumentation.thor +3 -3
  89. data/lib/tasks/tests.rake +71 -0
  90. data/newrelic.yml +93 -32
  91. data/newrelic_rpm.gemspec +4 -1
  92. metadata +39 -2
@@ -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
@@ -6,6 +6,7 @@ module ::Excon
6
6
  module Middleware
7
7
  class NewRelicCrossAppTracing
8
8
  TRACE_DATA_IVAR = :@newrelic_trace_data
9
+ INSTRUMENTATION_NAME = 'Excon'
9
10
 
10
11
  def initialize(stack)
11
12
  @stack = stack
@@ -18,6 +19,8 @@ module ::Excon
18
19
  # :idempotent in the options, but there will be only a single
19
20
  # accompanying response_call/error_call.
20
21
  if datum[:connection] && !datum[:connection].instance_variable_get(TRACE_DATA_IVAR)
22
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
23
+
21
24
  wrapped_request = ::NewRelic::Agent::HTTPClients::ExconHTTPRequest.new(datum)
22
25
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
23
26
  library: wrapped_request.type,
@@ -9,6 +9,8 @@ module NewRelic::Agent::Instrumentation
9
9
  module Instrumentation
10
10
  extend self
11
11
 
12
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
13
+
12
14
  # Since 1.2.0, the class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is `Grape::API::Instance`
13
15
  # https://github.com/ruby-grape/grape/blob/c20a73ac1e3f3ba1082005ed61bf69452373ba87/UPGRADING.md#upgrading-to--120
14
16
  def instrumented_class
@@ -46,6 +48,8 @@ module NewRelic::Agent::Instrumentation
46
48
  def handle_transaction(endpoint, class_name, version)
47
49
  return unless endpoint && route = endpoint.route
48
50
 
51
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
52
+
49
53
  name_transaction(route, class_name, version)
50
54
  capture_params(endpoint)
51
55
  end
@@ -12,10 +12,14 @@ module NewRelic
12
12
  module Client
13
13
  include NewRelic::Agent::Instrumentation::GRPC::Helper
14
14
 
15
+ INSTRUMENTATION_NAME = 'gRPC_Client'
16
+
15
17
  def issue_request_with_tracing(grpc_type, method, requests, marshal, unmarshal,
16
18
  deadline:, return_op:, parent:, credentials:, metadata:)
17
19
  return yield unless trace_with_newrelic?
18
20
 
21
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
22
+
19
23
  segment = request_segment(method)
20
24
  request_wrapper = NewRelic::Agent::Instrumentation::GRPC::Client::RequestWrapper.new(@host)
21
25
  # do not insert CAT headers for gRPC requests https://github.com/newrelic/newrelic-ruby-agent/issues/1730
@@ -11,6 +11,8 @@ module NewRelic
11
11
  module Server
12
12
  include NewRelic::Agent::Instrumentation::GRPC::Helper
13
13
 
14
+ INSTRUMENTATION_NAME = 'gRPC_Server'
15
+
14
16
  DT_KEYS = [NewRelic::NEWRELIC_KEY, NewRelic::TRACEPARENT_KEY, NewRelic::TRACESTATE_KEY].freeze
15
17
  INSTANCE_VAR_HOST = :@host_nr
16
18
  INSTANCE_VAR_PORT = :@port_nr
@@ -23,6 +25,8 @@ module NewRelic
23
25
  def handle_with_tracing(streamer_type, active_call, mth, _inter_ctx)
24
26
  return yield unless trace_with_newrelic?
25
27
 
28
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
29
+
26
30
  metadata = metadata_for_call(active_call)
27
31
  txn = NewRelic::Agent::Transaction.start_new_transaction(NewRelic::Agent::Tracer.state,
28
32
  CATEGORY,
@@ -13,7 +13,7 @@ DependencyDetection.defer do
13
13
  end
14
14
 
15
15
  executes do
16
- supportability_name = 'gRPC_Client'
16
+ supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
17
17
  if use_prepend?
18
18
  prepend_instrument GRPC::ClientStub, NewRelic::Agent::Instrumentation::GRPC::Client::Prepend, supportability_name
19
19
  else
@@ -14,7 +14,7 @@ DependencyDetection.defer do
14
14
  end
15
15
 
16
16
  executes do
17
- supportability_name = 'gRPC_Server'
17
+ supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
18
18
  if use_prepend?
19
19
  prepend_instrument GRPC::RpcServer, NewRelic::Agent::Instrumentation::GRPC::Server::RpcServerPrepend, supportability_name
20
20
  prepend_instrument GRPC::RpcDesc, NewRelic::Agent::Instrumentation::GRPC::Server::RpcDescPrepend, supportability_name
@@ -5,7 +5,11 @@
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module HTTPClient
7
7
  module Instrumentation
8
+ INSTRUMENTATION_NAME = 'HTTPClient'
9
+
8
10
  def with_tracing(request, connection)
11
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
12
+
9
13
  wrapped_request = NewRelic::Agent::HTTPClients::HTTPClientRequest.new(request)
10
14
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
11
15
  library: wrapped_request.type,
@@ -4,7 +4,11 @@
4
4
 
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module HTTPrb
7
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
8
+
7
9
  def with_tracing(request)
10
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
+
8
12
  wrapped_request = ::NewRelic::Agent::HTTPClients::HTTPRequest.new(request)
9
13
 
10
14
  begin
@@ -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
@@ -6,6 +6,8 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module Logger
9
+ INSTRUMENTATION_NAME = 'Logger'
10
+
9
11
  def skip_instrumenting?
10
12
  defined?(@skip_instrumenting) && @skip_instrumenting
11
13
  end
@@ -51,6 +53,7 @@ module NewRelic
51
53
  mark_skip_instrumenting
52
54
 
53
55
  unless ::NewRelic::Agent.agent.nil?
56
+ ::NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
54
57
  ::NewRelic::Agent.agent.log_event_aggregator.record(formatted_message, severity)
55
58
  formatted_message = LocalLogDecorator.decorate(formatted_message)
56
59
  end
@@ -10,8 +10,11 @@ module NewRelic::Agent::Instrumentation
10
10
  LOCALHOST = 'localhost'
11
11
  MULTIGET_METRIC_NAME = 'get_multi_request'
12
12
  MEMCACHED = 'Memcached'
13
+ INSTRUMENTATION_NAME = 'Dalli'
13
14
 
14
15
  def with_newrelic_tracing(operation, *args)
16
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
17
+
15
18
  segment = NewRelic::Agent::Tracer.start_datastore_segment(
16
19
  product: MEMCACHED,
17
20
  operation: operation
@@ -28,6 +31,8 @@ module NewRelic::Agent::Instrumentation
28
31
  end
29
32
 
30
33
  def server_for_key_with_newrelic_tracing
34
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
35
+
31
36
  yield.tap do |server|
32
37
  begin
33
38
  if txn = ::NewRelic::Agent::Tracer.current_transaction
@@ -43,6 +48,8 @@ module NewRelic::Agent::Instrumentation
43
48
  end
44
49
 
45
50
  def get_multi_with_newrelic_tracing(method_name)
51
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
52
+
46
53
  segment = NewRelic::Agent::Tracer.start_segment(
47
54
  name: "Ruby/Memcached/Dalli/#{method_name}"
48
55
  )
@@ -55,6 +62,8 @@ module NewRelic::Agent::Instrumentation
55
62
  end
56
63
 
57
64
  def send_multiget_with_newrelic_tracing(keys)
65
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
66
+
58
67
  segment = ::NewRelic::Agent::Tracer.start_datastore_segment(
59
68
  product: MEMCACHED,
60
69
  operation: MULTIGET_METRIC_NAME
@@ -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
@@ -6,7 +6,11 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module NetHTTP
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  def request_with_tracing(request)
12
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
13
+
10
14
  wrapped_request = NewRelic::Agent::HTTPClients::NetHTTPRequest.new(self, request)
11
15
 
12
16
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
@@ -31,7 +35,7 @@ module NewRelic
31
35
  segment.process_response_headers(wrapped_response)
32
36
  response
33
37
  ensure
34
- segment.finish
38
+ segment&.finish
35
39
  end
36
40
  end
37
41
  end
@@ -47,6 +47,10 @@ module NewRelic
47
47
  end
48
48
  end
49
49
 
50
+ # The agent doesn't use the traditional ActiveSupport::Notifications.subscribe
51
+ # pattern due to threading issues discovered on initial instrumentation.
52
+ # Instead we define a #start and #finish method, which Rails responds to.
53
+ # See: https://github.com/rails/rails/issues/12069
50
54
  def start(name, id, payload)
51
55
  return unless state.is_execution_traced?
52
56
 
@@ -4,7 +4,11 @@
4
4
 
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module Padrino
7
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
8
+
7
9
  def invoke_route_with_tracing(*args)
10
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
+
8
12
  begin
9
13
  env['newrelic.last_route'] = args[0].original_path
10
14
  rescue => e
@@ -6,6 +6,8 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module RackBuilder
9
+ INSTRUMENTATION_NAME = 'Rack'
10
+
9
11
  def self.track_deferred_detection(builder_class)
10
12
  class << builder_class
11
13
  attr_accessor :_nr_deferred_detection_ran
@@ -51,6 +53,8 @@ module NewRelic
51
53
  def run_with_tracing(app)
52
54
  return yield(app) unless middleware_instrumentation_enabled?
53
55
 
56
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
57
+
54
58
  yield(::NewRelic::Agent::Instrumentation::MiddlewareProxy.wrap(app, true))
55
59
  end
56
60
 
@@ -58,6 +62,8 @@ module NewRelic
58
62
  return if middleware_class.nil?
59
63
  return yield(middleware_class) unless middleware_instrumentation_enabled?
60
64
 
65
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
66
+
61
67
  yield(::NewRelic::Agent::Instrumentation::MiddlewareProxy.for_class(middleware_class))
62
68
  end
63
69
  end
@@ -9,6 +9,8 @@ module NewRelic
9
9
  module Instrumentation
10
10
  module Rails3
11
11
  module ActionController
12
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
13
+
12
14
  # determine the path that is used in the metric name for
13
15
  # the called controller action
14
16
  def newrelic_metric_path(action_name_override = nil)
@@ -21,6 +23,8 @@ module NewRelic
21
23
  end
22
24
 
23
25
  def process_action(*args) # THREAD_LOCAL_ACCESS
26
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
27
+
24
28
  munged_params = NewRelic::Agent::ParameterFiltering.filter_rails_request_parameters(request.filtered_parameters)
25
29
  perform_action_with_newrelic_trace(:category => :controller,
26
30
  :name => self.action_name,
@@ -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]
@@ -7,11 +7,15 @@ module NewRelic
7
7
  module Instrumentation
8
8
  module Rake
9
9
  module Tracer
10
+ INSTRUMENTATION_NAME = 'Rake'
11
+
10
12
  def invoke_with_newrelic_tracing(*args)
11
13
  unless NewRelic::Agent::Instrumentation::Rake.should_trace?(name)
12
14
  return yield
13
15
  end
14
16
 
17
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
18
+
15
19
  begin
16
20
  timeout = NewRelic::Agent.config[:'rake.connect_timeout']
17
21
  NewRelic::Agent.instance.wait_on_connect(timeout)
@@ -6,6 +6,8 @@ require_relative 'constants'
6
6
 
7
7
  module NewRelic::Agent::Instrumentation
8
8
  module Redis
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  def connect_with_tracing
10
12
  with_tracing(Constants::CONNECT, database: db) { yield }
11
13
  end
@@ -43,6 +45,8 @@ module NewRelic::Agent::Instrumentation
43
45
  private
44
46
 
45
47
  def with_tracing(operation, statement: nil, database: nil)
48
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
49
+
46
50
  segment = NewRelic::Agent::Tracer.start_datastore_segment(
47
51
  product: Constants::PRODUCT_NAME,
48
52
  operation: operation,
@@ -6,7 +6,11 @@ module NewRelic::Agent::Instrumentation
6
6
  module Resque
7
7
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
8
8
 
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  def with_tracing
12
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
13
+
10
14
  begin
11
15
  perform_action_with_newrelic_trace(
12
16
  :name => 'perform',
@@ -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
@@ -7,6 +7,8 @@ module NewRelic::Agent::Instrumentation
7
7
  module Tracer
8
8
  include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
9
9
 
10
+ INSTRUMENTATION_NAME = 'Roda'
11
+
10
12
  def self.included(clazz)
11
13
  clazz.extend(self)
12
14
  end
@@ -39,6 +41,8 @@ module NewRelic::Agent::Instrumentation
39
41
  end
40
42
 
41
43
  def _roda_handle_main_route_with_tracing(*args)
44
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
45
+
42
46
  perform_action_with_newrelic_trace(
43
47
  category: :roda,
44
48
  name: ::NewRelic::Agent::Instrumentation::Roda::TransactionNamer.transaction_name(request),
@@ -47,6 +51,18 @@ module NewRelic::Agent::Instrumentation
47
51
  yield
48
52
  end
49
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
50
66
  end
51
67
  end
52
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
@@ -6,7 +6,11 @@ module NewRelic::Agent::Instrumentation::Sidekiq
6
6
  class Client
7
7
  include Sidekiq::ClientMiddleware if defined?(Sidekiq::ClientMiddleware)
8
8
 
9
+ INSTRUMENTATION_NAME = 'SidekiqClient'
10
+
9
11
  def call(_worker_class, job, *_)
12
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
13
+
10
14
  job[NewRelic::NEWRELIC_KEY] ||= distributed_tracing_headers if ::NewRelic::Agent.config[:'distributed_tracing.enabled']
11
15
  yield
12
16
  end