newrelic_rpm 9.7.0 → 9.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (160) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +376 -2
  3. data/README.md +17 -18
  4. data/Rakefile +1 -1
  5. data/lib/boot/strap.rb +101 -0
  6. data/lib/new_relic/agent/agent.rb +4 -1
  7. data/lib/new_relic/agent/agent_helpers/connect.rb +10 -8
  8. data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -1
  9. data/lib/new_relic/agent/agent_helpers/startup.rb +2 -1
  10. data/lib/new_relic/agent/agent_logger.rb +3 -1
  11. data/lib/new_relic/agent/aws.rb +68 -0
  12. data/lib/new_relic/agent/configuration/default_source.rb +519 -23
  13. data/lib/new_relic/agent/configuration/environment_source.rb +14 -2
  14. data/lib/new_relic/agent/configuration/high_security_source.rb +1 -0
  15. data/lib/new_relic/agent/configuration/manager.rb +51 -8
  16. data/lib/new_relic/agent/configuration/security_policy_source.rb +11 -0
  17. data/lib/new_relic/agent/configuration/yaml_source.rb +2 -0
  18. data/lib/new_relic/agent/connect/request_builder.rb +1 -1
  19. data/lib/new_relic/agent/custom_event_aggregator.rb +27 -1
  20. data/lib/new_relic/agent/database/obfuscation_helpers.rb +11 -11
  21. data/lib/new_relic/agent/database/obfuscator.rb +1 -0
  22. data/lib/new_relic/agent/database.rb +39 -0
  23. data/lib/new_relic/agent/distributed_tracing/distributed_trace_payload.rb +1 -5
  24. data/lib/new_relic/agent/error_collector.rb +39 -10
  25. data/lib/new_relic/agent/harvester.rb +1 -1
  26. data/lib/new_relic/agent/instrumentation/active_merchant.rb +0 -13
  27. data/lib/new_relic/agent/instrumentation/active_record.rb +1 -8
  28. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +3 -0
  29. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +1 -12
  30. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger/instrumentation.rb +7 -3
  31. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +0 -2
  32. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +0 -2
  33. data/lib/new_relic/agent/instrumentation/async_http.rb +4 -3
  34. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/chain.rb +33 -0
  35. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +93 -0
  36. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/prepend.rb +23 -0
  37. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda.rb +23 -0
  38. data/lib/new_relic/agent/instrumentation/aws_sqs/chain.rb +37 -0
  39. data/lib/new_relic/agent/instrumentation/aws_sqs/instrumentation.rb +67 -0
  40. data/lib/new_relic/agent/instrumentation/aws_sqs/prepend.rb +21 -0
  41. data/lib/new_relic/agent/instrumentation/aws_sqs.rb +23 -0
  42. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +14 -0
  43. data/lib/new_relic/agent/instrumentation/bunny.rb +3 -4
  44. data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -2
  45. data/lib/new_relic/agent/instrumentation/curb.rb +3 -4
  46. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +0 -23
  47. data/lib/new_relic/agent/instrumentation/dynamodb/chain.rb +27 -0
  48. data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +64 -0
  49. data/lib/new_relic/agent/instrumentation/dynamodb/prepend.rb +19 -0
  50. data/lib/new_relic/agent/instrumentation/dynamodb.rb +23 -0
  51. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +58 -8
  52. data/lib/new_relic/agent/instrumentation/elasticsearch.rb +0 -2
  53. data/lib/new_relic/agent/instrumentation/ethon.rb +0 -4
  54. data/lib/new_relic/agent/instrumentation/excon.rb +0 -16
  55. data/lib/new_relic/agent/instrumentation/fiber.rb +0 -2
  56. data/lib/new_relic/agent/instrumentation/grape.rb +1 -1
  57. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +0 -1
  58. data/lib/new_relic/agent/instrumentation/grpc_server.rb +1 -1
  59. data/lib/new_relic/agent/instrumentation/httpclient.rb +0 -1
  60. data/lib/new_relic/agent/instrumentation/httprb.rb +0 -1
  61. data/lib/new_relic/agent/instrumentation/httpx.rb +0 -4
  62. data/lib/new_relic/agent/instrumentation/logger.rb +1 -3
  63. data/lib/new_relic/agent/instrumentation/logstasher/chain.rb +21 -0
  64. data/lib/new_relic/agent/instrumentation/logstasher/instrumentation.rb +24 -0
  65. data/lib/new_relic/agent/instrumentation/logstasher/prepend.rb +13 -0
  66. data/lib/new_relic/agent/instrumentation/logstasher.rb +25 -0
  67. data/lib/new_relic/agent/instrumentation/memcache.rb +0 -1
  68. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +6 -0
  69. data/lib/new_relic/agent/instrumentation/opensearch/chain.rb +21 -0
  70. data/lib/new_relic/agent/instrumentation/opensearch/instrumentation.rb +66 -0
  71. data/lib/new_relic/agent/instrumentation/opensearch/prepend.rb +13 -0
  72. data/lib/new_relic/agent/instrumentation/opensearch.rb +23 -0
  73. data/lib/new_relic/agent/instrumentation/padrino.rb +3 -3
  74. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +3 -0
  75. data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +9 -5
  76. data/lib/new_relic/agent/instrumentation/rake.rb +0 -1
  77. data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +72 -0
  78. data/lib/new_relic/agent/instrumentation/rdkafka/instrumentation.rb +70 -0
  79. data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +67 -0
  80. data/lib/new_relic/agent/instrumentation/rdkafka.rb +25 -0
  81. data/lib/new_relic/agent/instrumentation/redis/cluster_middleware.rb +26 -0
  82. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +14 -11
  83. data/lib/new_relic/agent/instrumentation/redis/middleware.rb +3 -0
  84. data/lib/new_relic/agent/instrumentation/redis.rb +11 -5
  85. data/lib/new_relic/agent/instrumentation/resque.rb +0 -4
  86. data/lib/new_relic/agent/instrumentation/roda.rb +4 -4
  87. data/lib/new_relic/agent/instrumentation/ruby_kafka/chain.rb +55 -0
  88. data/lib/new_relic/agent/instrumentation/ruby_kafka/instrumentation.rb +67 -0
  89. data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +60 -0
  90. data/lib/new_relic/agent/instrumentation/ruby_kafka.rb +25 -0
  91. data/lib/new_relic/agent/instrumentation/ruby_openai/chain.rb +36 -0
  92. data/lib/new_relic/agent/instrumentation/ruby_openai/instrumentation.rb +196 -0
  93. data/lib/new_relic/agent/instrumentation/ruby_openai/prepend.rb +20 -0
  94. data/lib/new_relic/agent/instrumentation/ruby_openai.rb +35 -0
  95. data/lib/new_relic/agent/instrumentation/sidekiq.rb +0 -14
  96. data/lib/new_relic/agent/instrumentation/sinatra.rb +3 -19
  97. data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +22 -1
  98. data/lib/new_relic/agent/instrumentation/thread.rb +0 -2
  99. data/lib/new_relic/agent/instrumentation/tilt.rb +0 -4
  100. data/lib/new_relic/agent/instrumentation/typhoeus.rb +0 -1
  101. data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +13 -6
  102. data/lib/new_relic/agent/instrumentation/view_component.rb +0 -2
  103. data/lib/new_relic/agent/javascript_instrumentor.rb +2 -3
  104. data/lib/new_relic/agent/llm/chat_completion_message.rb +25 -0
  105. data/lib/new_relic/agent/llm/chat_completion_summary.rb +66 -0
  106. data/lib/new_relic/agent/llm/embedding.rb +60 -0
  107. data/lib/new_relic/agent/llm/llm_event.rb +95 -0
  108. data/lib/new_relic/agent/llm/response_headers.rb +80 -0
  109. data/lib/new_relic/agent/llm.rb +49 -0
  110. data/lib/new_relic/agent/local_log_decorator.rb +8 -1
  111. data/lib/new_relic/agent/log_event_aggregator.rb +120 -44
  112. data/lib/new_relic/agent/messaging.rb +11 -5
  113. data/lib/new_relic/agent/new_relic_service.rb +12 -2
  114. data/lib/new_relic/agent/serverless_handler.rb +400 -0
  115. data/lib/new_relic/agent/serverless_handler_event_sources.json +155 -0
  116. data/lib/new_relic/agent/serverless_handler_event_sources.rb +49 -0
  117. data/lib/new_relic/agent/span_event_primitive.rb +8 -10
  118. data/lib/new_relic/agent/system_info.rb +14 -0
  119. data/lib/new_relic/agent/threading/agent_thread.rb +1 -2
  120. data/lib/new_relic/agent/tracer.rb +5 -5
  121. data/lib/new_relic/agent/transaction/abstract_segment.rb +1 -1
  122. data/lib/new_relic/agent/transaction/external_request_segment.rb +0 -10
  123. data/lib/new_relic/agent/transaction/request_attributes.rb +13 -1
  124. data/lib/new_relic/agent/transaction/trace_context.rb +1 -1
  125. data/lib/new_relic/agent/transaction/tracing.rb +2 -2
  126. data/lib/new_relic/agent/transaction.rb +2 -6
  127. data/lib/new_relic/agent/transaction_error_primitive.rb +23 -19
  128. data/lib/new_relic/agent.rb +198 -10
  129. data/lib/new_relic/constants.rb +2 -0
  130. data/lib/new_relic/control/frameworks/grape.rb +14 -0
  131. data/lib/new_relic/control/frameworks/padrino.rb +14 -0
  132. data/lib/new_relic/control/frameworks/rails4.rb +1 -3
  133. data/lib/new_relic/control/instance_methods.rb +8 -0
  134. data/lib/new_relic/control/private_instance_methods.rb +4 -0
  135. data/lib/new_relic/control/security_interface.rb +57 -0
  136. data/lib/new_relic/control.rb +1 -1
  137. data/lib/new_relic/dependency_detection.rb +10 -5
  138. data/lib/new_relic/environment_report.rb +2 -2
  139. data/lib/new_relic/helper.rb +15 -0
  140. data/lib/new_relic/language_support.rb +3 -1
  141. data/lib/new_relic/local_environment.rb +14 -10
  142. data/lib/new_relic/rack/browser_monitoring.rb +28 -12
  143. data/lib/new_relic/supportability_helper.rb +2 -0
  144. data/lib/new_relic/thread_local_storage.rb +31 -0
  145. data/lib/new_relic/version.rb +2 -2
  146. data/lib/sequel/extensions/new_relic_instrumentation.rb +3 -2
  147. data/lib/tasks/config.rake +8 -3
  148. data/lib/tasks/gha.rake +31 -0
  149. data/lib/tasks/helpers/config.html.erb +3 -2
  150. data/lib/tasks/helpers/format.rb +1 -1
  151. data/lib/tasks/helpers/newrelicyml.rb +76 -13
  152. data/lib/tasks/instrumentation_generator/instrumentation.thor +31 -22
  153. data/lib/tasks/instrumentation_generator/templates/chain.tt +0 -1
  154. data/lib/tasks/instrumentation_generator/templates/chain_method.tt +0 -1
  155. data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +11 -8
  156. data/lib/tasks/instrumentation_generator/templates/newrelic.yml.tt +1 -1
  157. data/newrelic.yml +387 -143
  158. data/newrelic_rpm.gemspec +2 -0
  159. data/test/agent_helper.rb +17 -2
  160. metadata +80 -3
