newrelic_rpm 9.2.0 → 9.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.build_ignore +26 -0
  3. data/CHANGELOG.md +197 -2
  4. data/README.md +8 -4
  5. data/lib/new_relic/agent/attribute_pre_filtering.rb +109 -0
  6. data/lib/new_relic/agent/configuration/default_source.rb +218 -63
  7. data/lib/new_relic/agent/configuration/environment_source.rb +1 -1
  8. data/lib/new_relic/agent/configuration/manager.rb +14 -0
  9. data/lib/new_relic/agent/configuration/yaml_source.rb +13 -0
  10. data/lib/new_relic/agent/distributed_tracing.rb +1 -1
  11. data/lib/new_relic/agent/error_collector.rb +1 -1
  12. data/lib/new_relic/agent/instrumentation/action_controller_other_subscriber.rb +1 -1
  13. data/lib/new_relic/agent/instrumentation/active_record.rb +1 -1
  14. data/lib/new_relic/agent/instrumentation/active_record_notifications.rb +2 -1
  15. data/lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb +4 -0
  16. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +9 -0
  17. data/lib/new_relic/agent/instrumentation/concurrent_ruby/chain.rb +1 -1
  18. data/lib/new_relic/agent/instrumentation/concurrent_ruby/instrumentation.rb +3 -4
  19. data/lib/new_relic/agent/instrumentation/concurrent_ruby/prepend.rb +1 -1
  20. data/lib/new_relic/agent/instrumentation/controller_instrumentation.rb +1 -2
  21. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +4 -0
  22. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +3 -0
  23. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +4 -1
  24. data/lib/new_relic/agent/instrumentation/excon/middleware.rb +3 -0
  25. data/lib/new_relic/agent/instrumentation/fiber/chain.rb +10 -3
  26. data/lib/new_relic/agent/instrumentation/fiber/instrumentation.rb +1 -2
  27. data/lib/new_relic/agent/instrumentation/fiber/prepend.rb +10 -3
  28. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +4 -0
  29. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +4 -0
  30. data/lib/new_relic/agent/instrumentation/grpc/server/instrumentation.rb +4 -0
  31. data/lib/new_relic/agent/instrumentation/grpc_client.rb +1 -1
  32. data/lib/new_relic/agent/instrumentation/grpc_server.rb +1 -1
  33. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +4 -0
  34. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +4 -0
  35. data/lib/new_relic/agent/instrumentation/logger/instrumentation.rb +3 -0
  36. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +12 -3
  37. data/lib/new_relic/agent/instrumentation/memcache.rb +2 -2
  38. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +4 -0
  39. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +4 -0
  40. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +4 -0
  41. data/lib/new_relic/agent/instrumentation/queue_time.rb +1 -1
  42. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +6 -0
  43. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +4 -0
  44. data/lib/new_relic/agent/instrumentation/rails_notifications/action_cable.rb +1 -1
  45. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +4 -0
  46. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +4 -0
  47. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +4 -0
  48. data/lib/new_relic/agent/instrumentation/roda/chain.rb +43 -0
  49. data/lib/new_relic/agent/instrumentation/roda/instrumentation.rb +56 -0
  50. data/lib/new_relic/agent/instrumentation/roda/prepend.rb +24 -0
  51. data/lib/new_relic/agent/instrumentation/roda/roda_transaction_namer.rb +30 -0
  52. data/lib/new_relic/agent/instrumentation/roda.rb +34 -0
  53. data/lib/new_relic/agent/instrumentation/sequel.rb +1 -1
  54. data/lib/new_relic/agent/instrumentation/sidekiq/client.rb +4 -0
  55. data/lib/new_relic/agent/instrumentation/sidekiq/server.rb +26 -3
  56. data/lib/new_relic/agent/instrumentation/sidekiq.rb +2 -2
  57. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +4 -0
  58. data/lib/new_relic/agent/instrumentation/stripe.rb +28 -0
  59. data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +77 -0
  60. data/lib/new_relic/agent/instrumentation/thread/chain.rb +1 -1
  61. data/lib/new_relic/agent/instrumentation/thread/instrumentation.rb +0 -1
  62. data/lib/new_relic/agent/instrumentation/thread/prepend.rb +1 -1
  63. data/lib/new_relic/agent/instrumentation/tilt/instrumentation.rb +4 -0
  64. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +5 -1
  65. data/lib/new_relic/agent/log_event_aggregator.rb +49 -2
  66. data/lib/new_relic/agent/log_event_attributes.rb +115 -0
  67. data/lib/new_relic/agent/logging.rb +4 -4
  68. data/lib/new_relic/agent/method_tracer_helpers.rb +26 -5
  69. data/lib/new_relic/agent/new_relic_service.rb +33 -17
  70. data/lib/new_relic/agent/pipe_service.rb +1 -1
  71. data/lib/new_relic/agent/tracer.rb +7 -6
  72. data/lib/new_relic/agent/transaction/abstract_segment.rb +52 -0
  73. data/lib/new_relic/agent/transaction/request_attributes.rb +45 -7
  74. data/lib/new_relic/agent/transaction/tracing.rb +6 -0
  75. data/lib/new_relic/agent/transaction.rb +9 -4
  76. data/lib/new_relic/agent/utilization/vendor.rb +5 -7
  77. data/lib/new_relic/agent.rb +50 -1
  78. data/lib/new_relic/cli/command.rb +1 -0
  79. data/lib/new_relic/control/class_methods.rb +1 -7
  80. data/lib/new_relic/control/frameworks/roda.rb +20 -0
  81. data/lib/new_relic/control/instrumentation.rb +0 -14
  82. data/lib/new_relic/dependency_detection.rb +16 -1
  83. data/lib/new_relic/language_support.rb +5 -0
  84. data/lib/new_relic/latest_changes.rb +1 -1
  85. data/lib/new_relic/noticed_error.rb +5 -2
  86. data/lib/new_relic/rack/agent_hooks.rb +1 -1
  87. data/lib/new_relic/rack/agent_middleware.rb +0 -16
  88. data/lib/new_relic/rack/browser_monitoring.rb +1 -1
  89. data/lib/new_relic/supportability_helper.rb +2 -0
  90. data/lib/new_relic/traced_thread.rb +2 -3
  91. data/lib/new_relic/version.rb +1 -1
  92. data/lib/sequel/extensions/new_relic_instrumentation.rb +1 -1
  93. data/lib/tasks/bump_version.rake +21 -0
  94. data/lib/tasks/config.rake +3 -2
  95. data/lib/tasks/helpers/config.html.erb +93 -0
  96. data/lib/tasks/helpers/format.rb +11 -7
  97. data/lib/tasks/helpers/newrelicyml.rb +144 -0
  98. data/lib/tasks/helpers/version_bump.rb +62 -0
  99. data/lib/tasks/newrelicyml.rake +13 -0
  100. data/newrelic.yml +364 -267
  101. data/newrelic_rpm.gemspec +11 -7
  102. metadata +36 -25
  103. data/.gitignore +0 -43
  104. data/.project +0 -23
  105. data/.rubocop.yml +0 -1845
  106. data/.rubocop_todo.yml +0 -61
  107. data/.simplecov +0 -16
  108. data/.snyk +0 -11
  109. data/.yardopts +0 -27
  110. data/Brewfile +0 -13
  111. data/DOCKER.md +0 -167
  112. data/Dockerfile +0 -10
  113. data/Guardfile +0 -27
  114. data/config/database.yml +0 -5
  115. data/config.dot +0 -278
  116. data/docker-compose.yml +0 -107
  117. data/lefthook.yml +0 -9
  118. data/lib/tasks/helpers/removers.rb +0 -33
  119. data/lib/tasks/multiverse.rake +0 -6
  120. data/lib/tasks/multiverse.rb +0 -84
