newrelic_rpm 9.9.0 → 9.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. checksums.yaml +4 -4
  2. data/.build_ignore +1 -0
  3. data/CHANGELOG.md +463 -1
  4. data/CONTRIBUTING.md +2 -2
  5. data/README.md +16 -17
  6. data/Rakefile +1 -1
  7. data/lib/boot/strap.rb +102 -0
  8. data/lib/new_relic/agent/agent.rb +6 -0
  9. data/lib/new_relic/agent/agent_helpers/connect.rb +3 -0
  10. data/lib/new_relic/agent/agent_helpers/harvest.rb +3 -0
  11. data/lib/new_relic/agent/agent_helpers/shutdown.rb +3 -0
  12. data/lib/new_relic/agent/agent_helpers/start_worker_thread.rb +1 -0
  13. data/lib/new_relic/agent/agent_helpers/startup.rb +7 -0
  14. data/lib/new_relic/agent/agent_logger.rb +1 -0
  15. data/lib/new_relic/agent/aws.rb +68 -0
  16. data/lib/new_relic/agent/configuration/default_source.rb +603 -105
  17. data/lib/new_relic/agent/configuration/environment_source.rb +5 -1
  18. data/lib/new_relic/agent/configuration/manager.rb +28 -2
  19. data/lib/new_relic/agent/configuration/yaml_source.rb +7 -2
  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 +41 -1
  23. data/lib/new_relic/agent/database_adapter.rb +1 -1
  24. data/lib/new_relic/agent/datastores/redis.rb +1 -1
  25. data/lib/new_relic/agent/distributed_tracing/cross_app_tracing.rb +1 -1
  26. data/lib/new_relic/agent/distributed_tracing.rb +4 -2
  27. data/lib/new_relic/agent/error_collector.rb +37 -10
  28. data/lib/new_relic/agent/external.rb +2 -0
  29. data/lib/new_relic/agent/health_check.rb +136 -0
  30. data/lib/new_relic/agent/http_clients/uri_util.rb +1 -1
  31. data/lib/new_relic/agent/instrumentation/action_dispatch.rb +1 -1
  32. data/lib/new_relic/agent/instrumentation/action_dispatch_subscriber.rb +1 -1
  33. data/lib/new_relic/agent/instrumentation/action_mailbox.rb +1 -1
  34. data/lib/new_relic/agent/instrumentation/action_mailer.rb +1 -1
  35. data/lib/new_relic/agent/instrumentation/active_job.rb +1 -1
  36. data/lib/new_relic/agent/instrumentation/active_job_subscriber.rb +6 -2
  37. data/lib/new_relic/agent/instrumentation/active_merchant.rb +0 -13
  38. data/lib/new_relic/agent/instrumentation/active_record.rb +7 -12
  39. data/lib/new_relic/agent/instrumentation/active_record_helper.rb +7 -3
  40. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +11 -9
  41. data/lib/new_relic/agent/instrumentation/active_record_prepend.rb +2 -2
  42. data/lib/new_relic/agent/instrumentation/active_record_subscriber.rb +9 -16
  43. data/lib/new_relic/agent/instrumentation/active_support_broadcast_logger.rb +0 -2
  44. data/lib/new_relic/agent/instrumentation/active_support_logger.rb +0 -2
  45. data/lib/new_relic/agent/instrumentation/async_http.rb +2 -3
  46. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/chain.rb +21 -0
  47. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/instrumentation.rb +66 -0
  48. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose/prepend.rb +15 -0
  49. data/lib/new_relic/agent/instrumentation/aws_sdk_firehose.rb +22 -0
  50. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/chain.rb +21 -0
  51. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/instrumentation.rb +91 -0
  52. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis/prepend.rb +15 -0
  53. data/lib/new_relic/agent/instrumentation/aws_sdk_kinesis.rb +22 -0
  54. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/chain.rb +33 -0
  55. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/instrumentation.rb +93 -0
  56. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda/prepend.rb +23 -0
  57. data/lib/new_relic/agent/instrumentation/aws_sdk_lambda.rb +23 -0
  58. data/lib/new_relic/agent/instrumentation/aws_sqs/chain.rb +37 -0
  59. data/lib/new_relic/agent/instrumentation/aws_sqs/instrumentation.rb +67 -0
  60. data/lib/new_relic/agent/instrumentation/aws_sqs/prepend.rb +21 -0
  61. data/lib/new_relic/agent/instrumentation/aws_sqs.rb +23 -0
  62. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +14 -0
  63. data/lib/new_relic/agent/instrumentation/bunny.rb +3 -4
  64. data/lib/new_relic/agent/instrumentation/concurrent_ruby.rb +1 -3
  65. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +4 -0
  66. data/lib/new_relic/agent/instrumentation/curb.rb +4 -5
  67. data/lib/new_relic/agent/instrumentation/delayed_job_instrumentation.rb +0 -23
  68. data/lib/new_relic/agent/instrumentation/dynamodb/chain.rb +27 -0
  69. data/lib/new_relic/agent/instrumentation/dynamodb/instrumentation.rb +64 -0
  70. data/lib/new_relic/agent/instrumentation/dynamodb/prepend.rb +19 -0
  71. data/lib/new_relic/agent/instrumentation/dynamodb.rb +23 -0
  72. data/lib/new_relic/agent/instrumentation/elasticsearch/chain.rb +1 -2
  73. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +53 -7
  74. data/lib/new_relic/agent/instrumentation/elasticsearch.rb +1 -3
  75. data/lib/new_relic/agent/instrumentation/ethon.rb +1 -5
  76. data/lib/new_relic/agent/instrumentation/excon.rb +1 -17
  77. data/lib/new_relic/agent/instrumentation/fiber/chain.rb +1 -1
  78. data/lib/new_relic/agent/instrumentation/fiber/prepend.rb +1 -1
  79. data/lib/new_relic/agent/instrumentation/fiber.rb +0 -2
  80. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +0 -3
  81. data/lib/new_relic/agent/instrumentation/grape.rb +1 -1
  82. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +0 -1
  83. data/lib/new_relic/agent/instrumentation/httpclient.rb +1 -5
  84. data/lib/new_relic/agent/instrumentation/httprb.rb +0 -1
  85. data/lib/new_relic/agent/instrumentation/httpx/instrumentation.rb +1 -1
  86. data/lib/new_relic/agent/instrumentation/httpx.rb +1 -5
  87. data/lib/new_relic/agent/instrumentation/logger.rb +1 -3
  88. data/lib/new_relic/agent/instrumentation/logstasher/chain.rb +21 -0
  89. data/lib/new_relic/agent/instrumentation/logstasher/instrumentation.rb +24 -0
  90. data/lib/new_relic/agent/instrumentation/logstasher/prepend.rb +13 -0
  91. data/lib/new_relic/agent/instrumentation/logstasher.rb +25 -0
  92. data/lib/new_relic/agent/instrumentation/memcache/dalli.rb +1 -1
  93. data/lib/new_relic/agent/instrumentation/memcache/helper.rb +2 -2
  94. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +1 -1
  95. data/lib/new_relic/agent/instrumentation/memcache/prepend.rb +1 -1
  96. data/lib/new_relic/agent/instrumentation/memcache.rb +0 -1
  97. data/lib/new_relic/agent/instrumentation/mongodb_command_subscriber.rb +1 -1
  98. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +3 -3
  99. data/lib/new_relic/agent/instrumentation/net_http.rb +2 -1
  100. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +0 -2
  101. data/lib/new_relic/agent/instrumentation/opensearch/chain.rb +21 -0
  102. data/lib/new_relic/agent/instrumentation/opensearch/instrumentation.rb +66 -0
  103. data/lib/{tasks/instrumentation_generator/templates/instrumentation.tt → new_relic/agent/instrumentation/opensearch/prepend.rb} +4 -4
  104. data/lib/new_relic/agent/instrumentation/opensearch.rb +23 -0
  105. data/lib/new_relic/agent/instrumentation/padrino.rb +3 -3
  106. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +3 -0
  107. data/lib/new_relic/agent/instrumentation/rails_notifications/action_controller.rb +9 -5
  108. data/lib/new_relic/agent/instrumentation/rake.rb +1 -2
  109. data/lib/new_relic/agent/instrumentation/rdkafka/chain.rb +72 -0
  110. data/lib/new_relic/agent/instrumentation/rdkafka/instrumentation.rb +70 -0
  111. data/lib/new_relic/agent/instrumentation/rdkafka/prepend.rb +67 -0
  112. data/lib/new_relic/agent/instrumentation/rdkafka.rb +25 -0
  113. data/lib/new_relic/agent/instrumentation/redis/cluster_middleware.rb +26 -0
  114. data/lib/new_relic/agent/instrumentation/redis/constants.rb +2 -2
  115. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +14 -11
  116. data/lib/new_relic/agent/instrumentation/redis/middleware.rb +3 -0
  117. data/lib/new_relic/agent/instrumentation/redis.rb +11 -5
  118. data/lib/new_relic/agent/instrumentation/resque.rb +8 -6
  119. data/lib/new_relic/agent/instrumentation/roda.rb +5 -5
  120. data/lib/new_relic/agent/instrumentation/ruby_kafka/chain.rb +55 -0
  121. data/lib/new_relic/agent/instrumentation/ruby_kafka/instrumentation.rb +67 -0
  122. data/lib/new_relic/agent/instrumentation/ruby_kafka/prepend.rb +60 -0
  123. data/lib/new_relic/agent/instrumentation/ruby_kafka.rb +25 -0
  124. data/lib/new_relic/agent/instrumentation/ruby_openai.rb +2 -2
  125. data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delay_extensions.rb +24 -0
  126. data/lib/new_relic/agent/instrumentation/sidekiq/extensions/delayed_class.rb +2 -2
  127. data/lib/new_relic/agent/instrumentation/sidekiq.rb +9 -15
  128. data/lib/new_relic/agent/instrumentation/sinatra.rb +3 -19
  129. data/lib/new_relic/agent/instrumentation/stripe.rb +1 -1
  130. data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +22 -1
  131. data/lib/new_relic/agent/instrumentation/thread.rb +0 -2
  132. data/lib/new_relic/agent/instrumentation/tilt.rb +0 -4
  133. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +2 -2
  134. data/lib/new_relic/agent/instrumentation/typhoeus.rb +0 -1
  135. data/lib/new_relic/agent/instrumentation/view_component/instrumentation.rb +11 -5
  136. data/lib/new_relic/agent/instrumentation/view_component.rb +0 -2
  137. data/lib/new_relic/agent/javascript_instrumentor.rb +2 -3
  138. data/lib/new_relic/agent/llm/chat_completion_summary.rb +1 -1
  139. data/lib/new_relic/agent/llm/embedding.rb +1 -1
  140. data/lib/new_relic/agent/local_log_decorator.rb +20 -3
  141. data/lib/new_relic/agent/log_event_aggregator.rb +119 -28
  142. data/lib/new_relic/agent/logging.rb +1 -1
  143. data/lib/new_relic/agent/messaging.rb +16 -5
  144. data/lib/new_relic/agent/method_tracer.rb +3 -0
  145. data/lib/new_relic/agent/monitors/inbound_request_monitor.rb +1 -1
  146. data/lib/new_relic/agent/monitors/synthetics_monitor.rb +1 -1
  147. data/lib/new_relic/agent/new_relic_service/json_marshaller.rb +2 -2
  148. data/lib/new_relic/agent/new_relic_service.rb +8 -2
  149. data/lib/new_relic/agent/opentelemetry/context/propagation/trace_propagator.rb +66 -0
  150. data/lib/new_relic/agent/opentelemetry/context/propagation.rb +15 -0
  151. data/lib/{tasks/instrumentation_generator/templates/Envfile.tt → new_relic/agent/opentelemetry/context.rb} +9 -5
  152. data/lib/new_relic/agent/opentelemetry/trace/span.rb +31 -0
  153. data/lib/new_relic/agent/opentelemetry/trace/tracer.rb +129 -0
  154. data/lib/new_relic/agent/opentelemetry/trace/tracer_provider.rb +18 -0
  155. data/lib/new_relic/agent/opentelemetry/trace.rb +15 -0
  156. data/lib/new_relic/agent/opentelemetry/transaction_patch.rb +69 -0
  157. data/lib/new_relic/agent/opentelemetry_bridge.rb +32 -0
  158. data/lib/new_relic/agent/parameter_filtering.rb +1 -1
  159. data/lib/new_relic/agent/samplers/cpu_sampler.rb +1 -1
  160. data/lib/new_relic/agent/samplers/memory_sampler.rb +1 -1
  161. data/lib/new_relic/agent/serverless_handler.rb +247 -12
  162. data/lib/new_relic/agent/serverless_handler_event_sources.json +155 -0
  163. data/lib/new_relic/agent/serverless_handler_event_sources.rb +49 -0
  164. data/lib/new_relic/agent/span_event_primitive.rb +16 -11
  165. data/lib/new_relic/agent/system_info.rb +14 -0
  166. data/lib/new_relic/agent/threading/backtrace_node.rb +10 -1
  167. data/lib/new_relic/agent/tracer.rb +1 -1
  168. data/lib/new_relic/agent/transaction/abstract_segment.rb +2 -1
  169. data/lib/new_relic/agent/transaction/datastore_segment.rb +1 -1
  170. data/lib/new_relic/agent/transaction/distributed_tracer.rb +3 -3
  171. data/lib/new_relic/agent/transaction/external_request_segment.rb +0 -10
  172. data/lib/new_relic/agent/transaction/message_broker_segment.rb +4 -1
  173. data/lib/new_relic/agent/transaction/request_attributes.rb +14 -7
  174. data/lib/new_relic/agent/transaction/trace_context.rb +34 -5
  175. data/lib/new_relic/agent/transaction/tracing.rb +3 -3
  176. data/lib/new_relic/agent/transaction.rb +4 -7
  177. data/lib/new_relic/agent/transaction_time_aggregator.rb +1 -1
  178. data/lib/new_relic/agent/utilization/ecs.rb +22 -0
  179. data/lib/new_relic/agent/utilization/ecs_v4.rb +22 -0
  180. data/lib/new_relic/agent/utilization_data.rb +40 -5
  181. data/lib/new_relic/agent/vm/c_ruby_vm.rb +3 -3
  182. data/lib/new_relic/agent.rb +124 -2
  183. data/lib/new_relic/constants.rb +1 -0
  184. data/lib/new_relic/control/frameworks/grape.rb +14 -0
  185. data/lib/new_relic/control/frameworks/padrino.rb +14 -0
  186. data/lib/new_relic/control/frameworks/rails4.rb +1 -3
  187. data/lib/new_relic/control/instance_methods.rb +6 -0
  188. data/lib/new_relic/control/instrumentation.rb +1 -1
  189. data/lib/new_relic/control/private_instance_methods.rb +4 -0
  190. data/lib/new_relic/control/security_interface.rb +57 -0
  191. data/lib/new_relic/control.rb +1 -1
  192. data/lib/new_relic/dependency_detection.rb +11 -14
  193. data/lib/new_relic/environment_report.rb +2 -2
  194. data/lib/new_relic/helper.rb +22 -0
  195. data/lib/new_relic/language_support.rb +3 -1
  196. data/lib/new_relic/local_environment.rb +1 -4
  197. data/lib/new_relic/rack/browser_monitoring.rb +20 -8
  198. data/lib/new_relic/version.rb +1 -1
  199. data/lib/sequel/extensions/new_relic_instrumentation.rb +3 -2
  200. data/lib/tasks/config.rake +7 -3
  201. data/lib/tasks/gha.rake +31 -0
  202. data/lib/tasks/helpers/config.html.erb +3 -2
  203. data/lib/tasks/helpers/format.rb +1 -1
  204. data/lib/tasks/helpers/newrelicyml.rb +80 -13
  205. data/newrelic.yml +425 -162
  206. data/newrelic_rpm.gemspec +3 -1
  207. data/test/agent_helper.rb +24 -2
  208. metadata +91 -22
  209. data/lib/tasks/instrumentation_generator/README.md +0 -63
  210. data/lib/tasks/instrumentation_generator/TODO.md +0 -33
  211. data/lib/tasks/instrumentation_generator/instrumentation.thor +0 -121
  212. data/lib/tasks/instrumentation_generator/templates/chain.tt +0 -21
  213. data/lib/tasks/instrumentation_generator/templates/chain_method.tt +0 -7
  214. data/lib/tasks/instrumentation_generator/templates/dependency_detection.tt +0 -29
  215. data/lib/tasks/instrumentation_generator/templates/instrumentation_method.tt +0 -3
  216. data/lib/tasks/instrumentation_generator/templates/newrelic.yml.tt +0 -19
  217. data/lib/tasks/instrumentation_generator/templates/prepend.tt +0 -13
  218. data/lib/tasks/instrumentation_generator/templates/prepend_method.tt +0 -3
  219. data/lib/tasks/instrumentation_generator/templates/test.tt +0 -15