@@ -0,0 +1,93 @@
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 'json'
6
+
7
+ module NewRelic::Agent::Instrumentation
8
+ module AwsSdkLambda
9
+ INSTRUMENTATION_NAME = 'aws_sdk_lambda'
10
+ AWS_SERVICE = 'lambda'
11
+ CLOUD_PLATFORM = 'aws_lambda'
12
+ WRAPPED_RESPONSE = Struct.new(:status_code, :has_status_code?)
13
+
14
+ def invoke_with_new_relic(*args)
15
+ with_tracing(:invoke, *args) { yield }
16
+ end
17
+
18
+ def invoke_async_with_new_relic(*args)
19
+ with_tracing(:invoke_async, *args) { yield }
20
+ end
21
+
22
+ def invoke_with_response_stream_with_new_relic(*args)
23
+ with_tracing(:invoke_with_response_stream, *args) { yield }
24
+ end
25
+
26
+ private
27
+
28
+ def with_tracing(action, *args)
29
+ segment = generate_segment(action, *args)
30
+
31
+ # prevent additional instrumentation for things like Net::HTTP from
32
+ # creating any segments that may appear as redundant / confusing
33
+ NewRelic::Agent.disable_all_tracing do
34
+ response = NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
35
+ process_response(response, segment)
36
+ response
37
+ end
38
+ ensure
39
+ segment&.finish
40
+ end
41
+
42
+ def process_response(response, segment)
43
+ process_function_error(response) if response.respond_to?(:function_error)
44
+ rescue => e
45
+ NewRelic::Agent.logger.error("Error processing aws-sdk-lambda invocation response: #{e}")
46
+ end
47
+
48
+ # notice error that was raised / unhandled by the function
49
+ def process_function_error(response)
50
+ function_error = response.function_error
51
+ return unless function_error
52
+
53
+ msg = "[#{function_error}]"
54
+ payload = response.payload&.string if response.respond_to?(:payload)
55
+ payload_hash = JSON.parse(payload) if payload
56
+ msg = "#{msg} #{payload_hash['errorType']} - #{payload_hash['errorMessage']}" if payload_hash
57
+ e = StandardError.new(msg)
58
+ e.set_backtrace(payload_hash['stackTrace']) if payload_hash
59
+
60
+ NewRelic::Agent.notice_error(e)
61
+ end
62
+
63
+ def generate_segment(action, options = {})
64
+ function = function_name(options)
65
+ region = aws_region
66
+ arn = aws_arn(function, region)
67
+ segment = NewRelic::Agent::Tracer.start_segment(name: "Lambda/#{action}/#{function}")
68
+ segment.add_agent_attribute('cloud.account.id', nr_account_id)
69
+ segment.add_agent_attribute('cloud.platform', CLOUD_PLATFORM)
70
+ segment.add_agent_attribute('cloud.region', region)
71
+ segment.add_agent_attribute('cloud.resource_id', arn) if arn
72
+ segment
73
+ end
74
+
75
+ def function_name(options = {})
76
+ (options.fetch(:function_name, nil) if options.respond_to?(:fetch)) || NewRelic::UNKNOWN
77
+ end
78
+
79
+ def aws_region
80
+ config&.region if self.respond_to?(:config)
81
+ end
82
+
83
+ def aws_arn(function, region)
84
+ NewRelic::Agent::Aws.create_arn(AWS_SERVICE, "function:#{function}", region, nr_account_id)
85
+ end
86
+
87
+ def nr_account_id
88
+ return @nr_account_id if defined?(@nr_account_id)
89
+
90
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,23 @@
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 'instrumentation'
6
+
7
+ module NewRelic::Agent::Instrumentation
8
+ module AwsSdkLambda::Prepend
9
+ include NewRelic::Agent::Instrumentation::AwsSdkLambda
10
+
11
+ def invoke(*args)
12
+ invoke_with_new_relic(*args) { super }
13
+ end
14
+
15
+ def invoke_async(*args)
16
+ invoke_async_with_new_relic(*args) { super }
17
+ end
18
+
19
+ def invoke_with_response_stream(*args)
20
+ invoke_with_response_stream_with_new_relic(*args) { super }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
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
+ DependencyDetection.defer do
6
+ named :aws_sdk_lambda
7
+
8
+ depends_on do
9
+ defined?(Aws::Lambda::Client)
10
+ end
11
+
12
+ executes do
13
+ require_relative 'aws_sdk_lambda/instrumentation'
14
+
15
+ if use_prepend?
16
+ require_relative 'aws_sdk_lambda/prepend'
17
+ prepend_instrument Aws::Lambda::Client, NewRelic::Agent::Instrumentation::AwsSdkLambda::Prepend
18
+ else
19
+ require_relative 'aws_sdk_lambda/chain'
20
+ chain_instrument NewRelic::Agent::Instrumentation::AwsSdkLambda::Chain
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,37 @@
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 AwsSqs::Chain
7
+ def self.instrument!
8
+ ::Aws::SQS::Client.class_eval do
9
+ include NewRelic::Agent::Instrumentation::AwsSqs
10
+
11
+ alias_method(:send_message_without_new_relic, :send_message)
12
+
13
+ def send_message(*args)
14
+ send_message_with_new_relic(*args) do
15
+ send_message_without_new_relic(*args)
16
+ end
17
+ end
18
+
19
+ alias_method(:send_message_batch_without_new_relic, :send_message_batch)
20
+
21
+ def send_message_batch(*args)
22
+ send_message_batch_with_new_relic(*args) do
23
+ send_message_batch_without_new_relic(*args)
24
+ end
25
+ end
26
+
27
+ alias_method(:receive_message_without_new_relic, :receive_message)
28
+
29
+ def receive_message(*args)
30
+ receive_message_with_new_relic(*args) do
31
+ receive_message_without_new_relic(*args)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,67 @@
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 AwsSqs
7
+ MESSAGING_LIBRARY = 'SQS'
8
+
9
+ def send_message_with_new_relic(*args)
10
+ with_tracing(:produce, args) do
11
+ yield
12
+ end
13
+ end
14
+
15
+ def send_message_batch_with_new_relic(*args)
16
+ with_tracing(:produce, args) do
17
+ yield
18
+ end
19
+ end
20
+
21
+ def receive_message_with_new_relic(*args)
22
+ with_tracing(:consume, args) do
23
+ yield
24
+ end
25
+ end
26
+
27
+ def with_tracing(action, params)
28
+ segment = nil
29
+ begin
30
+ info = get_url_info(params[0])
31
+ segment = NewRelic::Agent::Tracer.start_message_broker_segment(
32
+ action: action,
33
+ library: MESSAGING_LIBRARY,
34
+ destination_type: :queue,
35
+ destination_name: info[:queue_name]
36
+ )
37
+ add_aws_attributes(segment, info)
38
+ rescue => e
39
+ NewRelic::Agent.logger.error('Error starting message broker segment in Aws::SQS::Client', e)
40
+ end
41
+ NewRelic::Agent::Tracer.capture_segment_error(segment) do
42
+ yield
43
+ end
44
+ ensure
45
+ segment&.finish
46
+ end
47
+
48
+ private
49
+
50
+ def add_aws_attributes(segment, info)
51
+ return unless segment
52
+
53
+ segment.add_agent_attribute('messaging.system', 'aws_sqs')
54
+ segment.add_agent_attribute('cloud.region', config&.region)
55
+ segment.add_agent_attribute('cloud.account.id', info[:account_id])
56
+ segment.add_agent_attribute('messaging.destination.name', info[:queue_name])
57
+ end
58
+
59
+ def get_url_info(params)
60
+ split = params[:queue_url].split('/')
61
+ {
62
+ queue_name: split.last,
63
+ account_id: split[-2]
64
+ }
65
+ end
66
+ end
67
+ 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 AwsSqs::Prepend
7
+ include NewRelic::Agent::Instrumentation::AwsSqs
8
+
9
+ def send_message(*args)
10
+ send_message_with_new_relic(*args) { super }
11
+ end
12
+
13
+ def send_message_batch(*args)
14
+ send_message_batch_with_new_relic(*args) { super }
15
+ end
16
+
17
+ def receive_message(*args)
18
+ receive_message_with_new_relic(*args) { super }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
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 'aws_sqs/instrumentation'
6
+ require_relative 'aws_sqs/chain'
7
+ require_relative 'aws_sqs/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :aws_sqs
11
+
12
+ depends_on do
13
+ defined?(Aws::SQS::Client)
14
+ end
15
+
16
+ executes do
17
+ if use_prepend?
18
+ prepend_instrument Aws::SQS::Client, NewRelic::Agent::Instrumentation::AwsSqs::Prepend
19
+ else
20
+ chain_instrument NewRelic::Agent::Instrumentation::AwsSqs::Chain
21
+ end
22
+ end
23
+ end
@@ -48,6 +48,12 @@ module NewRelic
48
48
  correlation_id: opts[:correlation_id],
