newrelic_rpm 9.4.1 → 9.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +66 -4
  3. data/lib/new_relic/agent/attribute_pre_filtering.rb +109 -0
  4. data/lib/new_relic/agent/configuration/default_source.rb +74 -1
  5. data/lib/new_relic/agent/instrumentation/active_support_logger/instrumentation.rb +4 -0
  6. data/lib/new_relic/agent/instrumentation/bunny/instrumentation.rb +9 -0
  7. data/lib/new_relic/agent/instrumentation/concurrent_ruby/instrumentation.rb +2 -2
  8. data/lib/new_relic/agent/instrumentation/curb/instrumentation.rb +4 -0
  9. data/lib/new_relic/agent/instrumentation/delayed_job/instrumentation.rb +3 -0
  10. data/lib/new_relic/agent/instrumentation/elasticsearch/instrumentation.rb +4 -1
  11. data/lib/new_relic/agent/instrumentation/excon/middleware.rb +3 -0
  12. data/lib/new_relic/agent/instrumentation/grape/instrumentation.rb +4 -0
  13. data/lib/new_relic/agent/instrumentation/grpc/client/instrumentation.rb +4 -0
  14. data/lib/new_relic/agent/instrumentation/grpc/server/instrumentation.rb +4 -0
  15. data/lib/new_relic/agent/instrumentation/grpc_client.rb +1 -1
  16. data/lib/new_relic/agent/instrumentation/grpc_server.rb +1 -1
  17. data/lib/new_relic/agent/instrumentation/httpclient/instrumentation.rb +4 -0
  18. data/lib/new_relic/agent/instrumentation/httprb/instrumentation.rb +4 -0
  19. data/lib/new_relic/agent/instrumentation/logger/instrumentation.rb +3 -0
  20. data/lib/new_relic/agent/instrumentation/memcache/instrumentation.rb +9 -0
  21. data/lib/new_relic/agent/instrumentation/net_http/instrumentation.rb +4 -0
  22. data/lib/new_relic/agent/instrumentation/notifications_subscriber.rb +4 -0
  23. data/lib/new_relic/agent/instrumentation/padrino/instrumentation.rb +4 -0
  24. data/lib/new_relic/agent/instrumentation/rack/instrumentation.rb +6 -0
  25. data/lib/new_relic/agent/instrumentation/rails3/action_controller.rb +4 -0
  26. data/lib/new_relic/agent/instrumentation/rake/instrumentation.rb +4 -0
  27. data/lib/new_relic/agent/instrumentation/redis/instrumentation.rb +4 -0
  28. data/lib/new_relic/agent/instrumentation/resque/instrumentation.rb +4 -0
  29. data/lib/new_relic/agent/instrumentation/roda/instrumentation.rb +4 -0
  30. data/lib/new_relic/agent/instrumentation/roda/roda_transaction_namer.rb +1 -1
  31. data/lib/new_relic/agent/instrumentation/roda.rb +1 -0
  32. data/lib/new_relic/agent/instrumentation/sidekiq/client.rb +4 -0
  33. data/lib/new_relic/agent/instrumentation/sidekiq/server.rb +26 -3
  34. data/lib/new_relic/agent/instrumentation/sidekiq.rb +2 -2
  35. data/lib/new_relic/agent/instrumentation/sinatra/instrumentation.rb +4 -0
  36. data/lib/new_relic/agent/instrumentation/stripe.rb +28 -0
  37. data/lib/new_relic/agent/instrumentation/stripe_subscriber.rb +77 -0
  38. data/lib/new_relic/agent/instrumentation/tilt/instrumentation.rb +4 -0
  39. data/lib/new_relic/agent/instrumentation/typhoeus/instrumentation.rb +5 -1
  40. data/lib/new_relic/agent/transaction/abstract_segment.rb +52 -0
  41. data/lib/new_relic/agent.rb +12 -0
  42. data/lib/new_relic/rack/agent_hooks.rb +1 -1
  43. data/lib/new_relic/rack/agent_middleware.rb +0 -16
  44. data/lib/new_relic/supportability_helper.rb +1 -0
  45. data/lib/new_relic/version.rb +2 -2
  46. data/newrelic.yml +45 -0
  47. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8b8fd67a51c4774e943773f6a5b77937235b73def342903c8c9ae2a9e56dc516
4
- data.tar.gz: 871ccb95840ce1c380e6ba30c93994b0139d3e1c2a39293ea94b39aa3e356a28
3
+ metadata.gz: e5e1f4605a4afe166aa2035486a2433f91f4d0432666b787e6d9c5af9e5ee717
4
+ data.tar.gz: 16e7707b8c81774ea0c2bea54825d023c2849d0a03ab553e25800c131d9c2972
5
5
  SHA512:
6
- metadata.gz: e40a7cbe05b88f0380fe6291920f9ea389f9628a0571b019884f83c632fb99abaf6907e8fb9930cffd608f32a7ad1e9cb6d2ad8d31312a8b05f8b207ce3bf9c7
7
- data.tar.gz: a259750bda57cf53cee5e59327df1fb89ef5af9480f6e2d80c4c08a63c33a8d9f5ec402c040c93942f4ff2b96af1ba3d16bb07194fabd24ab54a4c661a071784
6
+ metadata.gz: 8180f7caa158ce2ea794914376f441f5f912f46fbce8caca074b8c2c9a06fb84ca5e290eab2aa63dfa68f5385e6f7f9f3ede8ee00ee97fbc30d692b93721ec8e
7
+ data.tar.gz: 21062253bf9236563dcd226ccc4e5e2f8d1fceaed24f6047fb6ed23f485f8b0e4607bbe9b7985c4a63809767c2f4b5785e4ec52c25ff6d2ac186ff7fb8a149f7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,67 @@
1
1
  # New Relic Ruby Agent Release Notes
2
2
 
3
+ ## v9.5.0
4
+
5
+ Version 9.5.0 introduces Stripe instrumentation, allows the agent to record additional response information on a transaction when middleware instrumentation is disabled, introduces new `:'sidekiq.args.include'` and `:'sidekiq.args.exclude:` configuration options to permit capturing only certain Sidekiq job arguments, updates Elasticsearch datastore instance metrics, and fixes a bug in `NewRelic::Rack::AgentHooks.needed?`.
6
+
7
+ - **Feature: Add Stripe instrumentation**
8
+
9
+ [Stripe](https://stripe.com/) calls are now automatically instrumented. Additionally, new `:'stripe.user_data.include'` and `:'stripe.user_data.exclude'` configuration options permit capturing custom `user_data` key-value pairs that can be stored in [Stripe events](https://github.com/stripe/stripe-ruby#instrumentation). No `user_data` key-value pairs are captured by default. The agent currently supports Stripe versions 5.38.0+. [PR#2180](https://github.com/newrelic/newrelic-ruby-agent/pull/2180)
10
+
11
+ - **Feature: Report transaction HTTP status codes when middleware instrumentation is disabled**
12
+
13
+ Previously, when `disable_middleware_instrumentation` was set to `true`, the agent would not record the value of the response code or content type on the transaction. This was due to the possibility that a middleware could alter the response, which would not be captured by the agent when the middleware instrumentation was disabled. However, based on customer feedback, the agent will now report the HTTP status code and content type on a transaction when middleware instrumentation is disabled. [PR#2175](https://github.com/newrelic/newrelic-ruby-agent/pull/2175)
14
+
15
+ - **Feature: Permit capturing only certain Sidekiq job arguments**
16
+
17
+ New `:'sidekiq.args.include'` and `:'sidekiq.args.exclude'` configuration options have been introduced to permit fine grained control over which Sidekiq job arguments (args) are reported to New Relic. By default, no Sidekiq args are reported. To report any Sidekiq options, the `:'attributes.include'` array must include the string `'jobs.sidekiq.args.*'`. With that string in place, all arguments will be reported unless one or more of the new include/exclude options are used. The `:'sidekiq.args.include'` option can be set to an array of strings. Each of those strings will be passed to `Regexp.new` and collectively serve as an allowlist for desired args. For job arguments that are hashes, if a hash's key matches one of the include patterns, then both the key and its corresponding value will be included. For scalar arguments, the string representation of the scalar will need to match one of the include patterns to be captured. The `:'sidekiq.args.exclude'` option works similarly. It can be set to an array of strings that will each be passed to `Regexp.new` to create patterns. These patterns will collectively serve as a denylist for unwanted job args. Any hash key, hash value, or scalar that matches an exclude pattern will be excluded (not sent to New Relic). [PR#2177](https://github.com/newrelic/newrelic-ruby-agent/pull/2177)
18
+
19
+ `newrelic.yml` examples:
20
+
21
+ Any string in the `:'sidekiq.args.include'` or `:'sidekiq.args.exclude'` arrays will be turned into a regular expression. Knowledge of [Ruby regular expression support](https://ruby-doc.org/3.2.2/Regexp.html) can be leveraged but is not required. If regular expression syntax is not used, inexact matches will be performed and the string "Fortune" will match both "Fortune 500" and "Fortune and Glory". For exact matches, use [regular expression anchors](https://ruby-doc.org/3.2.2/Regexp.html#class-Regexp-label-Anchors).
22
+
23
+ ```yaml
24
+ # Include any argument whose string representation matches either "apple" or "banana"
25
+ # The "apple" pattern will match both "green apple" and "red apple"
26
+ sidekiq.args.include:
27
+ - apple
28
+ - banana
29
+
30
+ # Exclude any arguments that match either "grape", "orange", or "pear"
31
+ sidekiq.args.exclude:
32
+ - grape
33
+ - orange
34
+ - pear
35
+
36
+ # Exclude any argument that is a 9 digit number
37
+ sidekiq.args.exclude:
38
+ - '\d{9}'
39
+
40
+ # Include anything that starts with "blue" but exclude anything that ends in "green"
41
+ sidekiq.args.include
42
+ - '^blue'
43
+
44
+ sidekiq.args.exclude
45
+ - 'green$'
46
+ ```
47
+
48
+ - **Bugfix: Update Elasticsearch datastore instance metric to use port instead of path**
49
+
50
+ Previously, the Elasticsearch datastore instance metric (`Datastore/instance/Elasticsearch/<host>/*`) used the path as the final value. This caused a [metrics grouping issue](https://docs.newrelic.com/docs/new-relic-solutions/solve-common-issues/troubleshooting/metric-grouping-issues) for some users, as every document ID created a unique metric. Now, the datastore instance metric has been updated to use the port as the final value. This also has the benefit of being more accurate for datastore instance metrics, as this port is directly associated with the already listed host.
51
+
52
+ - **Bugfix: Resolve inverted logic of NewRelic::Rack::AgentHooks.needed?**
53
+
54
+ Previously, `NewRelic::Rack::AgentHooks.needed?` incorrectly used inverted logic. This has now been resolved, allowing AgentHooks to be installed when `disable_middleware_instrumentation` is set to true. [PR#2175](https://github.com/newrelic/newrelic-ruby-agent/pull/2175)
55
+
56
+
57
+ ## v9.4.2
58
+
59
+ Version 9.4.2 of the agent re-addresses the 9.4.0 issue of `NoMethodError` seen when using the `uppy-s3_multipart` gem.
60
+
61
+ - **Bugfix: Resolve NoMethodError**
62
+
63
+ Ruby agent 9.4.1 attempted to fix a `NoMethodError` introduced in 9.4.0. A missing `require` prevented a method from scoping appropriately and has now been added. Thanks to [@spickermann](https://github.com/spickermann) and [@ColinOrr](https://github.com/ColinOrr) for working with us to get this resolved. [PR#2167](https://github.com/newrelic/newrelic-ruby-agent/pull/2167)
64
+
3
65
  ## v9.4.1
4
66
 
5
67
  Version 9.4.1 of the agent resolves a `NoMethodError` introduced in 9.4.0.
@@ -19,19 +81,19 @@ Version 9.4.0 of the agent adds [Roda](https://roda.jeremyevans.net/) instrument
19
81
  - **Feature: New allow_all_headers configuration option**
20
82
 
21
83
  A new `allow_all_headers` configuration option brings parity with the [Node.js agent](https://docs.newrelic.com/docs/release-notes/agent-release-notes/nodejs-release-notes/node-agent-270/) to capture all HTTP request headers.
22
-
84
+
23
85
  This configuration option:
24
86
  * Defaults to `false`
25
87
  * Is not compatible with high security mode
26
- * Requires Rack version 2 or higher (as does Ruby on Rails version 5 and above)
88
+ * Requires Rack version 2 or higher (as does Ruby on Rails version 5 and above)
27
89
  * Respects all existing behavior for the `attributes.include` and `attributes.exclude` [configuration options](https://docs.newrelic.com/docs/apm/agents/ruby-agent/configuration/ruby-agent-configuration/#attributes)
28
90
  * Captures the additional headers as attributes prefixed with `request.headers.`
29
-
91
+
30
92
  This work was done in response to a feature request submitted by community member [@jamesarosen](https://github.com/jamesarosen). Thank you very much, @jamesarosen! [Issue#1029](https://github.com/newrelic/newrelic-ruby-agent/issues/1029)
31
93
 
32
94
  - **Feature: Improved error tracking transaction linking**
33
95
 
34
- Errors tracked and sent to the New Relic errors inbox will now be associated with a transaction id to enable improved UI/UX associations between transactions and errors. [PR#2035](https://github.com/newrelic/newrelic-ruby-agent/pull/2035)
96
+ Errors tracked and sent to the New Relic errors inbox will now be associated with a transaction id to enable improved UI/UX associations between transactions and errors. [PR#2035](https://github.com/newrelic/newrelic-ruby-agent/pull/2035)
35
97
 
36
98
  - **Feature: Use Net::HTTP native timeout logic**
37
99
 
@@ -0,0 +1,109 @@
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 AttributePreFiltering
8
+ module_function
9
+
10
+ PRE_FILTER_KEYS = %i[include exclude].freeze
11
+ DISCARDED = :nr_discarded
12
+
13
+ def formulate_regexp_union(option)
14
+ return if NewRelic::Agent.config[option].empty?
15
+
16
+ Regexp.union(NewRelic::Agent.config[option].map { |p| string_to_regexp(p) }.uniq.compact).freeze
17
+ rescue StandardError => e
18
+ NewRelic::Agent.logger.warn("Failed to formulate a Regexp union from the '#{option}' configuration option " +
19
+ "- #{e.class}: #{e.message}")
20
+ end
21
+
22
+ def string_to_regexp(str)
23
+ Regexp.new(str)
24
+ rescue StandardError => e
25
+ NewRelic::Agent.logger.warn("Failed to initialize Regexp from string '#{str}' - #{e.class}: #{e.message}")
26
+ end
27
+
28
+ # attribute filtering suppresses data that has already been flattened
29
+ # and coerced (serialized as text) via #flatten_and_coerce, and is
30
+ # restricted to basic text matching with a single optional wildcard.
31
+ # pre filtering operates on raw Ruby objects beforehand and uses full
32
+ # Ruby regex syntax
33
+ def pre_filter(values = [], options = {})
34
+ return values unless !options.empty? && PRE_FILTER_KEYS.any? { |k| options.key?(k) }
35
+
36
+ # if there's a prefix in play for (non-pre) attribute filtration and
37
+ # attribute filtration won't allow that prefix, then don't even bother
38
+ # with pre filtration that could only result in values that would be
39
+ # blocked
40
+ if options.key?(:attribute_namespace) &&
41
+ !NewRelic::Agent.instance.attribute_filter.might_allow_prefix?(options[:attribute_namespace])
42
+ return values
43
+ end
44
+
45
+ values.each_with_object([]) do |element, filtered|
46
+ object = pre_filter_object(element, options)
47
+ filtered << object unless discarded?(object)
48
+ end
49
+ end
50
+
51
+ def pre_filter_object(object, options)
52
+ if object.is_a?(Hash)
53
+ pre_filter_hash(object, options)
54
+ elsif object.is_a?(Array)
55
+ pre_filter_array(object, options)
56
+ else
57
+ pre_filter_scalar(object, options)
58
+ end
59
+ end
60
+
61
+ def pre_filter_hash(hash, options)
62
+ filtered_hash = hash.each_with_object({}) do |(key, value), filtered|
63
+ filtered_key = pre_filter_object(key, options)
64
+ next if discarded?(filtered_key)
65
+
66
+ # If the key is permitted, skip include filtration for the value
67
+ # but still apply exclude filtration
68
+ if options.key?(:exclude)
69
+ exclude_only = options.dup
70
+ exclude_only.delete(:include)
71
+ filtered_value = pre_filter_object(value, exclude_only)
72
+ next if discarded?(filtered_value)
73
+ else
74
+ filtered_value = value
75
+ end
76
+
77
+ filtered[filtered_key] = filtered_value
78
+ end
79
+
80
+ filtered_hash.empty? && !hash.empty? ? DISCARDED : filtered_hash
81
+ end
82
+
83
+ def pre_filter_array(array, options)
84
+ filtered_array = array.each_with_object([]) do |element, filtered|
85
+ filtered_element = pre_filter_object(element, options)
86
+ next if discarded?(filtered_element)
87
+
88
+ filtered.push(filtered_element)
89
+ end
90
+
91
+ filtered_array.empty? && !array.empty? ? DISCARDED : filtered_array
92
+ end
93
+
94
+ def pre_filter_scalar(scalar, options)
95
+ return DISCARDED if options.key?(:include) && !scalar.to_s.match?(options[:include])
96
+ return DISCARDED if options.key?(:exclude) && scalar.to_s.match?(options[:exclude])
97
+
98
+ scalar
99
+ end
100
+
101
+ # `nil`, empty enumerable objects, and `false` are all valid in their
102
+ # own right as application data, so pre-filtering uses a special value
103
+ # to indicate that filtered out data has been discarded
104
+ def discarded?(object)
105
+ object == DISCARDED
106
+ end
107
+ end
108
+ end
109
+ end
@@ -3,6 +3,7 @@
3
3
  # frozen_string_literal: true
4
4
 
5
5
  require 'forwardable'
6
+ require_relative '../../constants'
6
7
 
7
8
  module NewRelic
8
9
  module Agent
@@ -1212,7 +1213,13 @@ module NewRelic
1212
1213
  :public => true,
1213
1214
  :type => Boolean,
1214
1215
  :allowed_from_server => false,
1215
- :description => 'If `true`, the agent won\'t wrap third-party middlewares in instrumentation (regardless of whether they are installed via `Rack::Builder` or Rails).'
1216
+ :description => <<~DESCRIPTION
1217
+ If `true`, the agent won't wrap third-party middlewares in instrumentation (regardless of whether they are installed via `Rack::Builder` or Rails).
1218
+
1219
+ <Callout variant="important">
1220
+ When middleware instrumentation is disabled, if an application is using middleware that could alter the response code, the HTTP status code reported on the transaction may not reflect the altered value.
1221
+ </Callout>
1222
+ DESCRIPTION
1216
1223
  },
1217
1224
  :disable_samplers => {
1218
1225
  :default => false,
@@ -1595,6 +1602,41 @@ module NewRelic
1595
1602
  :allowed_from_server => false,
1596
1603
  :description => 'Controls auto-instrumentation of Sinatra at start up. May be one of: `auto`, `prepend`, `chain`, `disabled`.'
1597
1604
  },
1605
+ :'instrumentation.stripe' => {
1606
+ :default => 'enabled',
1607
+ :public => true,
1608
+ :type => String,
1609
+ :allowed_from_server => false,
1610
+ :description => 'Controls auto-instrumentation of Stripe at startup. May be one of: `enabled`, `disabled`.'
1611
+ },
1612
+ :'stripe.user_data.include' => {
1613
+ default: NewRelic::EMPTY_ARRAY,
1614
+ public: true,
1615
+ type: Array,
1616
+ dynamic_name: true,
1617
+ allowed_from_server: false,
1618
+ :transform => DefaultSource.method(:convert_to_list),
1619
+ :description => <<~DESCRIPTION
1620
+ An array of strings to specify which keys inside a Stripe event's `user_data` hash should be reported
1621
+ to New Relic. Each string in this array will be turned into a regular expression via `Regexp.new` to
1622
+ permit advanced matching. Setting the value to `["."]` will report all `user_data`.
1623
+ DESCRIPTION
1624
+ },
1625
+ :'stripe.user_data.exclude' => {
1626
+ default: NewRelic::EMPTY_ARRAY,
1627
+ public: true,
1628
+ type: Array,
1629
+ dynamic_name: true,
1630
+ allowed_from_server: false,
1631
+ :transform => DefaultSource.method(:convert_to_list),
1632
+ :description => <<~DESCRIPTION
1633
+ An array of strings to specify which keys and/or values inside a Stripe event's `user_data` hash should
1634
+ not be reported to New Relic. Each string in this array will be turned into a regular expression via
1635
+ `Regexp.new` to permit advanced matching. For each hash pair, if either the key or value is matched the
1636
+ pair will not be reported. By default, no `user_data` is reported, so this option should only be used if
1637
+ the `stripe.user_data.include` option is being used.
1638
+ DESCRIPTION
1639
+ },
1598
1640
  :'instrumentation.thread' => {
1599
1641
  :default => 'auto',
1600
1642
  :public => true,
@@ -1709,6 +1751,37 @@ module NewRelic
1709
1751
  :transform => DefaultSource.method(:convert_to_regexp_list),
1710
1752
  :description => 'Define transactions you want the agent to ignore, by specifying a list of patterns matching the URI you want to ignore. For more detail, see [the docs on ignoring specific transactions](/docs/agents/ruby-agent/api-guides/ignoring-specific-transactions/#config-ignoring).'
1711
1753
  },
1754
+ # Sidekiq
1755
+ :'sidekiq.args.include' => {
1756
+ default: NewRelic::EMPTY_ARRAY,
1757
+ public: true,
1758
+ type: Array,
1759
+ dynamic_name: true,
1760
+ allowed_from_server: false,
1761
+ description: <<~SIDEKIQ_ARGS_INCLUDE.chomp.tr("\n", ' ')
1762
+ An array of strings that will collectively serve as an allowlist for filtering which Sidekiq
1763
+ job arguments get reported to New Relic. To capture any Sidekiq arguments,
1764
+ 'job.sidekiq.args.*' must be added to the separate `:'attributes.include'` configuration option. Each
1765
+ string in this array will be turned into a regular expression via `Regexp.new` to permit advanced
1766
+ matching. For job argument hashes, if either a key or value matches the pair will be included. All
1767
+ matching job argument array elements and job argument scalars will be included.
1768
+ SIDEKIQ_ARGS_INCLUDE
1769
+ },
1770
+ :'sidekiq.args.exclude' => {
1771
+ default: NewRelic::EMPTY_ARRAY,
1772
+ public: true,
1773
+ type: Array,
1774
+ dynamic_name: true,
1775
+ allowed_from_server: false,
1776
+ description: <<~SIDEKIQ_ARGS_EXCLUDE.chomp.tr("\n", ' ')
1777
+ An array of strings that will collectively serve as a denylist for filtering which Sidekiq
1778
+ job arguments get reported to New Relic. To capture any Sidekiq arguments,
1779
+ 'job.sidekiq.args.*' must be added to the separate `:'attributes.include'` configuration option. Each string
1780
+ in this array will be turned into a regular expression via `Regexp.new` to permit advanced matching.
1781
+ For job argument hashes, if either a key or value matches the pair will be excluded. All matching job
1782
+ argument array elements and job argument scalars will be excluded.
1783
+ SIDEKIQ_ARGS_EXCLUDE
1784
+ },
1712
1785
  # Slow SQL
1713
1786
  :'slow_sql.enabled' => {
1714
1787
  :default => value_of(:'transaction_tracer.enabled'),
@@ -6,8 +6,12 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module ActiveSupportLogger
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  # Mark @skip_instrumenting on any broadcasted loggers to instrument Rails.logger only
10
12
  def broadcast_with_tracing(logger)
13
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
14
+
11
15
  NewRelic::Agent::Instrumentation::Logger.mark_skip_instrumenting(logger)
12
16
  yield
13
17
  rescue => error
@@ -12,6 +12,7 @@ module NewRelic
12
12
  DEFAULT_NAME = 'Default'
13
13
  DEFAULT_TYPE = :direct
14
14
  SLASH = '/'
15
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
15
16
 
16
17
  def exchange_name(name)
17
18
  name.empty? ? DEFAULT_NAME : name
@@ -28,6 +29,8 @@ module NewRelic
28
29
  include Bunny
29
30
 
30
31
  def publish_with_tracing(payload, opts = {})
32
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
33
+
31
34
  begin
32
35
  destination = exchange_name(name)
33
36
 
@@ -62,6 +65,8 @@ module NewRelic
62
65
  include Bunny
63
66
 
64
67
  def pop_with_tracing
68
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
69
+
65
70
  bunny_error, delivery_info, message_properties, _payload = nil, nil, nil, nil
66
71
  begin
67
72
  t0 = Process.clock_gettime(Process::CLOCK_REALTIME)
@@ -104,6 +109,8 @@ module NewRelic
104
109
  end
105
110
 
106
111
  def purge_with_tracing
112
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
113
+
107
114
  begin
108
115
  type = server_named? ? :temporary_queue : :queue
109
116
  segment = NewRelic::Agent::Tracer.start_message_broker_segment(
@@ -129,6 +136,8 @@ module NewRelic
129
136
  include Bunny
130
137
 
131
138
  def call_with_tracing(*args)
139
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
140
+
132
141
  delivery_info, message_properties, _ = args
133
142
  queue_name = queue.respond_to?(:name) ? queue.name : queue
134
143
 
@@ -5,10 +5,10 @@
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module ConcurrentRuby
7
7
  SEGMENT_NAME = 'Concurrent/Task'
8
- SUPPORTABILITY_METRIC = 'Supportability/ConcurrentRuby/Invoked'
8
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
9
9
 
10
10
  def add_task_tracing(&task)
11
- NewRelic::Agent.record_metric_once(SUPPORTABILITY_METRIC)
11
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
12
12
 
13
13
  NewRelic::Agent::Tracer.thread_block_with_current_transaction(
14
14
  segment_name: SEGMENT_NAME,
@@ -68,6 +68,8 @@ module NewRelic
68
68
  module Multi
69
69
  include NewRelic::Agent::MethodTracer
70
70
 
71
+ INSTRUMENTATION_NAME = 'Curb'
72
+
71
73
  # Add CAT with callbacks if the request is serial
72
74
  def add_with_tracing(curl)
73
75
  if curl.respond_to?(:_nr_serial) && curl._nr_serial
@@ -81,6 +83,8 @@ module NewRelic
81
83
  def perform_with_tracing
82
84
  return yield if first_request_is_serial?
83
85
 
86
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
87
+
84
88
  trace_execution_scoped('External/Multiple/Curb::Multi/perform') do
85
89
  yield
86
90
  end
@@ -31,8 +31,11 @@ module NewRelic
31
31
  include NewRelic::Agent::Instrumentation::ControllerInstrumentation
32
32
 
33
33
  NR_TRANSACTION_CATEGORY = 'OtherTransaction/DelayedJob'.freeze
34
+ INSTRUMENTATION_NAME = 'DelayedJob'
34
35
 
35
36
  def invoke_job_with_tracing
37
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
38
+
36
39
  options = {
37
40
  :category => NR_TRANSACTION_CATEGORY,
38
41
  :path => ::NewRelic::Agent::Instrumentation::DelayedJob::Naming.name_from_payload(payload_object)
@@ -8,15 +8,18 @@ module NewRelic::Agent::Instrumentation
8
8
  module Elasticsearch
9
9
  PRODUCT_NAME = 'Elasticsearch'
10
10
  OPERATION = 'perform_request'
11
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
11
12
 
12
13
  def perform_request_with_tracing(method, path, params = {}, body = nil, headers = nil)
13
14
  return yield unless NewRelic::Agent::Tracer.tracing_enabled?
14
15
 
16
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
17
+
15
18
  segment = NewRelic::Agent::Tracer.start_datastore_segment(
16
19
  product: PRODUCT_NAME,
17
20
  operation: nr_operation || OPERATION,
18
21
  host: nr_hosts[:host],
19
- port_path_or_id: path,
22
+ port_path_or_id: nr_hosts[:port],
20
23
  database_name: nr_cluster_name
21
24
  )
22
25
  begin
@@ -6,6 +6,7 @@ module ::Excon
6
6
  module Middleware
7
7
  class NewRelicCrossAppTracing
8
8
  TRACE_DATA_IVAR = :@newrelic_trace_data
9
+ INSTRUMENTATION_NAME = 'Excon'
9
10
 
10
11
  def initialize(stack)
11
12
  @stack = stack
@@ -18,6 +19,8 @@ module ::Excon
18
19
  # :idempotent in the options, but there will be only a single
19
20
  # accompanying response_call/error_call.
20
21
  if datum[:connection] && !datum[:connection].instance_variable_get(TRACE_DATA_IVAR)
22
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
23
+
21
24
  wrapped_request = ::NewRelic::Agent::HTTPClients::ExconHTTPRequest.new(datum)
22
25
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
23
26
  library: wrapped_request.type,
@@ -9,6 +9,8 @@ module NewRelic::Agent::Instrumentation
9
9
  module Instrumentation
10
10
  extend self
11
11
 
12
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
13
+
12
14
  # Since 1.2.0, the class `Grape::API` no longer refers to an API instance, rather, what used to be `Grape::API` is `Grape::API::Instance`
13
15
  # https://github.com/ruby-grape/grape/blob/c20a73ac1e3f3ba1082005ed61bf69452373ba87/UPGRADING.md#upgrading-to--120
14
16
  def instrumented_class
@@ -46,6 +48,8 @@ module NewRelic::Agent::Instrumentation
46
48
  def handle_transaction(endpoint, class_name, version)
47
49
  return unless endpoint && route = endpoint.route
48
50
 
51
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
52
+
49
53
  name_transaction(route, class_name, version)
50
54
  capture_params(endpoint)
51
55
  end
@@ -12,10 +12,14 @@ module NewRelic
12
12
  module Client
13
13
  include NewRelic::Agent::Instrumentation::GRPC::Helper
14
14
 
15
+ INSTRUMENTATION_NAME = 'gRPC_Client'
16
+
15
17
  def issue_request_with_tracing(grpc_type, method, requests, marshal, unmarshal,
16
18
  deadline:, return_op:, parent:, credentials:, metadata:)
17
19
  return yield unless trace_with_newrelic?
18
20
 
21
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
22
+
19
23
  segment = request_segment(method)
20
24
  request_wrapper = NewRelic::Agent::Instrumentation::GRPC::Client::RequestWrapper.new(@host)
21
25
  # do not insert CAT headers for gRPC requests https://github.com/newrelic/newrelic-ruby-agent/issues/1730
@@ -11,6 +11,8 @@ module NewRelic
11
11
  module Server
12
12
  include NewRelic::Agent::Instrumentation::GRPC::Helper
13
13
 
14
+ INSTRUMENTATION_NAME = 'gRPC_Server'
15
+
14
16
  DT_KEYS = [NewRelic::NEWRELIC_KEY, NewRelic::TRACEPARENT_KEY, NewRelic::TRACESTATE_KEY].freeze
15
17
  INSTANCE_VAR_HOST = :@host_nr
16
18
  INSTANCE_VAR_PORT = :@port_nr
@@ -23,6 +25,8 @@ module NewRelic
23
25
  def handle_with_tracing(streamer_type, active_call, mth, _inter_ctx)
24
26
  return yield unless trace_with_newrelic?
25
27
 
28
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
29
+
26
30
  metadata = metadata_for_call(active_call)
27
31
  txn = NewRelic::Agent::Transaction.start_new_transaction(NewRelic::Agent::Tracer.state,
28
32
  CATEGORY,
@@ -13,7 +13,7 @@ DependencyDetection.defer do
13
13
  end
14
14
 
15
15
  executes do
16
- supportability_name = 'gRPC_Client'
16
+ supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
17
17
  if use_prepend?
18
18
  prepend_instrument GRPC::ClientStub, NewRelic::Agent::Instrumentation::GRPC::Client::Prepend, supportability_name
19
19
  else
@@ -14,7 +14,7 @@ DependencyDetection.defer do
14
14
  end
15
15
 
16
16
  executes do
17
- supportability_name = 'gRPC_Server'
17
+ supportability_name = NewRelic::Agent::Instrumentation::GRPC::Client::INSTRUMENTATION_NAME
18
18
  if use_prepend?
19
19
  prepend_instrument GRPC::RpcServer, NewRelic::Agent::Instrumentation::GRPC::Server::RpcServerPrepend, supportability_name
20
20
  prepend_instrument GRPC::RpcDesc, NewRelic::Agent::Instrumentation::GRPC::Server::RpcDescPrepend, supportability_name
@@ -5,7 +5,11 @@
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module HTTPClient
7
7
  module Instrumentation
8
+ INSTRUMENTATION_NAME = 'HTTPClient'
9
+
8
10
  def with_tracing(request, connection)
11
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
12
+
9
13
  wrapped_request = NewRelic::Agent::HTTPClients::HTTPClientRequest.new(request)
10
14
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
11
15
  library: wrapped_request.type,
@@ -4,7 +4,11 @@
4
4
 
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module HTTPrb
7
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
8
+
7
9
  def with_tracing(request)
10
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
+
8
12
  wrapped_request = ::NewRelic::Agent::HTTPClients::HTTPRequest.new(request)
9
13
 
10
14
  begin
@@ -6,6 +6,8 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module Logger
9
+ INSTRUMENTATION_NAME = 'Logger'
10
+
9
11
  def skip_instrumenting?
10
12
  defined?(@skip_instrumenting) && @skip_instrumenting
11
13
  end
@@ -51,6 +53,7 @@ module NewRelic
51
53
  mark_skip_instrumenting
52
54
 
53
55
  unless ::NewRelic::Agent.agent.nil?
56
+ ::NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
54
57
  ::NewRelic::Agent.agent.log_event_aggregator.record(formatted_message, severity)
55
58
  formatted_message = LocalLogDecorator.decorate(formatted_message)
56
59
  end
@@ -10,8 +10,11 @@ module NewRelic::Agent::Instrumentation
10
10
  LOCALHOST = 'localhost'
11
11
  MULTIGET_METRIC_NAME = 'get_multi_request'
12
12
  MEMCACHED = 'Memcached'
13
+ INSTRUMENTATION_NAME = 'Dalli'
13
14
 
14
15
  def with_newrelic_tracing(operation, *args)
16
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
17
+
15
18
  segment = NewRelic::Agent::Tracer.start_datastore_segment(
16
19
  product: MEMCACHED,
17
20
  operation: operation
@@ -28,6 +31,8 @@ module NewRelic::Agent::Instrumentation
28
31
  end
29
32
 
30
33
  def server_for_key_with_newrelic_tracing
34
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
35
+
31
36
  yield.tap do |server|
32
37
  begin
33
38
  if txn = ::NewRelic::Agent::Tracer.current_transaction
@@ -43,6 +48,8 @@ module NewRelic::Agent::Instrumentation
43
48
  end
44
49
 
45
50
  def get_multi_with_newrelic_tracing(method_name)
51
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
52
+
46
53
  segment = NewRelic::Agent::Tracer.start_segment(
47
54
  name: "Ruby/Memcached/Dalli/#{method_name}"
48
55
  )
@@ -55,6 +62,8 @@ module NewRelic::Agent::Instrumentation
55
62
  end
56
63
 
57
64
  def send_multiget_with_newrelic_tracing(keys)
65
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
66
+
58
67
  segment = ::NewRelic::Agent::Tracer.start_datastore_segment(
59
68
  product: MEMCACHED,
60
69
  operation: MULTIGET_METRIC_NAME
@@ -6,7 +6,11 @@ module NewRelic
6
6
  module Agent
7
7
  module Instrumentation
8
8
  module NetHTTP
9
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
10
+
9
11
  def request_with_tracing(request)
12
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
13
+
10
14
  wrapped_request = NewRelic::Agent::HTTPClients::NetHTTPRequest.new(self, request)
11
15
 
12
16
  segment = NewRelic::Agent::Tracer.start_external_request_segment(
@@ -47,6 +47,10 @@ module NewRelic
47
47
  end
48
48
  end
49
49
 
50
+ # The agent doesn't use the traditional ActiveSupport::Notifications.subscribe
51
+ # pattern due to threading issues discovered on initial instrumentation.
52
+ # Instead we define a #start and #finish method, which Rails responds to.
53
+ # See: https://github.com/rails/rails/issues/12069
50
54
  def start(name, id, payload)
51
55
  return unless state.is_execution_traced?
52
56
 
@@ -4,7 +4,11 @@
4
4
 
5
5
  module NewRelic::Agent::Instrumentation
6
6
  module Padrino
7
+ INSTRUMENTATION_NAME = NewRelic::Agent.base_name(name)
8
+
7
9
  def invoke_route_with_tracing(*args)
10
+ NewRelic::Agent.record_instrumentation_invocation(INSTRUMENTATION_NAME)
11
+
8
12
  begin
9
13
  env['newrelic.last_route'] = args[0].original_path
10
14
  rescue => e