@@ -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,43 @@
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 Chain
8
+ def self.instrument!
9
+ ::Roda.class_eval do
10
+ include ::NewRelic::Agent::Instrumentation::Roda::Tracer
11
+
12
+ alias_method(:_roda_handle_main_route_without_tracing, :_roda_handle_main_route)
13
+
14
+ def _roda_handle_main_route(*args)
15
+ _roda_handle_main_route_with_tracing(*args) do
16
+ _roda_handle_main_route_without_tracing(*args)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ module Build
24
+ module Chain
25
+ def self.instrument!
26
+ ::Roda.class_eval do
27
+ include ::NewRelic::Agent::Instrumentation::Roda::Tracer
28
+
29
+ class << self
30
+ alias_method(:build_rack_app_without_tracing, :build_rack_app)
31
+
32
+ def build_rack_app
33
+ build_rack_app_with_tracing do
34
+ build_rack_app_without_tracing
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,56 @@
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 Tracer
8
+ include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
9
+
10
+ INSTRUMENTATION_NAME = 'Roda'
11
+
12
+ def self.included(clazz)
13
+ clazz.extend(self)
14
+ end
15
+
16
+ def newrelic_middlewares
17
+ middlewares = [NewRelic::Rack::BrowserMonitoring]
18
+ if NewRelic::Rack::AgentHooks.needed?
19
+ middlewares << NewRelic::Rack::AgentHooks
20
+ end
21
+ middlewares
22
+ end
23
+
24
+ def build_rack_app_with_tracing
25
+ unless NewRelic::Agent.config[:disable_roda_auto_middleware]
26
+ newrelic_middlewares.each do |middleware_class|
27
+ self.use middleware_class
28
+ end
29
+ end
30
+ yield
31
+ end
32
+
33
+ # Roda makes use of Rack, so we can get params from the request object
34
+ def rack_request_params
35
+ begin
36
+ @_request.params
37
+ rescue => e
38
+ NewRelic::Agent.logger.debug('Failed to get params from Rack request.', e)
39
+ NewRelic::EMPTY_HASH
40
+ end
41
+ end
42
+
43
+ def _roda_handle_main_route_with_tracing(*args)
44
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
45
+
46
+ perform_action_with_newrelic_trace(
47
+ category: :roda,
48
+ name: ::NewRelic::Agent::Instrumentation::Roda::TransactionNamer.transaction_name(request),
49
+ params: ::NewRelic::Agent::ParameterFiltering::apply_filters(request.env, rack_request_params)
50
+ ) do
51
+ yield
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,24 @@
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 Prepend
8
+ include ::NewRelic::Agent::Instrumentation::Roda::Tracer
9
+
10
+ def _roda_handle_main_route(*args)
11
+ _roda_handle_main_route_with_tracing(*args) { super }
12
+ end
13
+ end
14
+
15
+ module Build
16
+ module Prepend
17
+ include ::NewRelic::Agent::Instrumentation::Roda::Tracer
18
+ def build_rack_app
19
+ build_rack_app_with_tracing { super }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,30 @@
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
6
+ module Agent
7
+ module Instrumentation
8
+ module Roda
9
+ module TransactionNamer
10
+ extend self
11
+
12
+ ROOT = '/'.freeze
13
+ REGEX_MULTIPLE_SLASHES = %r{^[/^]*(.*?)[/$?]*$}.freeze
14
+
15
+ def transaction_name(request)
16
+ path = request.path || ::NewRelic::Agent::UNKNOWN_METRIC
17
+ name = path.gsub(REGEX_MULTIPLE_SLASHES, '\1') # remove any rogue slashes
18
+ name = ROOT if name.empty?
19
+ name = "#{request.request_method} #{name}" if request.respond_to?(:request_method)
20
+
21
+ name
22
+ rescue => e
23
+ ::NewRelic::Agent.logger.debug("#{e.class} : #{e.message} - Error encountered trying to identify Roda transaction name")
24
+ ::NewRelic::Agent::UNKNOWN_METRIC
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
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 'roda/instrumentation'
6
+ require_relative 'roda/roda_transaction_namer'
7
+
8
+ DependencyDetection.defer do
9
+ named :roda
10
+
11
+ depends_on do
12
+ defined?(Roda) &&
13
+ Gem::Version.new(Roda::RodaVersion) >= Gem::Version.new('3.19.0') &&
14
+ Roda::RodaPlugins::Base::ClassMethods.private_method_defined?(:build_rack_app) &&
15
+ Roda::RodaPlugins::Base::InstanceMethods.method_defined?(:_roda_handle_main_route)
16
+ end
17
+
18
+ executes do
19
+ require_relative '../../rack/agent_hooks'
20
+ require_relative '../../rack/browser_monitoring'
21
+
22
+ NewRelic::Agent.logger.info('Installing Roda instrumentation')
23
+
24
+ if use_prepend?
25
+ require_relative 'roda/prepend'
26
+ prepend_instrument Roda.singleton_class, NewRelic::Agent::Instrumentation::Roda::Build::Prepend
27
+ prepend_instrument Roda, NewRelic::Agent::Instrumentation::Roda::Prepend
28
+ else
29
+ require_relative 'roda/chain'
30
+ chain_instrument NewRelic::Agent::Instrumentation::Roda::Build::Chain
31
+ chain_instrument NewRelic::Agent::Instrumentation::Roda::Chain
32
+ end
33
+ end
34
+ end
@@ -30,7 +30,7 @@ DependencyDetection.defer do
30
30
  else