49
49
  exchange_type: type
50
50
  )
51
+ if segment
52
+ segment.add_agent_attribute('server.address', channel&.connection&.hostname)
53
+ segment.add_agent_attribute('server.port', channel&.connection&.port)
54
+ segment.add_agent_attribute('messaging.destination.name', destination) # for produce, this is exchange name
55
+ segment.add_agent_attribute('messaging.rabbitmq.destination.routing_key', opts[:routing_key])
56
+ end
51
57
  rescue => e
52
58
  NewRelic::Agent.logger.error('Error starting message broker segment in Bunny::Exchange#publish', e)
53
59
  yield
@@ -94,6 +100,14 @@ module NewRelic
94
100
  queue_name: name,
95
101
  start_time: t0
96
102
  )
103
+ if segment
104
+ segment.add_agent_attribute('server.address', channel&.connection&.hostname)
105
+ segment.add_agent_attribute('server.port', channel&.connection&.port)
106
+ segment.add_agent_attribute('messaging.destination.name', name) # for consume, this is queue name
107
+ segment.add_agent_attribute('messaging.destination_publish.name', exch_name)
108
+ segment.add_agent_attribute('message.queueName', name)
109
+ segment.add_agent_attribute('messaging.rabbitmq.destination.routing_key', delivery_info&.routing_key)
110
+ end
97
111
  rescue => e