@@ -10,6 +10,7 @@ module NewRelic
10
10
  EVENT_ATTRIBUTES = %i[http_status method num_retries path request_id].freeze
11
11
  ATTRIBUTE_NAMESPACE = 'stripe.user_data'
12
12
  ATTRIBUTE_FILTER_TYPES = %i[include exclude].freeze
13
+ PATH_PORTION_PATTERN = %r{^/([^/]+/[^/]+)(?:/|\z)}.freeze
13
14
 
14
15
  def start_segment(event)
15
16
  return unless is_execution_traced?
@@ -39,7 +40,27 @@ module NewRelic
39
40
  end
40
41
 
41
42
  def metric_name(event)
42
- "Stripe#{event.path}/#{event.method}"
43
+ # Grab only the first 2 items from the slash (/) delimited event path.
44
+ # These items are the API version string and the category. Grabbing
45
+ # any more of the path will result in unique method names that will
46
+ # easily grow to be too numerous to sort through in the UI and
47
+ # possibly even violate default New Relic metric count thresholds.
48
+ # See newrelic/newrelic-ruby-agent#2654 and
49
+ # newrelic/newrelic-ruby-agent#2709 for more details.
50
+ #
51
+ # In Ruby v3.4 benchmarks, using regex to get at the first two path
52
+ # elements was seen as more performant than using String#split.
53
+ #
54
+ # Regex legend:
55
+ #
56
+ # ^ = starts with
57
+ # / = a literal '/'
58
+ # () = capture
59
+ # (?:) = don't capture
60
+ # [^/]+ = 1 or more characters that are not '/'
61
+ # /|\z = a literal '/' OR the end of the string
62
+ path_portion = event.path =~ PATH_PORTION_PATTERN ? Regexp.last_match(1) : NewRelic::UNKNOWN
63
+ "Stripe/#{path_portion}/#{event.method}"
43
64
  end