31
31
  NewRelic::Agent.logger.info('Detected Sequel version %s.' % [Sequel::VERSION])
32
32
  NewRelic::Agent.logger.info('Please see additional documentation: ' +
33
- 'https://newrelic.com/docs/ruby/sequel-instrumentation')
33
+ 'https://docs.newrelic.com/docs/apm/agents/ruby-agent/frameworks/sequel-instrumentation/')
34
34
  end
35
35
 
36
36
  Sequel.synchronize { Sequel::DATABASES.dup }.each do |db|
@@ -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
@@ -7,9 +7,16 @@ module NewRelic::Agent::Instrumentation::Sidekiq
7
7
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
8
8
  include Sidekiq::ServerMiddleware if defined?(Sidekiq::ServerMiddleware)
9
9
 
10
+ ATTRIBUTE_BASE_NAMESPACE = 'sidekiq.args'
11
+ ATTRIBUTE_FILTER_TYPES = %i[include exclude].freeze
12
+ ATTRIBUTE_JOB_NAMESPACE = :"job.#{ATTRIBUTE_BASE_NAMESPACE}"
13
+ INSTRUMENTATION_NAME = 'SidekiqServer'
14
+
10
15
  # Client middleware has additional parameters, and our tests use the
11
16
  # middleware client-side to work inline.