98
112
  NewRelic::Agent.logger.error('Error starting message broker segment in Bunny::Queue#pop', e)
99
113
  else
@@ -14,7 +14,6 @@ DependencyDetection.defer do
14
14
  end
15
15
 
16
16
  executes do
17
- NewRelic::Agent.logger.info('Installing Bunny instrumentation')
18
17
  require 'new_relic/agent/distributed_tracing/cross_app_tracing'
19
18
  require 'new_relic/agent/messaging'
20
19
  require 'new_relic/agent/transaction/message_broker_segment'
@@ -22,9 +21,9 @@ DependencyDetection.defer do
22
21
 
23
22
  executes do
24
23
  if use_prepend?
25
- prepend_instrument Bunny::Exchange, NewRelic::Agent::Instrumentation::Bunny::Prepend::Exchange
26
- prepend_instrument Bunny::Queue, NewRelic::Agent::Instrumentation::Bunny::Prepend::Queue
27
- prepend_instrument Bunny::Consumer, NewRelic::Agent::Instrumentation::Bunny::Prepend::Consumer
24
+ prepend_instrument Bunny::Exchange, NewRelic::Agent::Instrumentation::Bunny::Prepend::Exchange, 'Bunny::Exchange'
25
+ prepend_instrument Bunny::Queue, NewRelic::Agent::Instrumentation::Bunny::Prepend::Queue, 'Bunny::Queue'
26
+ prepend_instrument Bunny::Consumer, NewRelic::Agent::Instrumentation::Bunny::Prepend::Consumer, 'Bunny::Consumer'
28
27
  else