44
65
 
45
66
  def add_stripe_attributes(segment, event)
@@ -9,8 +9,6 @@ DependencyDetection.defer do
9
9
  named :thread
10
10
 
11
11
  executes do
12
- NewRelic::Agent.logger.info('Installing Thread Instrumentation')
13
-
14
12
  if use_prepend?
15
13
  prepend_instrument Thread, NewRelic::Agent::Instrumentation::MonitoredThread::Prepend
16
14
  else
@@ -11,10 +11,6 @@ DependencyDetection.defer do
11
11
 
12
12
  depends_on { defined?(Tilt) }
13
13
 
14
- executes do
15
- NewRelic::Agent.logger.info('Installing Tilt instrumentation')
16
- end
17
-
18
14
  executes do
19
15
  if use_prepend?
20
16
  prepend_instrument Tilt::Template, NewRelic::Agent::Instrumentation::Tilt::Prepend
@@ -8,11 +8,11 @@ module NewRelic
8
8
  module Typhoeus
9
9
  HYDRA_SEGMENT_NAME = 'External/Multiple/Typhoeus::Hydra/run'
10
10
  NOTICEABLE_ERROR_CLASS = 'Typhoeus::Errors::TyphoeusError'
11
- EARLIEST_VERSION = Gem::Version.new('0.5.3')
11
+ EARLIEST_VERSION = '0.5.3'
12
12
  INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