12
17
  def call(worker, msg, queue, *_)
18
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
19
+
13
20
  trace_args = if worker.respond_to?(:newrelic_trace_args)
14
21
  worker.newrelic_trace_args(msg, queue)
15
22
  else
@@ -18,10 +25,16 @@ module NewRelic::Agent::Instrumentation::Sidekiq
18
25
  trace_headers = msg.delete(NewRelic::NEWRELIC_KEY)
19
26
 
20
27
  perform_action_with_newrelic_trace(trace_args) do
21
- NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(msg['args'], :'job.sidekiq.args',
22
- NewRelic::Agent::AttributeFilter::DST_NONE)
28
+ NewRelic::Agent::Transaction.merge_untrusted_agent_attributes(
29
+ NewRelic::Agent::AttributePreFiltering.pre_filter(msg['args'], self.class.nr_attribute_options),
30
+ ATTRIBUTE_JOB_NAMESPACE,
31
+ NewRelic::Agent::AttributeFilter::DST_NONE
32
+ )
33
+
34
+ if ::NewRelic::Agent.config[:'distributed_tracing.enabled'] && trace_headers&.any?
35
+ ::NewRelic::Agent::DistributedTracing::accept_distributed_trace_headers(trace_headers, 'Other')
36
+ end
23
37
 
24
- ::NewRelic::Agent::DistributedTracing::accept_distributed_trace_headers(trace_headers, 'Other') if ::NewRelic::Agent.config[:'distributed_tracing.enabled'] && trace_headers&.any?
25
38
  yield
26
39
  end
27
40
  end
@@ -33,5 +46,15 @@ module NewRelic::Agent::Instrumentation::Sidekiq
33
46
  :category => 'OtherTransaction/SidekiqJob'
34
47
  }
35
48
  end
49
+
50
+ def self.nr_attribute_options
51
+ @nr_attribute_options ||= begin
52
+ ATTRIBUTE_FILTER_TYPES.each_with_object({}) do |type, opts|
53
+ pattern =
54
+ NewRelic::Agent::AttributePreFiltering.formulate_regexp_union(:"#{ATTRIBUTE_BASE_NAMESPACE}.#{type}")
55
+ opts[type] = pattern if pattern
56
+ end.merge(attribute_namespace: ATTRIBUTE_JOB_NAMESPACE)
57
+ end
58
+ end
36
59
  end
37
60
  end
@@ -43,8 +43,8 @@ DependencyDetection.defer do
43
43
  executes do
44
44
  next unless Gem::Version.new(Sidekiq::VERSION) < Gem::Version.new('5.0.0')
45
45
 
46
- deprecation_msg = 'Instrumentation for Sidekiq versions below 5.0.0 is deprecated.' \
47
- 'They will stop being monitored in version 9.0.0. ' \
46
+ deprecation_msg = 'Instrumentation for Sidekiq versions below 5.0.0 is deprecated ' \
47
+ 'and will be dropped entirely in a future major New Relic Ruby agent release.' \
48
48
  'Please upgrade your Sidekiq version to continue receiving full support. '