29
28
  chain_instrument NewRelic::Agent::Instrumentation::Bunny::Chain
30
29
  end
@@ -11,12 +11,11 @@ DependencyDetection.defer do
11
11
 
12
12
  depends_on do
13
13
  defined?(Concurrent) &&
14
+ defined?(Concurrent::VERSION) &&
14
15
  Gem::Version.new(Concurrent::VERSION) >= Gem::Version.new('1.1.5')
15
16
  end
16
17
 
17
18
  executes do
18
- NewRelic::Agent.logger.info('Installing concurrent-ruby instrumentation')
19
-
20
19
  if use_prepend?
21
20
  prepend_instrument(Concurrent::ThreadPoolExecutor, NewRelic::Agent::Instrumentation::ConcurrentRuby::Prepend)
22
21
 
@@ -16,17 +16,16 @@ DependencyDetection.defer do
16
16
  end
17
17
 
18
18
  executes do
19
- NewRelic::Agent.logger.info('Installing Curb instrumentation')
20
19
  require 'new_relic/agent/distributed_tracing/cross_app_tracing'
21
20
  require 'new_relic/agent/http_clients/curb_wrappers'
22
21
  end
23
22
 
24
23
  executes do
25
24
  if use_prepend?
26
- prepend_instrument Curl::Easy, NewRelic::Agent::Instrumentation::Curb::Easy::Prepend
27
- prepend_instrument Curl::Multi, NewRelic::Agent::Instrumentation::Curb::Multi::Prepend
25
+ prepend_instrument Curl::Easy, NewRelic::Agent::Instrumentation::Curb::Easy::Prepend, 'Curb::Easy'
26
+ prepend_instrument Curl::Multi, NewRelic::Agent::Instrumentation::Curb::Multi::Prepend, 'Curb::Multi'
28
27
  else