13
13
 
14
14
  def self.is_supported_version?
15
- Gem::Version.new(::Typhoeus::VERSION) >= EARLIEST_VERSION
15
+ NewRelic::Helper.version_satisfied?(::Typhoeus::VERSION, '>=', EARLIEST_VERSION)
16
16
  end
17
17
 
18
18
  def self.request_is_hydra_enabled?(request)
@@ -18,7 +18,6 @@ DependencyDetection.defer do
18
18
  end
19
19
 
20
20
  executes do
21
- NewRelic::Agent.logger.info('Installing Typhoeus instrumentation')
22
21
  require 'new_relic/agent/distributed_tracing/cross_app_tracing'
23
22
  require 'new_relic/agent/http_clients/typhoeus_wrappers'
24
23
  end
@@ -10,9 +10,7 @@ module NewRelic::Agent::Instrumentation
10
10
  NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
11
 
12
12
  begin
13
- segment = NewRelic::Agent::Tracer.start_segment(
14
- name: metric_name(self.class.identifier, self.class.name)
15
- )
13
+ segment = NewRelic::Agent::Tracer.start_segment(name: metric_name)
16
14
  yield
17
15
  rescue => e
18
16
  NewRelic::Agent.notice_error(e)
@@ -22,8 +20,16 @@ module NewRelic::Agent::Instrumentation
22
20
  end
23
21
  end
24
22
 
25
- def metric_name(identifier, component)
26
- "View/#{metric_path(identifier)}/#{component}"
23
+ def metric_name
24
+ # ViewComponent determines a component's identifier differently depending on the version
25
+ # https://github.com/ViewComponent/view_component/pull/2153
26
+ component_identifier = defined?(self.class.source_location) ? self.class.source_location : self.class.identifier
27
+
28
+ "View/#{metric_path(component_identifier)}/#{self.class.name}"
29
+ rescue => e
30
+ NewRelic::Agent.logger.error('Error identifying View Component metric name', e)
31
+
32
+ 'View/component'
27
33
  end
28
34
 
29
35
  def metric_path(identifier)
@@ -15,8 +15,6 @@ DependencyDetection.defer do
15
15
  end
16
16
 
17
17
  executes do
18
- NewRelic::Agent.logger.info('Installing ViewComponent instrumentation')
19
-
20
18
  if use_prepend?
21
19
  prepend_instrument ViewComponent::Base, NewRelic::Agent::Instrumentation::ViewComponent::Prepend
22
20
  else
@@ -164,9 +164,8 @@ module NewRelic
164
164
 
165
165
  def add_ssl_for_http(data)
166
166
  ssl_for_http = NewRelic::Agent.config[:'browser_monitoring.ssl_for_http']
167
- unless ssl_for_http.nil?
168
- data[SSL_FOR_HTTP_KEY] = ssl_for_http
169
- end
167
+
168
+ data[SSL_FOR_HTTP_KEY] = ssl_for_http if ssl_for_http
170
169
  end
171
170
 
172
171
  def add_attributes(data, txn)