49
49
 
50
50
  NewRelic::Agent.logger.log_once(
@@ -13,6 +13,8 @@ module NewRelic::Agent::Instrumentation
13
13
  module Tracer
14
14
  include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
15
15
 
16
+ INSTRUMENTATION_NAME = 'Sinatra'
17
+
16
18
  def self.included(clazz)
17
19
  clazz.extend(self)
18
20
  end
@@ -90,6 +92,8 @@ module NewRelic::Agent::Instrumentation
90
92
  end
91
93
 
92
94
  def dispatch_with_tracing
95
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
96
+
93
97
  request_params = get_request_params
94
98
  filtered_params = ::NewRelic::Agent::ParameterFiltering::apply_filters(request.env, request_params || {})
95
99
 
@@ -0,0 +1,28 @@
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/instrumentation/stripe_subscriber'
6
+
7
+ DependencyDetection.defer do
8
+ named :stripe
9
+
10
+ depends_on do
11
+ NewRelic::Agent.config[:'instrumentation.stripe'] == 'enabled'
12
+ end
13
+
14
+ depends_on do
15
+ defined?(Stripe) &&
16
+ Gem::Version.new(Stripe::VERSION) >= Gem::Version.new('5.38.0')
17
+ end
18
+
19
+ executes do
20
+ NewRelic::Agent.logger.info('Installing Stripe instrumentation')
21
+ end
22
+
23
+ executes do
24
+ newrelic_subscriber = NewRelic::Agent::Instrumentation::StripeSubscriber.new
25
+ Stripe::Instrumentation.subscribe(:request_begin) { |event| newrelic_subscriber.start_segment(event) }
26
+ Stripe::Instrumentation.subscribe(:request_end) { |event| newrelic_subscriber.finish_segment(event) }
27
+ end
28
+ end
@@ -0,0 +1,77 @@
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
6
+ module Agent
7
+ module Instrumentation
8
+ class StripeSubscriber
9
+ DEFAULT_DESTINATIONS = AttributeFilter::DST_SPAN_EVENTS
10
+ EVENT_ATTRIBUTES = %i[http_status method num_retries path request_id].freeze
11
+ ATTRIBUTE_NAMESPACE = 'stripe.user_data'
12
+ ATTRIBUTE_FILTER_TYPES = %i[include exclude].freeze
13
+
14
+ def start_segment(event)
15
+ return unless is_execution_traced?
16
+
17
+ segment = NewRelic::Agent::Tracer.start_segment(name: metric_name(event))
18
+ event.user_data[:newrelic_segment] = segment
19
+ rescue => e
20
+ NewRelic::Agent.logger.error("Error starting New Relic Stripe segment: #{e}")
21
+ end
22
+
23
+ def finish_segment(event)
24
+ return unless is_execution_traced?
25
+
26
+ segment = remove_and_return_nr_segment(event)
27
+ add_stripe_attributes(segment, event)
28
+ add_custom_attributes(segment, event)
29
+ rescue => e
30
+ NewRelic::Agent.logger.error("Error finishing New Relic Stripe segment: #{e}")
31
+ ensure
32
+ segment&.finish
33
+ end
34
+
35
+ private
36
+
37
+ def is_execution_traced?
38
+ NewRelic::Agent::Tracer.state.is_execution_traced?
39
+ end
40
+
41
+ def metric_name(event)
42
+ "Stripe#{event.path}/#{event.method}"
43
+ end
44
+
45
+ def add_stripe_attributes(segment, event)
46
+ EVENT_ATTRIBUTES.each do |attribute|
47
+ segment.add_agent_attribute("stripe_#{attribute}", event.send(attribute), DEFAULT_DESTINATIONS)
48
+ end
49
+ end
50
+
51
+ def add_custom_attributes(segment, event)
52
+ return if NewRelic::Agent.config[:'stripe.user_data.include'].empty?
53
+
54
+ filtered_attributes = NewRelic::Agent::AttributePreFiltering.pre_filter_hash(event.user_data, nr_attribute_options)
55
+ filtered_attributes.each do |key, value|
56
+ segment.add_agent_attribute("stripe_user_data_#{key}", value, DEFAULT_DESTINATIONS)
57
+ end
58
+ end
59
+
60
+ def nr_attribute_options
61
+ ATTRIBUTE_FILTER_TYPES.each_with_object({}) do |type, opts|
62
+ pattern =
63
+ NewRelic::Agent::AttributePreFiltering.formulate_regexp_union(:"#{ATTRIBUTE_NAMESPACE}.#{type}")
64
+ opts[type] = pattern if pattern
65
+ end
66
+ end
67
+
68
+ def remove_and_return_nr_segment(event)
69
+ segment = event.user_data[:newrelic_segment]
70
+ event.user_data.delete(:newrelic_segment)
71
+
72
+ segment
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -14,7 +14,7 @@ module NewRelic::Agent::Instrumentation
14
14
  alias_method(:initialize_without_new_relic, :initialize)
15
15
 
16
16
  def initialize(*args, &block)
17
- traced_block = add_thread_tracing(*args, &block)
17
+ traced_block = add_thread_tracing(&block)
18
18
  initialize_with_newrelic_tracing { initialize_without_new_relic(*args, &traced_block) }
19
19
  end
20
20
  end
@@ -17,7 +17,6 @@ module NewRelic
17
17
  return block if !NewRelic::Agent::Tracer.thread_tracing_enabled?
18
18
 
19
19
  NewRelic::Agent::Tracer.thread_block_with_current_transaction(
20
- *args,
21
20
  segment_name: 'Ruby/Thread',
22
21
  &block
23
22
  )
@@ -12,7 +12,7 @@ module NewRelic
12
12
  include NewRelic::Agent::Instrumentation::MonitoredThread
13
13
 
14
14
  def initialize(*args, &block)
15
- traced_block = add_thread_tracing(*args, &block)
15
+ traced_block = add_thread_tracing(&block)
16
16
  initialize_with_newrelic_tracing { super(*args, &traced_block) }
17
17
  end
18
18
  end
@@ -6,6 +6,8 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module Tilt
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  def metric_name(klass, file)
10
12
  "View/#{klass}/#{file}/Rendering"
11
13
  end
@@ -21,6 +23,8 @@ module NewRelic
21
23
  end
22
24
 
23
25
  def render_with_tracing(*args, &block)
26
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
27
+
24
28
  begin
25
29
  finishable = Tracer.start_segment(
26
30
  name: metric_name(self.class, create_filename_for_metric(self.file))
@@ -8,8 +8,8 @@ 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
-
12
11
  EARLIEST_VERSION = Gem::Version.new('0.5.3')
12
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
13
13
 
14
14
  def self.is_supported_version?
15
15
  Gem::Version.new(::Typhoeus::VERSION) >= EARLIEST_VERSION
@@ -31,6 +31,8 @@ module NewRelic
31
31
  end
32
32
 
33
33
  def with_tracing
34
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
35
+
34
36
  segment = NewRelic::Agent::Tracer.start_segment(name: HYDRA_SEGMENT_NAME)
35
37
  instance_variable_set(:@__newrelic_hydra_segment, segment)
36
38
  begin
@@ -41,6 +43,8 @@ module NewRelic
41
43
  end
42
44
 
43
45
  def self.trace(request)
46
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
47
+
44
48
  state = NewRelic::Agent::Tracer.state
45
49
  return unless state.is_execution_traced?
46
50
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'new_relic/agent/event_aggregator'
6
6
  require 'new_relic/agent/log_priority'
7
+ require 'new_relic/agent/log_event_attributes'
7
8
 
8
9
  module NewRelic
9
10
  module Agent
@@ -36,6 +37,10 @@ module NewRelic
36
37
  METRICS_ENABLED_KEY = :'application_logging.metrics.enabled'
37
38
  FORWARDING_ENABLED_KEY = :'application_logging.forwarding.enabled'
38
39
  DECORATING_ENABLED_KEY = :'application_logging.local_decorating.enabled'
40
+ LOG_LEVEL_KEY = :'application_logging.forwarding.log_level'
41
+ CUSTOM_ATTRIBUTES_KEY = :'application_logging.forwarding.custom_attributes'
42
+
43
+ attr_reader :attributes
39
44
 
40
45
  def initialize(events)
41
46
  super(events)
@@ -44,6 +49,7 @@ module NewRelic
44
49
  @seen_by_severity = Hash.new(0)
45
50
  @high_security = NewRelic::Agent.config[:high_security]
46
51
  @instrumentation_logger_enabled = NewRelic::Agent::Instrumentation::Logger.enabled?
52
+ @attributes = NewRelic::Agent::LogEventAttributes.new
47
53
  register_for_done_configuring(events)
48
54
  end
49
55
 
@@ -63,8 +69,9 @@ module NewRelic
63
69
  end
64
70
  end
65
71
 
72
+ return if severity_too_low?(severity)
66
73
  return if formatted_message.nil? || formatted_message.empty?
67
- return unless NewRelic::Agent.config[:'application_logging.forwarding.enabled']
74
+ return unless NewRelic::Agent.config[FORWARDING_ENABLED_KEY]
68
75
  return if @high_security
69
76
 
70
77
  txn = NewRelic::Agent::Transaction.tl_current
@@ -114,6 +121,10 @@ module NewRelic
114
121
  ]
115
122
  end
116
123
 
124
+ def add_custom_attributes(custom_attributes)
125
+ attributes.add_custom_attributes(custom_attributes)
126
+ end
127
+
117
128
  # Because our transmission format (MELT) is different than historical
118
129
  # agent payloads, extract the munging here to keep the service focused
119
130
  # on the general harvest + transmit instead of the format.
@@ -130,6 +141,8 @@ module NewRelic
130
141
  # sent by classic logs-in-context
131
142
  common_attributes.delete(ENTITY_TYPE_KEY)
132
143
 
144
+ common_attributes.merge!(NewRelic::Agent.agent.log_event_aggregator.attributes.custom_attributes)
145
+
133
146
  _, items = data
134
147
  payload = [{
135
148
  common: {attributes: common_attributes},
@@ -149,6 +162,7 @@ module NewRelic
149
162
  @seen = 0
150
163
  @seen_by_severity.clear
151
164
  end
165
+
152
166
  super
153
167
  end
154
168
 
@@ -168,6 +182,8 @@ module NewRelic
168
182
  record_configuration_metric(METRICS_SUPPORTABILITY_FORMAT, METRICS_ENABLED_KEY)
169
183
  record_configuration_metric(FORWARDING_SUPPORTABILITY_FORMAT, FORWARDING_ENABLED_KEY)
170
184
  record_configuration_metric(DECORATING_SUPPORTABILITY_FORMAT, DECORATING_ENABLED_KEY)
185
+
186
+ add_custom_attributes(NewRelic::Agent.config[CUSTOM_ATTRIBUTES_KEY])
171
187
  end
172
188
  end
173
189
 
@@ -191,7 +207,7 @@ module NewRelic
191
207
  # these until harvest before recording them
192
208
  def record_customer_metrics
193
209
  return unless enabled?
194
- return unless NewRelic::Agent.config[:'application_logging.metrics.enabled']
210
+ return unless NewRelic::Agent.config[METRICS_ENABLED_KEY]
195
211
 
196
212
  @counter_lock.synchronize do
197
213
  return unless @seen > 0
@@ -230,6 +246,37 @@ module NewRelic
230
246
 
231
247
  message.byteslice(0...MAX_BYTES)
232
248
  end
249
+
250
+ def minimum_log_level
251
+ if Logger::Severity.constants.include?(configured_log_level_constant)
252
+ configured_log_level_constant
253
+ else
254
+ NewRelic::Agent.logger.log_once(
255
+ :error,
256
+ 'Invalid application_logging.forwarding.log_level ' \
257
+ "'#{NewRelic::Agent.config[LOG_LEVEL_KEY]}' specified! " \
258
+ "Must be one of #{Logger::Severity.constants.join('|')}. " \
259
+ "Using default level of 'debug'"
260
+ )
261
+ :DEBUG
262
+ end
263
+ end
264
+
265
+ def configured_log_level_constant
266
+ format_log_level_constant(NewRelic::Agent.config[LOG_LEVEL_KEY])
267
+ end
268
+
269
+ def format_log_level_constant(log_level)
270
+ log_level.upcase.to_sym
271
+ end
272
+
273
+ def severity_too_low?(severity)
274
+ severity_constant = format_log_level_constant(severity)
275
+ # always record custom log levels
276
+ return false unless Logger::Severity.constants.include?(severity_constant)
277
+
278
+ Logger::Severity.const_get(severity_constant) < Logger::Severity.const_get(minimum_log_level)
279
+ end
233
280
  end
234
281
  end
235
282
  end