29
- chain_instrument NewRelic::Agent::Instrumentation::Curb::Chain
28
+ chain_instrument NewRelic::Agent::Instrumentation::Curb::Chain, NewRelic::Agent::Instrumentation::Curb::Multi::INSTRUMENTATION_NAME
30
29
  end
31
30
  end
32
31
  end
@@ -82,10 +82,6 @@ DependencyDetection.defer do
82
82
  defined?(Delayed) && defined?(Delayed::Worker)
83
83
  end
84
84
 
85
- executes do
86
- NewRelic::Agent.logger.info('Installing DelayedJob instrumentation [part 1/2]')
87
- end
88
-
89
85
  executes do
90
86
  if use_prepend?
91
87
  prepend_instrument Delayed::Worker, NewRelic::Agent::Instrumentation::DelayedJob::Prepend
@@ -93,23 +89,4 @@ DependencyDetection.defer do
93
89
  chain_instrument NewRelic::Agent::Instrumentation::DelayedJob::Chain
94
90
  end
95
91
  end
96
-
97
- executes do
98
- next unless delayed_job_version < Gem::Version.new('4.1.0')
99
-
100
- deprecation_msg = 'Instrumentation for DelayedJob versions below 4.1.0 is deprecated.' \
101
- 'It will stop being monitored in version 9.0.0. ' \
102
- 'Please upgrade your DelayedJob version to continue receiving full support. ' \
103
-
104
- NewRelic::Agent.logger.log_once(
105
- :warn,
106
- :deprecated_delayed_job_version,
107
- deprecation_msg
108
- )
109
- end
110
-
111
- def delayed_job_version
112
- # the following line needs else branch coverage
113
- Gem.loaded_specs['delayed_job'].version if Gem.loaded_specs['delayed_job'] # rubocop:disable Style/SafeNavigation
114
- end
115
92
  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