@@ -32,7 +32,7 @@ module NewRelic
32
32
  # TODO: OLD RUBIES < 2.6
33
33
  # Hash#merge accepts multiple arguments in 2.6
34
34
  # Remove condition once support for Ruby <2.6 is dropped
35
- if RUBY_VERSION >= '2.6.0'
35
+ if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '>=', '2.6.0')
36
36
  LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS, ATTRIBUTE_NAME_EXCEPTIONS)
37
37
  else
38
38
  LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS).merge(ATTRIBUTE_NAME_EXCEPTIONS)
@@ -25,7 +25,7 @@ module NewRelic
25
25
  # TODO: OLD RUBIES < 2.6
26
26
  # Hash#merge accepts multiple arguments in 2.6
27
27
  # Remove condition once support for Ruby <2.6 is dropped
28
- if RUBY_VERSION >= '2.6.0'
28
+ if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '>=', '2.6.0')
29
29
  LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS, ATTRIBUTE_NAME_EXCEPTIONS)
30
30
  else
31
31
  LlmEvent::ATTRIBUTE_NAME_EXCEPTIONS.merge(ResponseHeaders::ATTRIBUTE_NAME_EXCEPTIONS).merge(ATTRIBUTE_NAME_EXCEPTIONS)
@@ -9,9 +9,15 @@ module NewRelic
9
9
  extend self
10
10
 
11
11
  def decorate(message)
12
- return message unless decorating_enabled?
12
+ return message if !decorating_enabled? || message.nil?
13
13
 
14
14
  metadata = NewRelic::Agent.linking_metadata
15
+
16
+ if message.is_a?(Hash)
17
+ message.merge!(metadata) unless message.frozen?
18
+ return
19
+ end
20
+
15
21
  formatted_metadata = " NR-LINKING|#{metadata[ENTITY_GUID_KEY]}|#{metadata[HOSTNAME_KEY]}|" \
16
22
  "#{metadata[TRACE_ID_KEY]}|#{metadata[SPAN_ID_KEY]}|" \
17
23
  "#{escape_entity_name(metadata[ENTITY_NAME_KEY])}|"
@@ -23,14 +29,25 @@ module NewRelic
23
29
 
24
30
  def decorating_enabled?
25
31
  NewRelic::Agent.config[:'application_logging.enabled'] &&
26
- NewRelic::Agent::Instrumentation::Logger.enabled? &&
32
+ (NewRelic::Agent::Instrumentation::Logger.enabled? ||
33
+ NewRelic::Agent::Instrumentation::LogStasher.enabled?) &&
27
34
  NewRelic::Agent.config[:'application_logging.local_decorating.enabled']
28
35
  end
29
36
 
30
37
  def escape_entity_name(entity_name)
31
38
  return unless entity_name
32
39
 
33
- URI::DEFAULT_PARSER.escape(entity_name)
40
+ # TODO: OLD RUBIES 3.3
41
+ # URI version 1.0 marked URI::RFC3986_PARSER.escape as obsolete,
42
+ # which URI::DEFAULT_PARSER is an alias for.
43
+ # URI version 1.0+ will ship with Ruby 3.4
44
+ # Once we drop support for Rubies below 3.4, we can use the
45
+ # URI::RFC2396 parser exclusively.
46
+ if NewRelic::Helper.version_satisfied?(URI::VERSION, '>=', '1.0')
47
+ URI::RFC2396_PARSER.escape(entity_name)
48
+ else
49
+ URI::DEFAULT_PARSER.escape(entity_name)
50
+ end
34
51
  end
35
52
  end
36
53
  end
@@ -20,10 +20,12 @@ module NewRelic
20
20
  DROPPED_METRIC = 'Logging/Forwarding/Dropped'.freeze
21
21
  SEEN_METRIC = 'Supportability/Logging/Forwarding/Seen'.freeze
22
22
  SENT_METRIC = 'Supportability/Logging/Forwarding/Sent'.freeze
23
- OVERALL_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Ruby/Logger/%s'.freeze
23
+ LOGGER_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Ruby/Logger/%s'.freeze
24
+ LOGSTASHER_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Ruby/LogStasher/%s'.freeze
24
25
  METRICS_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Metrics/Ruby/%s'.freeze
25
26
  FORWARDING_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Forwarding/Ruby/%s'.freeze
26
27
  DECORATING_SUPPORTABILITY_FORMAT = 'Supportability/Logging/LocalDecorating/Ruby/%s'.freeze
28
+ LABELS_SUPPORTABILITY_FORMAT = 'Supportability/Logging/Labels/Ruby/%s'.freeze
27
29
  MAX_BYTES = 32768 # 32 * 1024 bytes (32 kibibytes)
28
30
 
29
31
  named :LogEventAggregator
@@ -37,6 +39,7 @@ module NewRelic
37
39
  METRICS_ENABLED_KEY = :'application_logging.metrics.enabled'
38
40
  FORWARDING_ENABLED_KEY = :'application_logging.forwarding.enabled'
39
41
  DECORATING_ENABLED_KEY = :'application_logging.local_decorating.enabled'
42
+ LABELS_ENABLED_KEY = :'application_logging.forwarding.labels.enabled'
40
43
  LOG_LEVEL_KEY = :'application_logging.forwarding.log_level'
41
44
  CUSTOM_ATTRIBUTES_KEY = :'application_logging.forwarding.custom_attributes'
42
45
 
@@ -50,6 +53,7 @@ module NewRelic
50
53
  @high_security = NewRelic::Agent.config[:high_security]
51
54
  @instrumentation_logger_enabled = NewRelic::Agent::Instrumentation::Logger.enabled?
52
55
  @attributes = NewRelic::Agent::LogEventAttributes.new
56
+
53
57
  register_for_done_configuring(events)
54
58
  end
55
59
 
@@ -58,38 +62,71 @@ module NewRelic
58
62
  end
59
63
 