+ module NewRelic::Agent::Instrumentation
6
+ module DynamoDB::Chain
7
+ def self.instrument!
8
+ ::Aws::DynamoDB::Client.class_eval do
9
+ include NewRelic::Agent::Instrumentation::DynamoDB
10
+
11
+ NewRelic::Agent::Instrumentation::DynamoDB::INSTRUMENTED_METHODS.each do |method_name|
12
+ alias_method("#{method_name}_without_new_relic".to_sym, method_name.to_sym)
13
+
14
+ define_method(method_name) do |*args|
15
+ instrument_method_with_new_relic(method_name, *args) { send("#{method_name}_without_new_relic".to_sym, *args) }
16
+ end
17
+ end
18
+
19
+ alias_method(:build_request_without_new_relic, :build_request)
20
+
21
+ def build_request(*args)
22
+ build_request_with_new_relic(*args) { build_request_without_new_relic(*args) }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
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 DynamoDB
7
+ INSTRUMENTED_METHODS = %w[
8
+ create_table
9
+ delete_item
10
+ delete_table
11
+ get_item
12
+ put_item
13
+ query
14
+ scan
15
+ update_item
16
+ ].freeze
17
+
18
+ PRODUCT = 'DynamoDB'
19
+ DEFAULT_HOST = 'dynamodb.amazonaws.com'
20
+
21
+ def instrument_method_with_new_relic(method_name, *args)
22
+ return yield unless NewRelic::Agent::Tracer.tracing_enabled?
23
+
24
+ NewRelic::Agent.record_instrumentation_invocation(PRODUCT)
25
+
26
+ segment = NewRelic::Agent::Tracer.start_datastore_segment(
27
+ product: PRODUCT,
28
+ operation: method_name,
29
+ host: config&.endpoint&.host || DEFAULT_HOST,
30
+ port_path_or_id: config&.endpoint&.port,
31
+ collection: args[0][:table_name]
32
+ )
33
+
34
+ arn = get_arn(args[0])
35
+ segment&.add_agent_attribute('cloud.resource_id', arn) if arn
36
+
37
+ @nr_captured_request = nil # clear request just in case
38
+ begin
39
+ NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
40
+ ensure
41
+ segment&.add_agent_attribute('aws.operation', method_name)
42
+ segment&.add_agent_attribute('aws.requestId', @nr_captured_request&.context&.http_response&.headers&.[]('x-amzn-requestid'))
43
+ segment&.add_agent_attribute('aws.region', config&.region)
44
+ segment&.finish
45
+ end
46
+ end
47
+
48
+ def build_request_with_new_relic(*args)
49
+ @nr_captured_request = yield
50
+ end
51
+
52
+ def nr_account_id
53
+ return @nr_account_id if defined?(@nr_account_id)
54
+
55
+ @nr_account_id = NewRelic::Agent::Aws.get_account_id(config)
56
+ end
57
+
58
+ def get_arn(params)
59
+ return unless params[:table_name]
60
+
61
+ NewRelic::Agent::Aws.create_arn(PRODUCT.downcase, "table/#{params[:table_name]}", config&.region, nr_account_id)
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,19 @@
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 DynamoDB::Prepend
7
+ include NewRelic::Agent::Instrumentation::DynamoDB
8
+
9
+ INSTRUMENTED_METHODS.each do |method_name|
10
+ define_method(method_name) do |*args|
11
+ instrument_method_with_new_relic(method_name, *args) { super(*args) }
12
+ end
13
+ end
14
+
15
+ def build_request(*args)
16
+ build_request_with_new_relic(*args) { super }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
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 'dynamodb/instrumentation'
6
+ require_relative 'dynamodb/chain'
7
+ require_relative 'dynamodb/prepend'
8
+
9
+ DependencyDetection.defer do
10
+ named :dynamodb
11
+
12
+ depends_on do
13
+ defined?(Aws::DynamoDB::Client)
14
+ end
15
+
16
+ executes do
17
+ if use_prepend?
18
+ prepend_instrument Aws::DynamoDB::Client, NewRelic::Agent::Instrumentation::DynamoDB::Prepend
19
+ else
20
+ chain_instrument NewRelic::Agent::Instrumentation::DynamoDB::Chain
21
+ end
22
+ end
23
+ end
@@ -8,9 +8,61 @@ module NewRelic::Agent::Instrumentation
8
8
  module Elasticsearch
9
9
  PRODUCT_NAME = 'Elasticsearch'
10
10
  OPERATION = 'perform_request'