60
64
  def record(formatted_message, severity)
61
- return unless enabled?
65
+ return unless logger_enabled?
62
66
 
63
67
  severity = 'UNKNOWN' if severity.nil? || severity.empty?
68
+ increment_event_counters(severity)
69
+
70
+ return if formatted_message.nil? || formatted_message.empty?
71
+ return unless monitoring_conditions_met?(severity)
72
+
73
+ txn = NewRelic::Agent::Transaction.tl_current
74
+ priority = LogPriority.priority_for(txn)
75
+
76
+ return txn.add_log_event(create_event(priority, formatted_message, severity)) if txn
64
77
 
65
- if NewRelic::Agent.config[METRICS_ENABLED_KEY]
66
- @counter_lock.synchronize do
67
- @seen += 1
68
- @seen_by_severity[severity] += 1
78
+ @lock.synchronize do
79
+ @buffer.append(priority: priority) do
80
+ create_event(priority, formatted_message, severity)
69
81
  end
70
82
  end
83
+ rescue
84
+ nil
85
+ end
71
86
 
72
- return if severity_too_low?(severity)
73
- return if formatted_message.nil? || formatted_message.empty?
74
- return unless NewRelic::Agent.config[FORWARDING_ENABLED_KEY]
75
- return if @high_security
87
+ def record_logstasher_event(log)
88
+ return unless logstasher_enabled?
89
+
90
+ # LogStasher logs do not inherently include a message key, so most logs are recorded.
91
+ # But when the key exists, we should not record the log if the message value is nil or empty.
92
+ return if log.key?('message') && (log['message'].nil? || log['message'].empty?)
93
+
94
+ severity = determine_severity(log)
95
+ increment_event_counters(severity)
96
+
97
+ return unless monitoring_conditions_met?(severity)
76
98
 
77
99
  txn = NewRelic::Agent::Transaction.tl_current
78
100
  priority = LogPriority.priority_for(txn)
79
101
 
80
- if txn
81
- return txn.add_log_event(create_event(priority, formatted_message, severity))
82
- else
83
- return @lock.synchronize do
84
- @buffer.append(priority: priority) do
85
- create_event(priority, formatted_message, severity)
86
- end
102
+ return txn.add_log_event(create_logstasher_event(priority, severity, log)) if txn
103
+
104
+ @lock.synchronize do
105
+ @buffer.append(priority: priority) do
106
+ create_logstasher_event(priority, severity, log)
87
107
  end
88
108
  end
89
109
  rescue
90
110
  nil
91
111
  end
92
112
 
113
+ def monitoring_conditions_met?(severity)
114
+ !severity_too_low?(severity) && NewRelic::Agent.config[FORWARDING_ENABLED_KEY] && !@high_security
115
+ end
116
+
117
+ def determine_severity(log)
118
+ log['level'] ? log['level'].to_s.upcase : 'UNKNOWN'
119
+ end
120
+
121
+ def increment_event_counters(severity)
122
+ return unless NewRelic::Agent.config[METRICS_ENABLED_KEY]
123
+
124
+ @counter_lock.synchronize do
125
+ @seen += 1
126
+ @seen_by_severity[severity] += 1
127
+ end
128
+ end
129
+
93
130
  def record_batch(txn, logs)
94
131
  # Ensure we have the same shared priority
95
132
  priority = LogPriority.priority_for(txn)
@@ -104,15 +141,17 @@ module NewRelic
104
141
  end
105
142
  end
106
143
 
107
- def create_event(priority, formatted_message, severity)
108
- formatted_message = truncate_message(formatted_message)
109
-
110
- event = LinkingMetadata.append_trace_linking_metadata({
144
+ def add_event_metadata(formatted_message, severity)
145
+ metadata = {
111
146
  LEVEL_KEY => severity,
112
- MESSAGE_KEY => formatted_message,
113
147
  TIMESTAMP_KEY => Process.clock_gettime(Process::CLOCK_REALTIME) * 1000
114
- })
148
+ }
149
+ metadata[MESSAGE_KEY] = formatted_message unless formatted_message.nil?
150
+
151
+ LinkingMetadata.append_trace_linking_metadata(metadata)
152
+ end
115
153
 
154
+ def create_prioritized_event(priority, event)
116
155
  [
117
156
  {
118
157
  PrioritySampledBuffer::PRIORITY_KEY => priority
@@ -121,10 +160,39 @@ module NewRelic
121
160
  ]
122
161
  end
123
162
 
163
+ def create_event(priority, formatted_message, severity)
164
+ formatted_message = truncate_message(formatted_message)
165
+ event = add_event_metadata(formatted_message, severity)
166
+
167
+ create_prioritized_event(priority, event)
168
+ end
169
+
170
+ def create_logstasher_event(priority, severity, log)
171
+ formatted_message = log['message'] ? truncate_message(log['message']) : nil
172
+ event = add_event_metadata(formatted_message, severity)
173
+ add_logstasher_event_attributes(event, log)
174
+
175
+ create_prioritized_event(priority, event)
176
+ end
177
+
178
+ def add_logstasher_event_attributes(event, log)
179
+ log_copy = log.dup
180
+ # Delete previously reported attributes
181
+ log_copy.delete('message')
182
+ log_copy.delete('level')
183
+ log_copy.delete('@timestamp')
184
+
185
+ event['attributes'] = log_copy
186
+ end
187
+
124
188
  def add_custom_attributes(custom_attributes)
125
189
  attributes.add_custom_attributes(custom_attributes)
126
190
  end
127
191
 
192
+ def labels
193
+ @labels ||= create_labels
194
+ end
195
+
128
196
  # Because our transmission format (MELT) is different than historical
129
197
  # agent payloads, extract the munging here to keep the service focused
130
198
  # on the general harvest + transmit instead of the format.
@@ -140,8 +208,9 @@ module NewRelic
140
208
  # To save on unnecessary data transmission, trim the entity.type
141
209
  # sent by classic logs-in-context
142
210
  common_attributes.delete(ENTITY_TYPE_KEY)
143
-
144
- common_attributes.merge!(NewRelic::Agent.agent.log_event_aggregator.attributes.custom_attributes)
211
+ aggregator = NewRelic::Agent.agent.log_event_aggregator
212
+ common_attributes.merge!(aggregator.attributes.custom_attributes)
213
+ common_attributes.merge!(aggregator.labels)
145
214
 
146
215
  _, items = data
147
216
  payload = [{
@@ -166,10 +235,14 @@ module NewRelic
166
235
  super
167
236
  end
168
237
 
169
- def enabled?
238
+ def logger_enabled?
170
239
  @enabled && @instrumentation_logger_enabled
171
240
  end
172
241
 
242
+ def logstasher_enabled?
243
+ @enabled && NewRelic::Agent::Instrumentation::LogStasher.enabled?
244
+ end
245
+
173
246
  private
174
247
 
175
248
  # We record once-per-connect metrics for enabled/disabled state at the
@@ -177,11 +250,12 @@ module NewRelic
177
250
  def register_for_done_configuring(events)
178
251
  events.subscribe(:server_source_configuration_added) do
179
252
  @high_security = NewRelic::Agent.config[:high_security]
180
-
181
- record_configuration_metric(OVERALL_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
253
+ record_configuration_metric(LOGGER_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
254
+ record_configuration_metric(LOGSTASHER_SUPPORTABILITY_FORMAT, OVERALL_ENABLED_KEY)
182
255
  record_configuration_metric(METRICS_SUPPORTABILITY_FORMAT, METRICS_ENABLED_KEY)
183
256
  record_configuration_metric(FORWARDING_SUPPORTABILITY_FORMAT, FORWARDING_ENABLED_KEY)
184
257
  record_configuration_metric(DECORATING_SUPPORTABILITY_FORMAT, DECORATING_ENABLED_KEY)
258
+ record_configuration_metric(LABELS_SUPPORTABILITY_FORMAT, LABELS_ENABLED_KEY)
185
259
 
186
260
  add_custom_attributes(NewRelic::Agent.config[CUSTOM_ATTRIBUTES_KEY])
187
261
  end
@@ -262,6 +336,23 @@ module NewRelic
262
336
 
263
337
  Logger::Severity.const_get(severity_constant) < Logger::Severity.const_get(configured_log_level_constant)
264
338
  end
339
+
340
+ def create_labels
341
+ return NewRelic::EMPTY_HASH unless NewRelic::Agent.config[LABELS_ENABLED_KEY]
342
+
343
+ downcased_exclusions = NewRelic::Agent.config[:'application_logging.forwarding.labels.exclude'].map(&:downcase)
344
+ log_labels = {}
345
+
346
+ NewRelic::Agent.config.parsed_labels.each do |parsed_label|
347
+ next if downcased_exclusions.include?(parsed_label['label_type'].downcase)
348
+
349
+ # labels are referred to as tags in the UI, so prefix the
350
+ # label-related attributes with 'tags.*'
351
+ log_labels["tags.#{parsed_label['label_type']}"] = parsed_label['label_value']
352
+ end
353
+
354
+ log_labels
355
+ end
265
356
  end
266
357
  end
267
358
  end
@@ -165,7 +165,7 @@ module NewRelic
165
165
  # Positional and Keyword arguments are separated beginning with Ruby 2.7
166
166
  # Signature of ::Logger constructor changes in Ruby 2.4 to have both positional and keyword args
167
167
  # We pivot on Ruby 2.7 for widest supportability with least amount of hassle.
168
- if RUBY_VERSION < '2.7.0'
168
+ if NewRelic::Helper.version_satisfied?(RUBY_VERSION, '<', '2.7.0')
169
169
  def initialize(*args)
170
170
  super(*args)
171
171
  self.formatter = DecoratingFormatter.new
@@ -49,6 +49,7 @@ module NewRelic
49
49
  #
50
50
  # @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
51
51
  #
52
+ # @!scope class
52
53
  # @api public
53
54
  #
54
55
  def start_message_broker_segment(action: nil,
@@ -107,6 +108,7 @@ module NewRelic
107
108
  # @return return value of given block, which will be the same as the
108
109
  # return value of an un-instrumented subscribed callback
109
110
  #
111
+ # @!scope class
110
112
  # @api public
111
113
  #
112
114
  def wrap_message_broker_consume_transaction(library:,
@@ -117,7 +119,8 @@ module NewRelic
117
119
  queue_name: nil,
118
120
  exchange_type: nil,
119
121
  reply_to: nil,
120
- correlation_id: nil)
122
+ correlation_id: nil,
123
+ action: nil)
121
124
 
122
125
  state = Tracer.state
123
126
  return yield if state.current_transaction
@@ -125,12 +128,12 @@ module NewRelic
125
128
  txn = nil
126
129
 
127
130
  begin
128
- txn_name = transaction_name(library, destination_type, destination_name)
131
+ txn_name = transaction_name(library, destination_type, destination_name, action)
129
132
 
130
133
  txn = Tracer.start_transaction(name: txn_name, category: :message)
131
-
132
134
  if headers
133
- txn.distributed_tracer.consume_message_headers(headers, state, RABBITMQ_TRANSPORT_TYPE)
135
+ NewRelic::Agent::DistributedTracing::accept_distributed_trace_headers(headers, library) # to handle the new w3c headers
136
+ txn.distributed_tracer.consume_message_headers(headers, state, library) # to do the expected old things
134
137
  CrossAppTracing.reject_messaging_cat_headers(headers).each do |k, v|
135
138
  txn.add_agent_attribute(:"message.headers.#{k}", v, AttributeFilter::DST_NONE) unless v.nil?
136
139
  end
@@ -179,6 +182,7 @@ module NewRelic
179
182
  #
180
183
  # @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
181
184
  #
185
+ # @!scope class
182
186
  # @api public
183
187
  #
184
188
  def start_amqp_publish_segment(library:,
@@ -239,6 +243,7 @@ module NewRelic
239
243
  #
240
244
  # @return [NewRelic::Agent::Transaction::MessageBrokerSegment]
241
245
  #
246
+ # @!scope class
242
247
  # @api public
243
248
  #
244
249
  def start_amqp_consume_segment(library:,
@@ -300,6 +305,7 @@ module NewRelic
300
305
  # @return return value of given block, which will be the same as the
301
306
  # return value of an un-instrumented subscribed callback
302
307
  #
308
+ # @!scope class
303
309
  # @api public
304
310
  #
305
311
  def wrap_amqp_consume_transaction(library: nil,
@@ -327,12 +333,17 @@ module NewRelic
327
333
  NewRelic::Agent.config[:'message_tracer.segment_parameters.enabled']
328
334
  end
329
335
 
330
- def transaction_name(library, destination_type, destination_name)
336
+ def transaction_name(library, destination_type, destination_name, action = nil)
331
337
  transaction_name = Transaction::MESSAGE_PREFIX + library
332
338
  transaction_name << NewRelic::SLASH
333
339
  transaction_name << Transaction::MessageBrokerSegment::TYPES[destination_type]
334
340
  transaction_name << NewRelic::SLASH
335
341
 
342
+ if action == :consume
343
+ transaction_name << 'Consume'
344
+ transaction_name << NewRelic::SLASH
345
+ end
346
+
336
347
  case destination_type
337
348
  when :queue
338
349
  transaction_name << Transaction::MessageBrokerSegment::NAMED
@@ -65,6 +65,7 @@ module NewRelic
65
65
  # categories, but generally this *should never ever be done*. Most of the time you can aggregate
66
66
  # on the server.
67
67
  #
68
+ # @!scope class
68
69
  # @api public
69
70
  #
70
71
  def trace_execution_scoped(metric_names, options = NewRelic::EMPTY_HASH) # THREAD_LOCAL_ACCESS
@@ -81,6 +82,7 @@ module NewRelic
81
82
  #
82
83
  # * <tt>metric_names</tt> is a single name or an array of names of metrics
83
84
  #
85
+ # @!scope class
84
86
  # @api public
85
87
  #
86
88
  def trace_execution_unscoped(metric_names, options = NewRelic::EMPTY_HASH) # THREAD_LOCAL_ACCESS
@@ -241,6 +243,7 @@ module NewRelic
241
243
  # # Instrument foo in transaction traces only
242
244
  # add_method_tracer :foo, 'Custom/foo', :metric => false
243
245
  #
246
+ # @!scope class
244
247
  # @api public
245
248
  #
246
249
  def add_method_tracer(method_name, metric_name = nil, options = {})
@@ -32,7 +32,7 @@ module NewRelic
32
32
 
33
33
  def deserialize_header(encoded_header, key)
34
34
  decoded_header = obfuscator.deobfuscate(encoded_header)
35
- ::JSON.load(decoded_header)
35
+ ::JSON.parse(decoded_header)
36
36
  rescue => err
37
37
  # If we have a failure of any type here, just return nil and carry on
38
38
  NewRelic::Agent.logger.debug("Failure deserializing encoded header '#{key}' in #{self.class}, #{err.class}, #{err.message}")
@@ -35,7 +35,7 @@ module NewRelic
35
35
  end
36
36
 
37
37
  def load_json(header, key)
38
- ::JSON.load(header)
38
+ ::JSON.parse(header)
39
39
  rescue => err
40
40
  NewRelic::Agent.logger.debug("Failure loading json header '#{key}' in #{self.class}, #{err.class}, #{err.message}")
41
41
  nil
@@ -19,7 +19,7 @@ module NewRelic
19
19
  def warn_for_yajl
20
20
  if defined?(::Yajl)
21
21
  require 'yajl/version'
22
- if Gem::Version.new(::Yajl::VERSION) < OK_YAJL_VERSION
22
+ if NewRelic::Helper.version_satisfied?(::Yajl::VERSION, '<', OK_YAJL_VERSION)
23
23
  ::NewRelic::Agent.logger.warn("Detected yajl-ruby version #{::Yajl::VERSION} which can cause segfaults with newrelic_rpm's thread profiling features. We strongly recommend you upgrade to the latest yajl-ruby version available.")
24
24
  end
25
25
  end
@@ -42,7 +42,7 @@ module NewRelic
42
42
  return nil
43
43
  end
44
44
 
45
- return_value(::JSON.load(data))
45
+ return_value(::JSON.parse(data))
46
46
  rescue => e
47
47
  ::NewRelic::Agent.logger.debug("#{e.class.name} : #{e.message} encountered loading collector response: #{data}")
48
48
  raise
@@ -455,6 +455,8 @@ module NewRelic
455
455
  end
456
456
 
457
457
  def handle_error_response(response, endpoint)
458
+ NewRelic::Agent.agent&.health_check&.update_status(NewRelic::Agent::HealthCheck::HTTP_ERROR, [response.code, endpoint])
459
+
458
460
  case response
459
461
  when Net::HTTPRequestTimeOut,
460
462
  Net::HTTPTooManyRequests,
@@ -637,9 +639,13 @@ module NewRelic
637
639
  def send_request(opts)
638
640
  request = prep_request(opts)
639
641
  response = relay_request(request, opts)
640
- return response if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
641
642
 
642
- handle_error_response(response, opts[:endpoint])
643
+ if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPAccepted)
644
+ NewRelic::Agent.agent&.health_check&.update_status(NewRelic::Agent::HealthCheck::HEALTHY)
645
+ response
646
+ else
647
+ handle_error_response(response, opts[:endpoint])
648
+ end
643
649
  end
644
650
 
645
651
  def log_response(response)