11
+
12
+ # Pattern to use with client caller location strings. Look for a location
13
+ # that contains '/lib/elasticsearch/api/' and is NOT followed by the
14
+ # string held in the OPERATION constant
15
+ OPERATION_PATTERN = %r{/lib/elasticsearch/api/(?!.+#{OPERATION})}.freeze
16
+
17
+ # Use the OPERATION_PATTERN pattern to find the appropriate caller location
18
+ # that will contain the client instance method (example: 'search') and
19
+ # return that method name.
20
+ #
21
+ # A Ruby caller location matching the OPERATION_PATTERN will contain an
22
+ # elasticsearch client instance method name (such as "search"), and that
23
+ # method name will be used as the operation name.
24
+ #
25
+ # With Ruby < 3.4 the method name is listed as:
26
+ #
27
+ # `search'
28
+ #
29
+ # with an opening backtick and a closing single tick. And only the
30
+ # method name itself is listed.
31
+ #
32
+ # With Ruby 3.4+ the method name is listed as:
33
+ #
34
+ # 'Elasticsearch::API::Actions#search'
35
+ #
36
+ # with opening and closing single ticks and the class defining the
37
+ # instance method listed.
38
+ #
39
+ # (?:) = ?: prevents capturing
40
+ # (?:`|') = allow ` or '
41
+ # (?:.+#) = allow the class name and '#' prefix to exist but ignore it
42
+ # ([^']+)' = after the opening ` or ', capturing everything up to the
43
+ # closing '. [^']+ = one or more characters that are not '
44
+ #
45
+ # Example Ruby 3.3.1 input:
46
+ #
47
+ # /Users/fallwith/.rubies/ruby-3.3.1/lib/ruby/gems/3.3.0/gems/elasticsearch-api-7.17.10/lib/elasticsearch/api/actions/index.rb:74:in `index'
48
+ #
49
+ # Example Ruby 3.4.0-preview1 input:
50
+ #
51
+ # /Users/fallwith/.rubies/ruby-3.4.0-preview1/lib/ruby/gems/3.4.0+0/gems/elasticsearch-api-7.17.10/lib/elasticsearch/api/actions/index.rb:74:in 'Elasticsearch::API::Actions#index'
52
+ #
53
+ # Example output for both Rubies:
54
+ #
55
+ # index
56
+
57
+ INSTANCE_METHOD_PATTERN = /:in (?:`|')(?:.+#)?([^']+)'\z/.freeze
58
+
11
59
  INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
12
60
 
13
- def perform_request_with_tracing(method, path, params = {}, body = nil, headers = nil)
61
+ # We need the positional arguments `params` and `body`
62
+ # to capture the nosql statement
63
+ # *args protects the instrumented method if new arguments are added to
64
+ # perform_request
65
+ def perform_request_with_tracing(_method, _path, params = {}, body = nil, _headers = nil, *_args)
14
66
  return yield unless NewRelic::Agent::Tracer.tracing_enabled?
15
67
 
16
68
  NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
@@ -22,6 +74,7 @@ module NewRelic::Agent::Instrumentation
22
74
  port_path_or_id: nr_hosts[:port],
23
75
  database_name: nr_cluster_name
24
76
  )
77
+
25
78
  begin
26
79
  NewRelic::Agent::Tracer.capture_segment_error(segment) { yield }
27
80
  ensure
@@ -35,13 +88,10 @@ module NewRelic::Agent::Instrumentation
35
88
  private
36
89
 
37
90
  def nr_operation
38
- operation_index = caller_locations.index do |line|
39
- string = line.to_s
40
- string.include?('lib/elasticsearch/api') && !string.include?(OPERATION)
41
- end
42
- return nil unless operation_index
91
+ location = caller_locations.detect { |loc| loc.to_s.match?(OPERATION_PATTERN) }
92
+ return unless location && location.to_s =~ INSTANCE_METHOD_PATTERN
43
93
 
44
- caller_locations[operation_index].to_s.split('`')[-1].gsub(/\W/, '')
94
+ Regexp.last_match(1)
45
95
  end
46
96
 
47
97
  def nr_reported_query(query)
@@ -52,7 +102,7 @@ module NewRelic::Agent::Instrumentation
52
102
  end
53
103
 
54
104
  def nr_cluster_name
55
- return @nr_cluster_name if @nr_cluster_name
105
+ return @nr_cluster_name if defined?(@nr_cluster_name)
56
106
  return if nr_hosts.empty?
57
107
 
58
108
  NewRelic::Agent.disable_all_tracing do
@@ -14,8 +14,6 @@ DependencyDetection.defer do
14
14
  end
15
15
 
16
16
  executes do
17
- NewRelic::Agent.logger.info('Installing Elasticsearch instrumentation')
18
-
19
17
  to_instrument = if Gem::Version.create(Elasticsearch::VERSION) < Gem::Version.create('8.0.0')
20
18
  Elasticsearch::Transport::Client
21
19
  else
@@ -21,10 +21,6 @@ DependencyDetection.defer do
21
21
  defined?(Ethon) && Gem::Version.new(Ethon::VERSION) >= Gem::Version.new('0.12.0')
22
22
  end
23
23
 
24
- executes do
25
- NewRelic::Agent.logger.info('Installing ethon instrumentation')
26
- end
27
-
28
24
  executes do
29
25
  if use_prepend?
30
26
  # NOTE: by default prepend_instrument will go with the module name that